diff options
10 files changed, 408 insertions, 8 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/dock/DockManager.java b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java new file mode 100644 index 000000000000..233b9c678085 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dock/DockManager.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2018 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.dock; + +/** + * Allows an app to handle dock events + */ +public interface DockManager { + + /** + * Uninitialized / unknow dock states + */ + int STATE_NONE = 0; + /** + * The state for docking + */ + int STATE_DOCKING = 1; + /** + * The state for undocking + */ + int STATE_UNDOCKING = 2; + + /** + * Add a dock event listener into manager + * + * @param callback A {@link #DockEventListener} which want to add + */ + void addListener(DockEventListener callback); + + /** + * Remove the added listener from dock manager + * + * @param callback A {@link #DockEventListener} which want to remove + */ + void removeListener(DockEventListener callback); + + /** Callback for receiving dock events */ + interface DockEventListener { + /** + * Override to handle dock events + * + * Events reference: {@link #DockState} + */ + void onEvent(int event); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java new file mode 100644 index 000000000000..fa33cb847880 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeDockHandler.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2018 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.doze; + +import android.content.Context; +import android.os.Handler; +import android.util.Log; + +import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.systemui.SysUiServiceProvider; +import com.android.systemui.dock.DockManager; + +import java.io.PrintWriter; + +/** + * Handles dock events for ambient state changes. + */ +public class DozeDockHandler implements DozeMachine.Part { + + private static final String TAG = "DozeDockHandler"; + private static final boolean DEBUG = DozeService.DEBUG; + + private final Context mContext; + private final DozeMachine mMachine; + private final DozeHost mDozeHost; + private final AmbientDisplayConfiguration mConfig; + private final Handler mHandler; + private final DockEventListener mDockEventListener = new DockEventListener(); + private final DockManager mDockManager; + + private boolean mDocking; + + public DozeDockHandler(Context context, DozeMachine machine, DozeHost dozeHost, + AmbientDisplayConfiguration config, Handler handler) { + mContext = context; + mMachine = machine; + mDozeHost = dozeHost; + mConfig = config; + mHandler = handler; + mDockManager = SysUiServiceProvider.getComponent(context, DockManager.class); + } + + @Override + public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { + switch (newState) { + case INITIALIZED: + mDockEventListener.register(); + break; + case DOZE: + case DOZE_AOD: + mHandler.post(() -> requestPulse()); + break; + case FINISH: + mDockEventListener.unregister(); + break; + default: + } + } + + private void requestPulse() { + if (!mDocking || mDozeHost.isPulsingBlocked() || !canPulse()) { + return; + } + + mMachine.requestPulse(DozeLog.PULSE_REASON_DOCKING); + } + + private boolean canPulse() { + return mMachine.getState() == DozeMachine.State.DOZE + || mMachine.getState() == DozeMachine.State.DOZE_AOD; + } + + private void requestPulseOutNow() { + final DozeMachine.State state = mMachine.getState(); + final int pulseReason = mMachine.getPulseReason(); + + if ((state == DozeMachine.State.DOZE_PULSING + || state == DozeMachine.State.DOZE_REQUEST_PULSE) + && pulseReason == DozeLog.PULSE_REASON_DOCKING) { + mDozeHost.stopPulsing(); + } + } + + @Override + public void dump(PrintWriter pw) { + pw.print(" DozeDockTriggers docking="); pw.println(mDocking); + } + + private class DockEventListener implements DockManager.DockEventListener { + private boolean mRegistered; + + @Override + public void onEvent(int event) { + if (DEBUG) Log.d(TAG, "dock event = " + event); + switch (event) { + case DockManager.STATE_DOCKING: + mDocking = true; + requestPulse(); + break; + case DockManager.STATE_UNDOCKING: + mDocking = false; + requestPulseOutNow(); + break; + default: + // no-op + } + } + + void register() { + if (mRegistered) { + return; + } + + if (mDockManager != null) { + mDockManager.addListener(this); + } + mRegistered = true; + } + + void unregister() { + if (!mRegistered) { + return; + } + if (mDockManager != null) { + mDockManager.removeListener(this); + } + mRegistered = false; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index d93ed1785a0a..58ae555dbff1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -68,7 +68,8 @@ public class DozeFactory { new DozeScreenState(wrappedService, handler, params, wakeLock), createDozeScreenBrightness(context, wrappedService, sensorManager, host, params, handler), - new DozeWallpaperState(context) + new DozeWallpaperState(context), + new DozeDockHandler(context, machine, host, config, handler) }); return machine; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 461aef1fa54c..4cb1feebcce4 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -45,6 +45,11 @@ public interface DozeHost { void onIgnoreTouchWhilePulsing(boolean ignore); + /** + * Leaves pulsing state, going back to ambient UI. + */ + void stopPulsing(); + interface Callback { /** * Called when a high priority notification is added. diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index 974cd8804841..ab77e553303b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -35,7 +35,7 @@ public class DozeLog { private static final int SIZE = Build.IS_DEBUGGABLE ? 400 : 50; static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); - private static final int REASONS = 7; + private static final int REASONS = 8; public static final int PULSE_REASON_NONE = -1; public static final int PULSE_REASON_INTENT = 0; @@ -44,7 +44,8 @@ public class DozeLog { public static final int PULSE_REASON_SENSOR_PICKUP = 3; public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4; public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5; - public static final int REASON_SENSOR_WAKE_UP = 6; + public static final int PULSE_REASON_DOCKING = 6; + public static final int REASON_SENSOR_WAKE_UP = 7; private static boolean sRegisterKeyguardCallback = true; @@ -211,6 +212,7 @@ public class DozeLog { case PULSE_REASON_SENSOR_PICKUP: return "pickup"; case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap"; case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; + case PULSE_REASON_DOCKING: return "docking"; case REASON_SENSOR_WAKE_UP: return "wakeup"; default: throw new IllegalArgumentException("bad reason: " + pulseReason); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java index cfa751cec613..280dda0cd1dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeScrimController.java @@ -65,10 +65,11 @@ public class DozeScrimController implements StateListener { if (!mDozing) { return; } - // All pulses except notifications should time out on their own. Pulses due to - // notifications should instead be managed externally based off the notification's - // lifetime. - if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION) { + // Notifications should time out on their own. Pulses due to notifications should + // instead be managed externally based off the notification's lifetime. + // Dock also controls the time out by self. + if (mPulseReason != DozeLog.PULSE_REASON_NOTIFICATION + && mPulseReason != DozeLog.PULSE_REASON_DOCKING) { mHandler.postDelayed(mPulseOut, mDozeParameters.getPulseVisibleDuration()); mHandler.postDelayed(mPulseOutExtended, mDozeParameters.getPulseVisibleDurationExtended()); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 465187d038ad..40e2aaec44dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4066,6 +4066,13 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override + public void stopPulsing() { + if (mDozeScrimController.isPulsing()) { + mDozeScrimController.pulseOutNow(); + } + } + + @Override public void setAnimateWakeup(boolean animateWakeup) { if (mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_AWAKE || mWakefulnessLifecycle.getWakefulness() == WAKEFULNESS_WAKING) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java new file mode 100644 index 000000000000..b368876d2ae7 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dock/DockManagerFake.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2018 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.dock; + +/** + * A rudimentary fake for DockManager. + */ +public class DockManagerFake implements DockManager { + DockEventListener mCallback; + + @Override + public void addListener(DockEventListener callback) { + this.mCallback = callback; + } + + @Override + public void removeListener(DockEventListener callback) { + this.mCallback = null; + } + + public void setDockEvent(int event) { + mCallback.onEvent(event); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java new file mode 100644 index 000000000000..e1c0cd4dc7e9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2018 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.doze; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.Instrumentation; +import android.os.Handler; +import android.os.Looper; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.RunWithLooper; + +import com.android.internal.hardware.AmbientDisplayConfiguration; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dock.DockManager; +import com.android.systemui.dock.DockManagerFake; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +@RunWithLooper +public class DozeDockHandlerTest extends SysuiTestCase { + private DozeDockHandler mDockHandler; + private DozeMachine mMachine; + private DozeHostFake mHost; + private AmbientDisplayConfiguration mConfig; + private Instrumentation mInstrumentation; + private DockManagerFake mDockManagerFake; + + @BeforeClass + public static void setupSuite() { + // We can't use KeyguardUpdateMonitor from tests. + DozeLog.setRegisterKeyguardCallback(false); + } + + @Before + public void setUp() throws Exception { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mMachine = mock(DozeMachine.class); + mHost = spy(new DozeHostFake()); + mConfig = DozeConfigurationUtil.createMockConfig(); + + mDockManagerFake = spy(new DockManagerFake()); + mContext.putComponent(DockManager.class, mDockManagerFake); + + mDockHandler = new DozeDockHandler(mContext, mMachine, mHost, mConfig, + Handler.createAsync(Looper.myLooper())); + } + + @Test + public void testDockEventListener_registerAndUnregister() throws Exception { + mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + + verify(mDockManagerFake).addListener(any()); + + mDockHandler.transitionTo(DozeMachine.State.DOZE, DozeMachine.State.FINISH); + + verify(mDockManagerFake).removeListener(any()); + } + + @Test + public void testOnEvent_dockingWhenDoze_requestPulse() throws Exception { + mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + mDockManagerFake.setDockEvent(DockManager.STATE_DOCKING); + + verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING)); + } + + @Test + public void testOnEvent_dockingWhenPausing_neverRequestPulse() throws Exception { + mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_AOD_PAUSING); + + mDockManagerFake.setDockEvent(DockManager.STATE_DOCKING); + + verify(mMachine, never()).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING)); + } + + @Test + public void testOnEvent_undockedWhenPulsing_requestPulseOut() throws Exception { + mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING); + when(mMachine.getPulseReason()).thenReturn(DozeLog.PULSE_REASON_DOCKING); + + mDockManagerFake.setDockEvent(DockManager.STATE_UNDOCKING); + + verify(mHost).stopPulsing(); + } + + @Test + public void testOnEvent_undockedWhenDoze_neverRequestPulseOut() throws Exception { + mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE_PULSING); + + mDockManagerFake.setDockEvent(DockManager.STATE_UNDOCKING); + + verify(mHost, never()).stopPulsing(); + } + + @Test + public void testTransitionToDozeWhenDocking_RequestPulse() throws Exception { + mDockHandler.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); + mDockManagerFake.setDockEvent(DockManager.STATE_DOCKING); + mDockHandler.transitionTo(DozeMachine.State.DOZE_AOD_PAUSING, DozeMachine.State.DOZE); + when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + + TestableLooper.get(this).processAllMessages(); + + verify(mMachine).requestPulse(eq(DozeLog.PULSE_REASON_DOCKING)); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java index f3cb27f8d010..ce28b50436eb 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java @@ -17,7 +17,6 @@ package com.android.systemui.doze; import android.annotation.NonNull; -import android.app.PendingIntent; /** * A rudimentary fake for DozeHost. @@ -92,6 +91,9 @@ class DozeHostFake implements DozeHost { } @Override + public void stopPulsing() {} + + @Override public void setAnimateWakeup(boolean animateWakeup) { this.animateWakeup = animateWakeup; } |