diff --git a/core/java/com/android/internal/util/NotificationColorUtil.java b/core/java/com/android/internal/util/NotificationColorUtil.java index 4748e6fb19ff0..7ee5170ef6184 100644 --- a/core/java/com/android/internal/util/NotificationColorUtil.java +++ b/core/java/com/android/internal/util/NotificationColorUtil.java @@ -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. diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/doze/DozeProvider.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/doze/DozeProvider.java new file mode 100644 index 0000000000000..688df466d2444 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/doze/DozeProvider.java @@ -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); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 6802fd7acbd49..b20798481a585 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -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, }; /** diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index 4cfc811de1a0a..5b10756988a40 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -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() { + @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); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index fb940b511b24f..e081b53ffda3f 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -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(); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 94cbdd41e220b..78b96b34efad0 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -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 diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 9f26b0c57dbea..84b22ea93b811 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -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) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index c4fb21eda9826..6f13ba50b22fc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -5167,5 +5167,10 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode, return mNotificationLightOn; } + @Override + public void startPendingIntentDismissingKeyguard(PendingIntent intent) { + PhoneStatusBar.this.startPendingIntentDismissingKeyguard(intent); + } + } }