Doze: Add plugin hook

Test: mmm vendor/google_experimental/users/roosa/DozePlugin/
Change-Id: I7ea55954f0c07be932e8ee7037e03b7c01cc1108
This commit is contained in:
Adrian Roos
2016-11-08 15:42:20 -08:00
parent 1e151b81c9
commit f9d13f6d7a
8 changed files with 297 additions and 24 deletions

View File

@@ -267,6 +267,45 @@ public class NotificationColorUtil {
return ColorUtilsFromCompat.LABToColor(low, a, b);
}
/**
* Finds a suitable color such that there's enough contrast.
*
* @param color the color to start searching from.
* @param other the color to ensure contrast against. Assumed to be darker than {@param color}
* @param findFg if true, we assume {@param color} is a foreground, otherwise a background.
* @param minRatio the minimum contrast ratio required.
* @return a color with the same hue as {@param color}, potentially darkened to meet the
* contrast ratio.
*/
public static int findContrastColorAgainstDark(int color, int other, boolean findFg,
double minRatio) {
int fg = findFg ? color : other;
int bg = findFg ? other : color;
if (ColorUtilsFromCompat.calculateContrast(fg, bg) >= minRatio) {
return color;
}
double[] lab = new double[3];
ColorUtilsFromCompat.colorToLAB(findFg ? fg : bg, lab);
double low = lab[0], high = 100;
final double a = lab[1], b = lab[2];
for (int i = 0; i < 15 && high - low > 0.00001; i++) {
final double l = (low + high) / 2;
if (findFg) {
fg = ColorUtilsFromCompat.LABToColor(l, a, b);
} else {
bg = ColorUtilsFromCompat.LABToColor(l, a, b);
}
if (ColorUtilsFromCompat.calculateContrast(fg, bg) > minRatio) {
high = l;
} else {
low = l;
}
}
return ColorUtilsFromCompat.LABToColor(high, a, b);
}
/**
* Finds a text color with sufficient contrast over bg that has the same hue as the original
* color, assuming it is for large text.

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.plugins.doze;
import android.app.PendingIntent;
import android.content.Context;
import com.android.systemui.plugins.Plugin;
/**
* Provides a {@link DozeUi}.
*/
public interface DozeProvider extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_DOZE";
int VERSION = 1;
/**
* Caution: Even if this is called, the DozeUi provided may still be in use until it transitions
* to DozeState.FINISH
*/
@Override
default void onDestroy() {
}
/**
* @return the plugin's implementation of DozeUi.
*/
DozeUi provideDozeUi(Context context, DozeMachine machine, WakeLock wakeLock);
/**
* If true, the plugin allows the default pulse triggers to fire, otherwise they are disabled.
*/
default boolean allowDefaultPulseTriggers() {
return false;
}
/**
* Ui for use in DozeMachine.
*/
interface DozeUi {
/** Called whenever the DozeMachine state transitions */
void transitionTo(DozeState oldState, DozeState newState);
}
/** WakeLock wrapper for testability */
interface WakeLock {
/** @see android.os.PowerManager.WakeLock#acquire() */
void acquire();
/** @see android.os.PowerManager.WakeLock#release() */
void release();
/** @see android.os.PowerManager.WakeLock#wrap(Runnable) */
Runnable wrap(Runnable r);
}
/** Plugin version of the DozeMachine's state */
enum DozeState {
/** Default state. Transition to INITIALIZED to get Doze going. */
UNINITIALIZED,
/** Doze components are set up. Followed by transition to DOZE or DOZE_AOD. */
INITIALIZED,
/** Regular doze. Device is asleep and listening for pulse triggers. */
DOZE,
/** Always-on doze. Device is asleep, showing UI and listening for pulse triggers. */
DOZE_AOD,
/** Pulse has been requested. Device is awake and preparing UI */
DOZE_REQUEST_PULSE,
/** Pulse is showing. Device is awake and showing UI. */
DOZE_PULSING,
/** Pulse is done showing. Followed by transition to DOZE or DOZE_AOD. */
DOZE_PULSE_DONE,
/** Doze is done. DozeService is finished. */
FINISH,
/** WakeUp. */
WAKE_UP,
}
/** Plugin interface for the doze machine. */
interface DozeMachine {
/** Request that the DozeMachine transitions to {@code state} */
void requestState(DozeState state);
/** Request that the PendingIntent is sent. */
void requestSendIntent(PendingIntent intent);
}
}

View File

