Loading core/java/android/service/dreams/DreamOverlayService.java +4 −5 Original line number Diff line number Diff line Loading @@ -42,8 +42,11 @@ public abstract class DreamOverlayService extends Service { private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override public void startDream(WindowManager.LayoutParams layoutParams, IDreamOverlayCallback callback) { IDreamOverlayCallback callback, String dreamComponent, boolean shouldShowComplications) { mDreamOverlayCallback = callback; mDreamComponent = ComponentName.unflattenFromString(dreamComponent); mShowComplications = shouldShowComplications; onStartDream(layoutParams); } }; Loading @@ -56,10 +59,6 @@ public abstract class DreamOverlayService extends Service { @Nullable @Override public final IBinder onBind(@NonNull Intent intent) { mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, DreamService.DEFAULT_SHOW_COMPLICATIONS); mDreamComponent = intent.getParcelableExtra(DreamService.EXTRA_DREAM_COMPONENT, ComponentName.class); return mDreamOverlay.asBinder(); } Loading core/java/android/service/dreams/DreamService.java +12 −22 Original line number Diff line number Diff line Loading @@ -213,19 +213,6 @@ public class DreamService extends Service implements Window.Callback { */ private static final String DREAM_META_DATA_ROOT_TAG = "dream"; /** * Extra containing a boolean for whether to show complications on the overlay. * @hide */ public static final String EXTRA_SHOW_COMPLICATIONS = "android.service.dreams.SHOW_COMPLICATIONS"; /** * Extra containing the component name for the active dream. * @hide */ public static final String EXTRA_DREAM_COMPONENT = "android.service.dreams.DREAM_COMPONENT"; /** * The default value for whether to show complications on the overlay. * Loading @@ -252,6 +239,9 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; private ComponentName mDreamComponent; private boolean mShouldShowComplications; private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; Loading Loading @@ -947,6 +937,11 @@ public class DreamService extends Service implements Window.Callback { @Override public void onCreate() { if (mDebug) Slog.v(mTag, "onCreate()"); mDreamComponent = new ComponentName(this, getClass()); mShouldShowComplications = fetchShouldShowComplications(this /*context*/, fetchServiceInfo(this /*context*/, mDreamComponent)); super.onCreate(); } Loading Loading @@ -994,14 +989,7 @@ public class DreamService extends Service implements Window.Callback { // Connect to the overlay service if present. if (!mWindowless && overlayComponent != null) { final Resources resources = getResources(); final ComponentName dreamService = new ComponentName(this, getClass()); final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService); final Intent overlayIntent = new Intent() .setComponent(overlayComponent) .putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(this, serviceInfo)) .putExtra(EXTRA_DREAM_COMPONENT, dreamService); final Intent overlayIntent = new Intent().setComponent(overlayComponent); mOverlayConnection = new OverlayConnection( /* context= */ this, Loading Loading @@ -1364,7 +1352,9 @@ public class DreamService extends Service implements Window.Callback { // parameters once the window has been attached. mDreamStartOverlayConsumer = overlay -> { try { overlay.startDream(mWindow.getAttributes(), mOverlayCallback); overlay.startDream(mWindow.getAttributes(), mOverlayCallback, mDreamComponent.flattenToString(), mShouldShowComplications); } catch (RemoteException e) { Log.e(mTag, "could not send window attributes:" + e); } Loading core/java/android/service/dreams/IDreamOverlay.aidl +6 −2 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ interface IDreamOverlay { token of the Dream Activity. * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the * dream. * @param dreamComponent The component name of the dream service requesting overlay. * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock * and weather. */ void startDream(in LayoutParams params, in IDreamOverlayCallback callback); void startDream(in LayoutParams params, in IDreamOverlayCallback callback, in String dreamComponent, in boolean shouldShowComplications); } packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +54 −29 Original line number Diff line number Diff line Loading @@ -64,29 +64,26 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final Executor mExecutor; // A controller for the dream overlay container view (which contains both the status bar and the // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Nullable private final ComponentName mLowLightDreamComponent; private final UiEventLogger mUiEventLogger; private final WindowManager mWindowManager; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; // True if a dream has bound to the service and dream overlay service has started. private boolean mStarted = false; // True if the service has been destroyed. private boolean mDestroyed; private boolean mDestroyed = false; private final Complication.Host mHost = new Complication.Host() { @Override public void requestExitDream() { mExecutor.execute(DreamOverlayService.this::requestExit); } }; private final DreamOverlayComponent mDreamOverlayComponent; private final LifecycleRegistry mLifecycleRegistry; private ViewModelStore mViewModelStore = new ViewModelStore(); private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor; private final KeyguardUpdateMonitorCallback mKeyguardCallback = Loading @@ -103,7 +100,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; private DreamOverlayStateController mStateController; private final DreamOverlayStateController mStateController; @VisibleForTesting public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum { Loading @@ -128,6 +125,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public DreamOverlayService( Context context, @Main Executor executor, WindowManager windowManager, DreamOverlayComponent.Factory dreamOverlayComponentFactory, DreamOverlayStateController stateController, KeyguardUpdateMonitor keyguardUpdateMonitor, Loading @@ -136,19 +134,19 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ ComponentName lowLightDreamComponent) { mContext = context; mExecutor = executor; mWindowManager = windowManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLowLightDreamComponent = lowLightDreamComponent; mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mStateController = stateController; mUiEventLogger = uiEventLogger; final DreamOverlayComponent component = dreamOverlayComponentFactory.create(mViewModelStore, mHost); mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController(); final ViewModelStore viewModelStore = new ViewModelStore(); final Complication.Host host = () -> mExecutor.execute(DreamOverlayService.this::requestExit); mDreamOverlayComponent = dreamOverlayComponentFactory.create(viewModelStore, host); mLifecycleRegistry = mDreamOverlayComponent.getLifecycleRegistry(); setCurrentState(Lifecycle.State.CREATED); mLifecycleRegistry = component.getLifecycleRegistry(); mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor(); mDreamOverlayTouchMonitor.init(); } private void setCurrentState(Lifecycle.State state) { Loading @@ -159,34 +157,48 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onDestroy() { mKeyguardUpdateMonitor.removeCallback(mKeyguardCallback); setCurrentState(Lifecycle.State.DESTROYED); final WindowManager windowManager = mContext.getSystemService(WindowManager.class); if (mWindow != null) { windowManager.removeView(mWindow.getDecorView()); } mStateController.setOverlayActive(false); mStateController.setLowLightActive(false); resetCurrentDreamOverlay(); mDestroyed = true; super.onDestroy(); } @Override public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); setCurrentState(Lifecycle.State.STARTED); final ComponentName dreamComponent = getDreamComponent(); mStateController.setLowLightActive( dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mExecutor.execute(() -> { mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); if (mDestroyed) { // The task could still be executed after the service has been destroyed. Bail if // that is the case. return; } if (mStarted) { // Reset the current dream overlay before starting a new one. This can happen // when two dreams overlap (briefly, for a smoother dream transition) and both // dreams are bound to the dream overlay service. resetCurrentDreamOverlay(); } mDreamOverlayContainerViewController = mDreamOverlayComponent.getDreamOverlayContainerViewController(); mDreamOverlayTouchMonitor = mDreamOverlayComponent.getDreamOverlayTouchMonitor(); mDreamOverlayTouchMonitor.init(); mStateController.setShouldShowComplications(shouldShowComplications()); addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); final ComponentName dreamComponent = getDreamComponent(); mStateController.setLowLightActive( dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); mStarted = true; }); } Loading Loading @@ -222,8 +234,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ removeContainerViewFromParent(); mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView()); final WindowManager windowManager = mContext.getSystemService(WindowManager.class); windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); } private void removeContainerViewFromParent() { Loading @@ -238,4 +249,18 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ Log.w(TAG, "Removing dream overlay container view parent!"); parentView.removeView(containerView); } private void resetCurrentDreamOverlay() { if (mStarted && mWindow != null) { mWindowManager.removeView(mWindow.getDecorView()); } mStateController.setOverlayActive(false); mStateController.setLowLightActive(false); mDreamOverlayContainerViewController = null; mDreamOverlayTouchMonitor = null; mStarted = false; } } packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +102 −25 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.dreams; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading @@ -27,10 +28,10 @@ import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.service.dreams.DreamService; import android.service.dreams.IDreamOverlay; import android.service.dreams.IDreamOverlayCallback; import android.testing.AndroidTestingRunner; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerImpl; Loading @@ -53,6 +54,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading @@ -61,6 +64,7 @@ import org.mockito.MockitoAnnotations; public class DreamOverlayServiceTest extends SysuiTestCase { private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", "lowlight"); private static final String DREAM_COMPONENT = "package/dream"; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); Loading Loading @@ -108,12 +112,14 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock UiEventLogger mUiEventLogger; @Captor ArgumentCaptor<View> mViewCaptor; DreamOverlayService mService; @Before public void setup() { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(WindowManager.class, mWindowManager); when(mDreamOverlayComponent.getDreamOverlayContainerViewController()) .thenReturn(mDreamOverlayContainerViewController); Loading @@ -129,7 +135,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { when(mDreamOverlayContainerViewController.getContainerView()) .thenReturn(mDreamOverlayContainerView); mService = new DreamOverlayService(mContext, mMainExecutor, mService = new DreamOverlayService(mContext, mMainExecutor, mWindowManager, mDreamOverlayComponentFactory, mStateController, mKeyguardUpdateMonitor, Loading @@ -143,7 +149,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); Loading @@ -157,7 +164,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mWindowManager).addView(any(), any()); Loading @@ -169,7 +177,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewController).init(); Loading @@ -186,49 +195,76 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView); } @Test public void testShouldShowComplicationsFalseByDefault() { mService.onBind(new Intent()); public void testShouldShowComplicationsSetByStartDream() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); assertThat(mService.shouldShowComplications()).isFalse(); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, true /*shouldShowComplication*/); assertThat(mService.shouldShowComplications()).isTrue(); } @Test public void testShouldShowComplicationsSetByIntentExtra() { final Intent intent = new Intent(); intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, true); mService.onBind(intent); public void testLowLightSetByStartDream() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); assertThat(mService.shouldShowComplications()).isTrue(); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); mMainExecutor.runAllReady(); assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); verify(mStateController).setLowLightActive(true); } @Test public void testLowLightSetByIntentExtra() throws RemoteException { final Intent intent = new Intent(); intent.putExtra(DreamService.EXTRA_DREAM_COMPONENT, LOW_LIGHT_COMPONENT); final IBinder proxy = mService.onBind(intent); public void testDestroy() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mStateController).setLowLightActive(true); // Verify view added. verify(mWindowManager).addView(mViewCaptor.capture(), any()); // Service destroyed. mService.onDestroy(); mMainExecutor.runAllReady(); // Verify view removed. verify(mWindowManager).removeView(mViewCaptor.getValue()); // Verify state correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()); verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); verify(mStateController).setOverlayActive(false); verify(mStateController).setLowLightActive(false); } @Test public void testDestroy() { public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() { // Service destroyed without ever starting dream. mService.onDestroy(); mMainExecutor.runAllReady(); // Verify no view is removed. verify(mWindowManager, never()).removeView(any()); // Verify state still correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()); verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); verify(mStateController).setOverlayActive(false); Loading @@ -245,7 +281,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); // Destroy the service. mService.onDestroy(); Loading @@ -255,4 +292,44 @@ public class DreamOverlayServiceTest extends SysuiTestCase { verify(mWindowManager, never()).addView(any(), any()); } @Test public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. Do not show dream complications. overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); // Verify that a new window is added. verify(mWindowManager).addView(mViewCaptor.capture(), any()); final View windowDecorView = mViewCaptor.getValue(); // Assert that the overlay is not showing complications. assertThat(mService.shouldShowComplications()).isFalse(); clearInvocations(mDreamOverlayComponent); clearInvocations(mWindowManager); // New dream starting with dream complications showing. Note that when a new dream is // binding to the dream overlay service, it receives the same instance of IBinder as the // first one. overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, true /*shouldShowComplication*/); mMainExecutor.runAllReady(); // Assert that the overlay is showing complications. assertThat(mService.shouldShowComplications()).isTrue(); // Verify that the old overlay window has been removed, and a new one created. verify(mWindowManager).removeView(windowDecorView); verify(mWindowManager).addView(any(), any()); // Verify that new instances of overlay container view controller and overlay touch monitor // are created. verify(mDreamOverlayComponent).getDreamOverlayContainerViewController(); verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor(); } } Loading
core/java/android/service/dreams/DreamOverlayService.java +4 −5 Original line number Diff line number Diff line Loading @@ -42,8 +42,11 @@ public abstract class DreamOverlayService extends Service { private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() { @Override public void startDream(WindowManager.LayoutParams layoutParams, IDreamOverlayCallback callback) { IDreamOverlayCallback callback, String dreamComponent, boolean shouldShowComplications) { mDreamOverlayCallback = callback; mDreamComponent = ComponentName.unflattenFromString(dreamComponent); mShowComplications = shouldShowComplications; onStartDream(layoutParams); } }; Loading @@ -56,10 +59,6 @@ public abstract class DreamOverlayService extends Service { @Nullable @Override public final IBinder onBind(@NonNull Intent intent) { mShowComplications = intent.getBooleanExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, DreamService.DEFAULT_SHOW_COMPLICATIONS); mDreamComponent = intent.getParcelableExtra(DreamService.EXTRA_DREAM_COMPONENT, ComponentName.class); return mDreamOverlay.asBinder(); } Loading
core/java/android/service/dreams/DreamService.java +12 −22 Original line number Diff line number Diff line Loading @@ -213,19 +213,6 @@ public class DreamService extends Service implements Window.Callback { */ private static final String DREAM_META_DATA_ROOT_TAG = "dream"; /** * Extra containing a boolean for whether to show complications on the overlay. * @hide */ public static final String EXTRA_SHOW_COMPLICATIONS = "android.service.dreams.SHOW_COMPLICATIONS"; /** * Extra containing the component name for the active dream. * @hide */ public static final String EXTRA_DREAM_COMPONENT = "android.service.dreams.DREAM_COMPONENT"; /** * The default value for whether to show complications on the overlay. * Loading @@ -252,6 +239,9 @@ public class DreamService extends Service implements Window.Callback { private boolean mDebug = false; private ComponentName mDreamComponent; private boolean mShouldShowComplications; private DreamServiceWrapper mDreamServiceWrapper; private Runnable mDispatchAfterOnAttachedToWindow; Loading Loading @@ -947,6 +937,11 @@ public class DreamService extends Service implements Window.Callback { @Override public void onCreate() { if (mDebug) Slog.v(mTag, "onCreate()"); mDreamComponent = new ComponentName(this, getClass()); mShouldShowComplications = fetchShouldShowComplications(this /*context*/, fetchServiceInfo(this /*context*/, mDreamComponent)); super.onCreate(); } Loading Loading @@ -994,14 +989,7 @@ public class DreamService extends Service implements Window.Callback { // Connect to the overlay service if present. if (!mWindowless && overlayComponent != null) { final Resources resources = getResources(); final ComponentName dreamService = new ComponentName(this, getClass()); final ServiceInfo serviceInfo = fetchServiceInfo(this, dreamService); final Intent overlayIntent = new Intent() .setComponent(overlayComponent) .putExtra(EXTRA_SHOW_COMPLICATIONS, fetchShouldShowComplications(this, serviceInfo)) .putExtra(EXTRA_DREAM_COMPONENT, dreamService); final Intent overlayIntent = new Intent().setComponent(overlayComponent); mOverlayConnection = new OverlayConnection( /* context= */ this, Loading Loading @@ -1364,7 +1352,9 @@ public class DreamService extends Service implements Window.Callback { // parameters once the window has been attached. mDreamStartOverlayConsumer = overlay -> { try { overlay.startDream(mWindow.getAttributes(), mOverlayCallback); overlay.startDream(mWindow.getAttributes(), mOverlayCallback, mDreamComponent.flattenToString(), mShouldShowComplications); } catch (RemoteException e) { Log.e(mTag, "could not send window attributes:" + e); } Loading
core/java/android/service/dreams/IDreamOverlay.aidl +6 −2 Original line number Diff line number Diff line Loading @@ -32,6 +32,10 @@ interface IDreamOverlay { token of the Dream Activity. * @param callback The {@link IDreamOverlayCallback} for requesting actions such as exiting the * dream. * @param dreamComponent The component name of the dream service requesting overlay. * @param shouldShowComplications Whether the dream overlay should show complications, e.g. clock * and weather. */ void startDream(in LayoutParams params, in IDreamOverlayCallback callback); void startDream(in LayoutParams params, in IDreamOverlayCallback callback, in String dreamComponent, in boolean shouldShowComplications); }
packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +54 −29 Original line number Diff line number Diff line Loading @@ -64,29 +64,26 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final Executor mExecutor; // A controller for the dream overlay container view (which contains both the status bar and the // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @Nullable private final ComponentName mLowLightDreamComponent; private final UiEventLogger mUiEventLogger; private final WindowManager mWindowManager; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; // True if a dream has bound to the service and dream overlay service has started. private boolean mStarted = false; // True if the service has been destroyed. private boolean mDestroyed; private boolean mDestroyed = false; private final Complication.Host mHost = new Complication.Host() { @Override public void requestExitDream() { mExecutor.execute(DreamOverlayService.this::requestExit); } }; private final DreamOverlayComponent mDreamOverlayComponent; private final LifecycleRegistry mLifecycleRegistry; private ViewModelStore mViewModelStore = new ViewModelStore(); private DreamOverlayTouchMonitor mDreamOverlayTouchMonitor; private final KeyguardUpdateMonitorCallback mKeyguardCallback = Loading @@ -103,7 +100,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; private DreamOverlayStateController mStateController; private final DreamOverlayStateController mStateController; @VisibleForTesting public enum DreamOverlayEvent implements UiEventLogger.UiEventEnum { Loading @@ -128,6 +125,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public DreamOverlayService( Context context, @Main Executor executor, WindowManager windowManager, DreamOverlayComponent.Factory dreamOverlayComponentFactory, DreamOverlayStateController stateController, KeyguardUpdateMonitor keyguardUpdateMonitor, Loading @@ -136,19 +134,19 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ ComponentName lowLightDreamComponent) { mContext = context; mExecutor = executor; mWindowManager = windowManager; mKeyguardUpdateMonitor = keyguardUpdateMonitor; mLowLightDreamComponent = lowLightDreamComponent; mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback); mStateController = stateController; mUiEventLogger = uiEventLogger; final DreamOverlayComponent component = dreamOverlayComponentFactory.create(mViewModelStore, mHost); mDreamOverlayContainerViewController = component.getDreamOverlayContainerViewController(); final ViewModelStore viewModelStore = new ViewModelStore(); final Complication.Host host = () -> mExecutor.execute(DreamOverlayService.this::requestExit); mDreamOverlayComponent = dreamOverlayComponentFactory.create(viewModelStore, host); mLifecycleRegistry = mDreamOverlayComponent.getLifecycleRegistry(); setCurrentState(Lifecycle.State.CREATED); mLifecycleRegistry = component.getLifecycleRegistry(); mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor(); mDreamOverlayTouchMonitor.init(); } private void setCurrentState(Lifecycle.State state) { Loading @@ -159,34 +157,48 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onDestroy() { mKeyguardUpdateMonitor.removeCallback(mKeyguardCallback); setCurrentState(Lifecycle.State.DESTROYED); final WindowManager windowManager = mContext.getSystemService(WindowManager.class); if (mWindow != null) { windowManager.removeView(mWindow.getDecorView()); } mStateController.setOverlayActive(false); mStateController.setLowLightActive(false); resetCurrentDreamOverlay(); mDestroyed = true; super.onDestroy(); } @Override public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); setCurrentState(Lifecycle.State.STARTED); final ComponentName dreamComponent = getDreamComponent(); mStateController.setLowLightActive( dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mExecutor.execute(() -> { mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); if (mDestroyed) { // The task could still be executed after the service has been destroyed. Bail if // that is the case. return; } if (mStarted) { // Reset the current dream overlay before starting a new one. This can happen // when two dreams overlap (briefly, for a smoother dream transition) and both // dreams are bound to the dream overlay service. resetCurrentDreamOverlay(); } mDreamOverlayContainerViewController = mDreamOverlayComponent.getDreamOverlayContainerViewController(); mDreamOverlayTouchMonitor = mDreamOverlayComponent.getDreamOverlayTouchMonitor(); mDreamOverlayTouchMonitor.init(); mStateController.setShouldShowComplications(shouldShowComplications()); addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); final ComponentName dreamComponent = getDreamComponent(); mStateController.setLowLightActive( dreamComponent != null && dreamComponent.equals(mLowLightDreamComponent)); mUiEventLogger.log(DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); mStarted = true; }); } Loading Loading @@ -222,8 +234,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ removeContainerViewFromParent(); mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView()); final WindowManager windowManager = mContext.getSystemService(WindowManager.class); windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); mWindowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); } private void removeContainerViewFromParent() { Loading @@ -238,4 +249,18 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ Log.w(TAG, "Removing dream overlay container view parent!"); parentView.removeView(containerView); } private void resetCurrentDreamOverlay() { if (mStarted && mWindow != null) { mWindowManager.removeView(mWindow.getDecorView()); } mStateController.setOverlayActive(false); mStateController.setLowLightActive(false); mDreamOverlayContainerViewController = null; mDreamOverlayTouchMonitor = null; mStarted = false; } }
packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +102 −25 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.dreams; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; Loading @@ -27,10 +28,10 @@ import android.content.ComponentName; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import android.service.dreams.DreamService; import android.service.dreams.IDreamOverlay; import android.service.dreams.IDreamOverlayCallback; import android.testing.AndroidTestingRunner; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.WindowManagerImpl; Loading @@ -53,6 +54,8 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; Loading @@ -61,6 +64,7 @@ import org.mockito.MockitoAnnotations; public class DreamOverlayServiceTest extends SysuiTestCase { private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", "lowlight"); private static final String DREAM_COMPONENT = "package/dream"; private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); Loading Loading @@ -108,12 +112,14 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock UiEventLogger mUiEventLogger; @Captor ArgumentCaptor<View> mViewCaptor; DreamOverlayService mService; @Before public void setup() { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(WindowManager.class, mWindowManager); when(mDreamOverlayComponent.getDreamOverlayContainerViewController()) .thenReturn(mDreamOverlayContainerViewController); Loading @@ -129,7 +135,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { when(mDreamOverlayContainerViewController.getContainerView()) .thenReturn(mDreamOverlayContainerView); mService = new DreamOverlayService(mContext, mMainExecutor, mService = new DreamOverlayService(mContext, mMainExecutor, mWindowManager, mDreamOverlayComponentFactory, mStateController, mKeyguardUpdateMonitor, Loading @@ -143,7 +149,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); Loading @@ -157,7 +164,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mWindowManager).addView(any(), any()); Loading @@ -169,7 +177,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewController).init(); Loading @@ -186,49 +195,76 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView); } @Test public void testShouldShowComplicationsFalseByDefault() { mService.onBind(new Intent()); public void testShouldShowComplicationsSetByStartDream() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); assertThat(mService.shouldShowComplications()).isFalse(); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, true /*shouldShowComplication*/); assertThat(mService.shouldShowComplications()).isTrue(); } @Test public void testShouldShowComplicationsSetByIntentExtra() { final Intent intent = new Intent(); intent.putExtra(DreamService.EXTRA_SHOW_COMPLICATIONS, true); mService.onBind(intent); public void testLowLightSetByStartDream() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); assertThat(mService.shouldShowComplications()).isTrue(); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); mMainExecutor.runAllReady(); assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); verify(mStateController).setLowLightActive(true); } @Test public void testLowLightSetByIntentExtra() throws RemoteException { final Intent intent = new Intent(); intent.putExtra(DreamService.EXTRA_DREAM_COMPONENT, LOW_LIGHT_COMPONENT); final IBinder proxy = mService.onBind(intent); public void testDestroy() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); mMainExecutor.runAllReady(); verify(mStateController).setLowLightActive(true); // Verify view added. verify(mWindowManager).addView(mViewCaptor.capture(), any()); // Service destroyed. mService.onDestroy(); mMainExecutor.runAllReady(); // Verify view removed. verify(mWindowManager).removeView(mViewCaptor.getValue()); // Verify state correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()); verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); verify(mStateController).setOverlayActive(false); verify(mStateController).setLowLightActive(false); } @Test public void testDestroy() { public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() { // Service destroyed without ever starting dream. mService.onDestroy(); mMainExecutor.runAllReady(); // Verify no view is removed. verify(mWindowManager, never()).removeView(any()); // Verify state still correctly set. verify(mKeyguardUpdateMonitor).removeCallback(any()); verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); verify(mStateController).setOverlayActive(false); Loading @@ -245,7 +281,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase { final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); // Destroy the service. mService.onDestroy(); Loading @@ -255,4 +292,44 @@ public class DreamOverlayServiceTest extends SysuiTestCase { verify(mWindowManager, never()).addView(any(), any()); } @Test public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException { final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); // Inform the overlay service of dream starting. Do not show dream complications. overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, false /*shouldShowComplication*/); mMainExecutor.runAllReady(); // Verify that a new window is added. verify(mWindowManager).addView(mViewCaptor.capture(), any()); final View windowDecorView = mViewCaptor.getValue(); // Assert that the overlay is not showing complications. assertThat(mService.shouldShowComplications()).isFalse(); clearInvocations(mDreamOverlayComponent); clearInvocations(mWindowManager); // New dream starting with dream complications showing. Note that when a new dream is // binding to the dream overlay service, it receives the same instance of IBinder as the // first one. overlay.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, true /*shouldShowComplication*/); mMainExecutor.runAllReady(); // Assert that the overlay is showing complications. assertThat(mService.shouldShowComplications()).isTrue(); // Verify that the old overlay window has been removed, and a new one created. verify(mWindowManager).removeView(windowDecorView); verify(mWindowManager).addView(any(), any()); // Verify that new instances of overlay container view controller and overlay touch monitor // are created. verify(mDreamOverlayComponent).getDreamOverlayContainerViewController(); verify(mDreamOverlayComponent).getDreamOverlayTouchMonitor(); } }