Loading core/java/com/android/internal/util/NotificationColorUtil.java +39 −0 Original line number Diff line number Diff line Loading @@ -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. Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/doze/DozeProvider.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * 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); } } packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +3 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -76,7 +77,8 @@ public class SystemUIApplication extends Application { PipUI.class, ShortcutKeyDispatcher.class, VendorServices.class, LatencyTester.class LatencyTester.class, DozeFactory.Initializer.class, }; /** Loading packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +136 −5 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading @@ -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) { Loading @@ -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); } } } packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +9 −6 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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(); } Loading Loading
core/java/com/android/internal/util/NotificationColorUtil.java +39 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/doze/DozeProvider.java 0 → 100644 +100 −0 Original line number Diff line number Diff line /* * 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); } }
packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +3 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -76,7 +77,8 @@ public class SystemUIApplication extends Application { PipUI.class, ShortcutKeyDispatcher.class, VendorServices.class, LatencyTester.class LatencyTester.class, DozeFactory.Initializer.class, }; /** Loading
packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +136 −5 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading @@ -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) { Loading @@ -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); } } }
packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +9 −6 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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(); } Loading