@@ -29,6 +29,7 @@ import android.os.SystemProperties;
import android.os.UserHandle;
import android.util.Log;
import com.android.systemui.doze.DozeFactory;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyboard.KeyboardUI;
import com.android.systemui.keyguard.KeyguardViewMediator;
@@ -76,7 +77,8 @@ public class SystemUIApplication extends Application {
PipUI.class,
ShortcutKeyDispatcher.class,
VendorServices.class,
LatencyTester.class
LatencyTester.class,
DozeFactory.Initializer.class,
};
/**

View File

@@ -17,19 +17,52 @@
package com.android.systemui.doze;
import android.app.Application;
import android.app.PendingIntent;
import android.content.Context;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.PowerManager;
import android.os.SystemClock;
import com.android.internal.hardware.AmbientDisplayConfiguration;
import com.android.systemui.SystemUI;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.doze.DozeProvider;
import com.android.systemui.statusbar.phone.DozeParameters;
public class DozeFactory {
private static DozeFactory sInstance;
private DozeProvider mDozePlugin;
/** Returns the singleton instance. */
public static DozeFactory getInstance(Context context) {
if (sInstance == null) {
sInstance = new DozeFactory();
PluginManager.getInstance(context).addPluginListener(DozeProvider.ACTION,
new PluginListener<DozeProvider>() {
@Override
public void onPluginConnected(DozeProvider plugin) {
sInstance.mDozePlugin = plugin;
}
@Override
public void onPluginDisconnected(DozeProvider plugin) {
if (sInstance.mDozePlugin == plugin) {
sInstance.mDozePlugin = null;
}
}
},
DozeProvider.VERSION, false /* Only one */);
}
return sInstance;
}
/** Creates a DozeMachine with its parts for {@code dozeService}. */
public static DozeMachine assembleMachine(DozeService dozeService) {
public DozeMachine assembleMachine(DozeService dozeService) {
Context context = dozeService;
SensorManager sensorManager = context.getSystemService(SensorManager.class);
PowerManager powerManager = context.getSystemService(PowerManager.class);
@@ -43,14 +76,103 @@ public class DozeFactory {
DozeMachine machine = new DozeMachine(dozeService, params, wakeLock);
machine.setParts(new DozeMachine.Part[]{
new DozeTriggers(context, machine, host, config, params,
sensorManager, handler, wakeLock),
new DozeUi(context, machine, wakeLock, host),
createDozeTriggers(context, sensorManager, host, config, params, handler, wakeLock,
machine),
createDozeUi(context, host, wakeLock, machine),
});
return machine;
}
private DozeTriggers createDozeTriggers(Context context, SensorManager sensorManager,
DozeHost host, AmbientDisplayConfiguration config, DozeParameters params,
Handler handler, WakeLock wakeLock, DozeMachine machine) {
boolean allowPulseTriggers = mDozePlugin == null || mDozePlugin.allowDefaultPulseTriggers();
return new DozeTriggers(context, machine, host, config, params,
sensorManager, handler, wakeLock, allowPulseTriggers);
}
private DozeMachine.Part createDozeUi(Context context, DozeHost host, WakeLock wakeLock,
DozeMachine machine) {
if (mDozePlugin != null) {
DozeProvider.DozeUi dozeUi = mDozePlugin.provideDozeUi(context,
pluginMachine(context, machine, host),
wakeLock);
return (oldState, newState) -> {
dozeUi.transitionTo(pluginState(oldState),
pluginState(newState));
};
} else {
return new DozeUi(context, machine, wakeLock, host);
}
}
private DozeProvider.DozeMachine pluginMachine(Context context, DozeMachine machine,
DozeHost host) {
return new DozeProvider.DozeMachine() {
@Override
public void requestState(DozeProvider.DozeState state) {
if (state == DozeProvider.DozeState.WAKE_UP) {
PowerManager pm = context.getSystemService(PowerManager.class);
pm.wakeUp(SystemClock.uptimeMillis(), "com.android.systemui:NODOZE");
return;
}
machine.requestState(implState(state));
}
@Override
public void requestSendIntent(PendingIntent intent) {
host.startPendingIntentDismissingKeyguard(intent);
}
};
}
private DozeMachine.State implState(DozeProvider.DozeState s) {
switch (s) {
case UNINITIALIZED:
return DozeMachine.State.UNINITIALIZED;
case INITIALIZED:
return DozeMachine.State.INITIALIZED;
case DOZE:
return DozeMachine.State.DOZE;
case DOZE_AOD:
return DozeMachine.State.DOZE_AOD;
case DOZE_REQUEST_PULSE:
return DozeMachine.State.DOZE_REQUEST_PULSE;
case DOZE_PULSING:
return DozeMachine.State.DOZE_PULSING;
case DOZE_PULSE_DONE:
return DozeMachine.State.DOZE_PULSE_DONE;
case FINISH:
return DozeMachine.State.FINISH;
default:
throw new IllegalArgumentException("Unknown state: " + s);
}
}
private DozeProvider.DozeState pluginState(DozeMachine.State s) {
switch (s) {
case UNINITIALIZED:
return DozeProvider.DozeState.UNINITIALIZED;
case INITIALIZED:
return DozeProvider.DozeState.INITIALIZED;
case DOZE:
return DozeProvider.DozeState.DOZE;
case DOZE_AOD:
return DozeProvider.DozeState.DOZE_AOD;
case DOZE_REQUEST_PULSE:
return DozeProvider.DozeState.DOZE_REQUEST_PULSE;
case DOZE_PULSING:
return DozeProvider.DozeState.DOZE_PULSING;
case DOZE_PULSE_DONE:
return DozeProvider.DozeState.DOZE_PULSE_DONE;
case FINISH:
return DozeProvider.DozeState.FINISH;
default:
throw new IllegalArgumentException("Unknown state: " + s);
}
}
private static DozeHost getHost(DozeService service) {
Application appCandidate = service.getApplication();
final SystemUIApplication app = (SystemUIApplication) appCandidate;
@@ -58,7 +180,7 @@ public class DozeFactory {
}
/** A wrapper around {@link PowerManager.WakeLock} for testability. */
public static class WakeLock {
public static class WakeLock implements DozeProvider.WakeLock {
private final PowerManager.WakeLock mInner;
public WakeLock(PowerManager.WakeLock inner) {
@@ -80,4 +202,13 @@ public class DozeFactory {
return mInner.wrap(runnable);
}
}
/** Hack: We need to initialize the plugin listener before doze actually starts.
* This will be unnecessary once we have proper one-shot support */
public static class Initializer extends SystemUI {
@Override
public void start() {
getInstance(mContext);
}
}
}

View File

@@ -17,6 +17,7 @@
package com.android.systemui.doze;
import android.annotation.NonNull;
import android.app.PendingIntent;
/**
* Interface the doze service uses to communicate with the rest of system UI.
@@ -32,14 +33,16 @@ public interface DozeHost {
boolean isNotificationLightOn();
boolean isPulsingBlocked();
public interface Callback {
void onNewNotifications();
void onBuzzBeepBlinked();
void onNotificationLight(boolean on);
void onPowerSaveChanged(boolean active);
void startPendingIntentDismissingKeyguard(PendingIntent intent);
interface Callback {
default void onNewNotifications() {}
default void onBuzzBeepBlinked() {}
default void onNotificationLight(boolean on) {}
default void onPowerSaveChanged(boolean active) {}
}
public interface PulseCallback {
interface PulseCallback {
void onPulseStarted();
void onPulseFinished();
}

View File

@@ -38,7 +38,7 @@ public class DozeService extends DreamService implements DozeMachine.Service {
setWindowless(true);
mDozeMachine = DozeFactory.assembleMachine(this);
mDozeMachine = DozeFactory.getInstance(getApplication()).assembleMachine(this);
}
@Override

View File

@@ -58,6 +58,7 @@ public class DozeTriggers implements DozeMachine.Part {
private final SensorManager mSensorManager;
private final Handler mHandler;
private final DozeFactory.WakeLock mWakeLock;
private final boolean mAllowPulseTriggers;
private final UiModeManager mUiModeManager;
private final TriggerReceiver mBroadcastReceiver = new TriggerReceiver();
@@ -68,7 +69,7 @@ public class DozeTriggers implements DozeMachine.Part {
public DozeTriggers(Context context, DozeMachine machine, DozeHost dozeHost,
AmbientDisplayConfiguration config,
DozeParameters dozeParameters, SensorManager sensorManager, Handler handler,
DozeFactory.WakeLock wakeLock) {
DozeFactory.WakeLock wakeLock, boolean allowPulseTriggers) {
mContext = context;
mMachine = machine;
mDozeHost = dozeHost;
@@ -77,6 +78,7 @@ public class DozeTriggers implements DozeMachine.Part {
mSensorManager = sensorManager;
mHandler = handler;
mWakeLock = wakeLock;
mAllowPulseTriggers = allowPulseTriggers;
mDozeSensors = new DozeSensors(context, mSensorManager, dozeParameters, config,
wakeLock, this::onSensor);
mUiModeManager = mContext.getSystemService(UiModeManager.class);
@@ -149,7 +151,7 @@ public class DozeTriggers implements DozeMachine.Part {
private void requestPulse(final int reason, boolean performedProxCheck) {
Assert.isMainThread();
if (mPulsePending || !canPulse()) {
if (mPulsePending || !mAllowPulseTriggers || !canPulse()) {
return;
}
@@ -306,20 +308,11 @@ public class DozeTriggers implements DozeMachine.Part {
}
private DozeHost.Callback mHostCallback = new DozeHost.Callback() {
@Override
public void onNewNotifications() {
}
@Override
public void onBuzzBeepBlinked() {
onNotification();
}
@Override
public void onNotificationLight(boolean on) {
}
@Override
public void onPowerSaveChanged(boolean active) {
if (active) {

View File

@@ -5167,5 +5167,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode,
return mNotificationLightOn;
}
@Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
PhoneStatusBar.this.startPendingIntentDismissingKeyguard(intent);
}
}
}