diff options
author | 2021-12-16 18:01:55 -0800 | |
---|---|---|
committer | 2021-12-22 10:23:07 -0800 | |
commit | 9710d3a2bb48999b9c5bf9d899d232117480656c (patch) | |
tree | ba2753a7ef21af651b33f79848d7ecc44a4c10f2 | |
parent | 14dca2041f060eb8531f8f45f99d9bed9b2fa85b (diff) |
Fix dragging down the notification shade in dreams.
Also includes a small refactor of the dream overlay service code.
Test: atest DreamOverlayContainerViewControllerTest DreamOverlayServiceTest
Bug: 207681309
Change-Id: Ib7aadccfdc140d54407ef78850cbff2aee0cb187
8 files changed, 355 insertions, 172 deletions
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml index b611ffa6d84e..4929f502fef0 100644 --- a/packages/SystemUI/res/layout/dream_overlay_container.xml +++ b/packages/SystemUI/res/layout/dream_overlay_container.xml @@ -32,8 +32,8 @@ android:id="@+id/dream_overlay_status_bar" android:layout_width="match_parent" android:layout_height="@dimen/dream_overlay_status_bar_height" - android:layout_marginEnd="@dimen/dream_overlay_status_bar_margin" - android:layout_marginStart="@dimen/dream_overlay_status_bar_margin" + android:paddingEnd="@dimen/dream_overlay_status_bar_margin" + android:paddingStart="@dimen/dream_overlay_status_bar_margin" app:layout_constraintTop_toTopOf="parent"> <androidx.constraintlayout.widget.ConstraintLayout diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index b7f25e81046c..c6640aa8083c 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1309,4 +1309,7 @@ <dimen name="dream_overlay_status_bar_height">80dp</dimen> <dimen name="dream_overlay_status_bar_margin">40dp</dimen> <dimen name="dream_overlay_status_icon_margin">8dp</dimen> + <!-- Height of the area at the top of the dream overlay to allow dragging down the notifications + shade. --> + <dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java new file mode 100644 index 000000000000..572bb4467c97 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2021 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.dreams; + +import android.graphics.Rect; +import android.graphics.Region; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import androidx.constraintlayout.widget.ConstraintLayout; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.systemui.R; +import com.android.systemui.dreams.dagger.DreamOverlayComponent; +import com.android.systemui.dreams.dagger.DreamOverlayModule; +import com.android.systemui.util.ViewController; + +import javax.inject.Inject; +import javax.inject.Named; + +/** + * View controller for {@link DreamOverlayContainerView}. + */ +@DreamOverlayComponent.DreamOverlayScope +public class DreamOverlayContainerViewController extends ViewController<DreamOverlayContainerView> { + // The height of the area at the top of the dream overlay to allow dragging down the + // notifications shade. + private final int mDreamOverlayNotificationsDragAreaHeight; + private final DreamOverlayStatusBarViewController mStatusBarViewController; + + // The dream overlay's content view, which is located below the status bar (in z-order) and is + // the space into which widgets are placed. + private final ViewGroup mDreamOverlayContentView; + + // A hook into the internal inset calculation where we declare the overlays as the only + // touchable regions. + private final ViewTreeObserver.OnComputeInternalInsetsListener + mOnComputeInternalInsetsListener = + new ViewTreeObserver.OnComputeInternalInsetsListener() { + @Override + public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { + inoutInfo.setTouchableInsets( + ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); + final Region region = new Region(); + final Rect rect = new Rect(); + final int childCount = mDreamOverlayContentView.getChildCount(); + for (int i = 0; i < childCount; i++) { + View child = mDreamOverlayContentView.getChildAt(i); + if (child.getGlobalVisibleRect(rect)) { + region.op(rect, Region.Op.UNION); + } + } + + // Add the notifications drag area to the tap region (otherwise the + // notifications shade can't be dragged down). + if (mDreamOverlayContentView.getGlobalVisibleRect(rect)) { + rect.bottom = rect.top + mDreamOverlayNotificationsDragAreaHeight; + region.op(rect, Region.Op.UNION); + } + + inoutInfo.touchableRegion.set(region); + } + }; + + @Inject + public DreamOverlayContainerViewController( + DreamOverlayContainerView containerView, + @Named(DreamOverlayModule.DREAM_OVERLAY_CONTENT_VIEW) ViewGroup contentView, + DreamOverlayStatusBarViewController statusBarViewController) { + super(containerView); + mDreamOverlayContentView = contentView; + mStatusBarViewController = statusBarViewController; + mDreamOverlayNotificationsDragAreaHeight = + mView.getResources().getDimensionPixelSize( + R.dimen.dream_overlay_notifications_drag_area_height); + } + + @Override + protected void onInit() { + mStatusBarViewController.init(); + } + + @Override + protected void onViewAttached() { + mView.getViewTreeObserver() + .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); + } + + @Override + protected void onViewDetached() { + mView.getViewTreeObserver() + .removeOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); + } + + void addOverlay(View overlayView, ConstraintLayout.LayoutParams layoutParams) { + mDreamOverlayContentView.addView(overlayView, layoutParams); + } + + View getContainerView() { + return mView; + } + + void removeAllOverlays() { + mDreamOverlayContentView.removeAllViews(); + } + + @VisibleForTesting + int getDreamOverlayNotificationsDragAreaHeight() { + return mDreamOverlayNotificationsDragAreaHeight; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 7c2bb4bcf4f3..a53120f15a14 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -17,13 +17,8 @@ package com.android.systemui.dreams; import android.content.Context; -import android.graphics.Rect; -import android.graphics.Region; import android.graphics.drawable.ColorDrawable; import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; @@ -54,12 +49,12 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ private final Executor mExecutor; // The state controller informs the service of updates to the complications present. private final DreamOverlayStateController mStateController; - // The component used to resolve dream overlay dependencies. - private final DreamOverlayComponent mDreamOverlayComponent; + // A controller for the dream overlay container view (which contains both the status bar and the + // content area). + private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; - // The dream overlay's content view, which is located below the status bar (in z-order) and is - // the space into which widgets are placed. - private ViewGroup mDreamOverlayContentView; + // A reference to the {@link Window} used to hold the dream overlay. + private Window mWindow; private final DreamOverlayStateController.Callback mOverlayStateCallback = new DreamOverlayStateController.Callback() { @@ -69,47 +64,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ } }; - // The service listens to view changes in order to declare that input occurring in areas outside - // the overlay should be passed through to the dream underneath. - private final View.OnAttachStateChangeListener mRootViewAttachListener = - new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(View v) { - v.getViewTreeObserver() - .addOnComputeInternalInsetsListener(mOnComputeInternalInsetsListener); - } - - @Override - public void onViewDetachedFromWindow(View v) { - v.getViewTreeObserver() - .removeOnComputeInternalInsetsListener( - mOnComputeInternalInsetsListener); - } - }; - - // A hook into the internal inset calculation where we declare the complications as the only - // touchable regions. - private final ViewTreeObserver.OnComputeInternalInsetsListener - mOnComputeInternalInsetsListener = - new ViewTreeObserver.OnComputeInternalInsetsListener() { - @Override - public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo inoutInfo) { - if (mDreamOverlayContentView != null) { - inoutInfo.setTouchableInsets( - ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); - final Region region = new Region(); - for (int i = 0; i < mDreamOverlayContentView.getChildCount(); i++) { - View child = mDreamOverlayContentView.getChildAt(i); - final Rect rect = new Rect(); - child.getGlobalVisibleRect(rect); - region.op(rect, Region.Op.UNION); - } - - inoutInfo.touchableRegion.set(region); - } - } - }; - @Inject public DreamOverlayService( Context context, @@ -119,23 +73,29 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ mContext = context; mExecutor = executor; mStateController = overlayStateController; - mDreamOverlayComponent = dreamOverlayComponentFactory.create(); + mDreamOverlayContainerViewController = + dreamOverlayComponentFactory.create().getDreamOverlayContainerViewController(); mStateController.addCallback(mOverlayStateCallback); } @Override + public void onDestroy() { + final WindowManager windowManager = mContext.getSystemService(WindowManager.class); + windowManager.removeView(mWindow.getDecorView()); + mStateController.removeCallback(mOverlayStateCallback); + super.onDestroy(); + } + + @Override public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { mExecutor.execute(() -> addOverlayWindowLocked(layoutParams)); } private void reloadComplicationsLocked() { - if (mDreamOverlayContentView == null) { - return; - } - mDreamOverlayContentView.removeAllViews(); - for (ComplicationProvider complicationProvider : mStateController.getComplications()) { - addComplication(complicationProvider); + mDreamOverlayContainerViewController.removeAllOverlays(); + for (ComplicationProvider overlayProvider : mStateController.getComplications()) { + addComplication(overlayProvider); } } @@ -147,32 +107,28 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ * into the dream window. */ private void addOverlayWindowLocked(WindowManager.LayoutParams layoutParams) { - final PhoneWindow window = new PhoneWindow(mContext); - window.setAttributes(layoutParams); - window.setWindowManager(null, layoutParams.token, "DreamOverlay", true); + mWindow = new PhoneWindow(mContext); + mWindow.setAttributes(layoutParams); + mWindow.setWindowManager(null, layoutParams.token, "DreamOverlay", true); - window.setBackgroundDrawable(new ColorDrawable(0)); + mWindow.setBackgroundDrawable(new ColorDrawable(0)); - window.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); - window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); - window.requestFeature(Window.FEATURE_NO_TITLE); + mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); + mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); + mWindow.requestFeature(Window.FEATURE_NO_TITLE); // Hide all insets when the dream is showing - window.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); - window.setDecorFitsSystemWindows(false); + mWindow.getDecorView().getWindowInsetsController().hide(WindowInsets.Type.systemBars()); + mWindow.setDecorFitsSystemWindows(false); if (DEBUG) { Log.d(TAG, "adding overlay window to dream"); } - window.setContentView(mDreamOverlayComponent.getDreamOverlayContainerView()); - - mDreamOverlayContentView = mDreamOverlayComponent.getDreamOverlayContentView(); - mDreamOverlayContentView.addOnAttachStateChangeListener(mRootViewAttachListener); - - mDreamOverlayComponent.getDreamOverlayStatusBarViewController().init(); + mDreamOverlayContainerViewController.init(); + mWindow.setContentView(mDreamOverlayContainerViewController.getContainerView()); final WindowManager windowManager = mContext.getSystemService(WindowManager.class); - windowManager.addView(window.getDecorView(), window.getAttributes()); + windowManager.addView(mWindow.getDecorView(), mWindow.getAttributes()); mExecutor.execute(this::reloadComplicationsLocked); } @@ -181,25 +137,12 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ provider.onCreateComplication(mContext, (view, layoutParams) -> { // Always move UI related work to the main thread. - mExecutor.execute(() -> { - if (mDreamOverlayContentView == null) { - return; - } - - mDreamOverlayContentView.addView(view, layoutParams); - }); + mExecutor.execute(() -> mDreamOverlayContainerViewController + .addOverlay(view, layoutParams)); }, () -> { // The Callback is set on the main thread. - mExecutor.execute(() -> { - requestExit(); - }); + mExecutor.execute(this::requestExit); }); } - - @Override - public void onDestroy() { - mStateController.removeCallback(mOverlayStateCallback); - super.onDestroy(); - } } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java index a3a446a0dbca..c90332bb9f31 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java @@ -18,10 +18,7 @@ package com.android.systemui.dreams.dagger; import static java.lang.annotation.RetentionPolicy.RUNTIME; -import android.view.ViewGroup; - -import com.android.systemui.dreams.DreamOverlayContainerView; -import com.android.systemui.dreams.DreamOverlayStatusBarViewController; +import com.android.systemui.dreams.DreamOverlayContainerViewController; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -48,15 +45,7 @@ public interface DreamOverlayComponent { @Scope @interface DreamOverlayScope {} - /** Builds a {@link DreamOverlayContainerView} */ - @DreamOverlayScope - DreamOverlayContainerView getDreamOverlayContainerView(); - - /** Builds a content view for dream overlays */ - @DreamOverlayScope - ViewGroup getDreamOverlayContentView(); - - /** Builds a {@link DreamOverlayStatusBarViewController}. */ + /** Builds a {@link DreamOverlayContainerViewController}. */ @DreamOverlayScope - DreamOverlayStatusBarViewController getDreamOverlayStatusBarViewController(); + DreamOverlayContainerViewController getDreamOverlayContainerViewController(); } diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java index d0a8fad9b1e5..5b588a9d9023 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -44,6 +44,7 @@ public abstract class DreamOverlayModule { private static final String DREAM_OVERLAY_BATTERY_VIEW = "dream_overlay_battery_view"; public static final String DREAM_OVERLAY_BATTERY_CONTROLLER = "dream_overlay_battery_controller"; + public static final String DREAM_OVERLAY_CONTENT_VIEW = "dream_overlay_content_view"; /** */ @Provides @@ -58,6 +59,7 @@ public abstract class DreamOverlayModule { /** */ @Provides @DreamOverlayComponent.DreamOverlayScope + @Named(DREAM_OVERLAY_CONTENT_VIEW) public static ViewGroup providesDreamOverlayContentView(DreamOverlayContainerView view) { return Preconditions.checkNotNull(view.findViewById(R.id.dream_overlay_content), "R.id.dream_overlay_content must not be null"); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java new file mode 100644 index 000000000000..cf53ccffcdb0 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2021 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.dreams; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.testing.AndroidTestingRunner; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; + +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { + private static final int DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT = 100; + + @Mock + Resources mResources; + + @Mock + ViewTreeObserver mViewTreeObserver; + + @Mock + DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController; + + @Mock + DreamOverlayContainerView mDreamOverlayContainerView; + + @Mock + ViewGroup mDreamOverlayContentView; + + DreamOverlayContainerViewController mController; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + + when(mResources.getDimensionPixelSize( + R.dimen.dream_overlay_notifications_drag_area_height)).thenReturn( + DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT); + when(mDreamOverlayContainerView.getResources()).thenReturn(mResources); + when(mDreamOverlayContainerView.getViewTreeObserver()).thenReturn(mViewTreeObserver); + + mController = new DreamOverlayContainerViewController( + mDreamOverlayContainerView, mDreamOverlayContentView, + mDreamOverlayStatusBarViewController); + } + + @Test + public void testDreamOverlayStatusBarViewControllerInitialized() { + mController.init(); + verify(mDreamOverlayStatusBarViewController).init(); + } + + @Test + public void testSetsDreamOverlayNotificationsDragAreaHeight() { + assertEquals( + mController.getDreamOverlayNotificationsDragAreaHeight(), + DREAM_OVERLAY_NOTIFICATIONS_DRAG_AREA_HEIGHT); + } + + @Test + public void testAddOverlayAddsOverlayToContentView() { + View overlay = new View(getContext()); + ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(100, 100); + mController.addOverlay(overlay, layoutParams); + verify(mDreamOverlayContentView).addView(overlay, layoutParams); + } + + @Test + public void testRemoveAllOverlaysRemovesOverlaysFromContentView() { + mController.removeAllOverlays(); + verify(mDreamOverlayContentView).removeAllViews(); + } + + @Test + public void testOnViewAttachedRegistersComputeInsetsListener() { + mController.onViewAttached(); + verify(mViewTreeObserver).addOnComputeInternalInsetsListener(any()); + } + + @Test + public void testOnViewDetachedUnregistersComputeInsetsListener() { + mController.onViewDetached(); + verify(mViewTreeObserver).removeOnComputeInternalInsetsListener(any()); + } + + @Test + public void testComputeInsetsListenerReturnsRegion() { + final ArgumentCaptor<ViewTreeObserver.OnComputeInternalInsetsListener> + computeInsetsListenerCapture = + ArgumentCaptor.forClass(ViewTreeObserver.OnComputeInternalInsetsListener.class); + mController.onViewAttached(); + verify(mViewTreeObserver).addOnComputeInternalInsetsListener( + computeInsetsListenerCapture.capture()); + final ViewTreeObserver.InternalInsetsInfo info = new ViewTreeObserver.InternalInsetsInfo(); + computeInsetsListenerCapture.getValue().onComputeInternalInsets(info); + assertNotNull(info.touchableRegion); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java index 5fa710f284b4..8cd8e4d7382d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -17,7 +17,6 @@ package com.android.systemui.dreams; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -55,8 +54,8 @@ import java.util.Arrays; @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayServiceTest extends SysuiTestCase { - private FakeSystemClock mFakeSystemClock = new FakeSystemClock(); - private FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); + private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); + private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); @Rule public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); @@ -80,51 +79,55 @@ public class DreamOverlayServiceTest extends SysuiTestCase { DreamOverlayStateController mDreamOverlayStateController; @Mock - DreamOverlayComponent.Factory mDreamOverlayStatusBarViewComponentFactory; + DreamOverlayComponent.Factory mDreamOverlayComponentFactory; @Mock DreamOverlayComponent mDreamOverlayComponent; @Mock - DreamOverlayStatusBarViewController mDreamOverlayStatusBarViewController; - - @Mock DreamOverlayContainerView mDreamOverlayContainerView; @Mock - ViewGroup mDreamOverlayContentView; + DreamOverlayContainerViewController mDreamOverlayContainerViewController; + + DreamOverlayService mService; @Before - public void setup() { + public void setup() throws Exception { MockitoAnnotations.initMocks(this); mContext.addMockSystemService(WindowManager.class, mWindowManager); - when(mDreamOverlayComponent.getDreamOverlayContentView()) - .thenReturn(mDreamOverlayContentView); - when(mDreamOverlayComponent.getDreamOverlayContainerView()) - .thenReturn(mDreamOverlayContainerView); - when(mDreamOverlayComponent.getDreamOverlayStatusBarViewController()) - .thenReturn(mDreamOverlayStatusBarViewController); - when(mDreamOverlayStatusBarViewComponentFactory.create()) + when(mDreamOverlayComponent.getDreamOverlayContainerViewController()) + .thenReturn(mDreamOverlayContainerViewController); + when(mDreamOverlayComponentFactory.create()) .thenReturn(mDreamOverlayComponent); + when(mDreamOverlayContainerViewController.getContainerView()) + .thenReturn(mDreamOverlayContainerView); - } - - @Test - public void testInteraction() throws Exception { - final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor, - mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory); - final IBinder proxy = service.onBind(new Intent()); + mService = new DreamOverlayService(mContext, mMainExecutor, + mDreamOverlayStateController, mDreamOverlayComponentFactory); + final IBinder proxy = mService.onBind(new Intent()); final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - clearInvocations(mWindowManager); // Inform the overlay service of dream starting. overlay.startDream(mWindowParams, mDreamOverlayCallback); mMainExecutor.runAllReady(); + } + + @Test + public void testOverlayContainerViewAddedToWindow() { verify(mWindowManager).addView(any(), any()); + } + @Test + public void testDreamOverlayContainerViewControllerInitialized() { + verify(mDreamOverlayContainerViewController).init(); + } + + @Test + public void testAddingOverlayToDream() throws Exception { // Add overlay. - service.addComplication(mProvider); + mService.addComplication(mProvider); mMainExecutor.runAllReady(); final ArgumentCaptor<ComplicationHost.CreationCallback> creationCallbackCapture = @@ -139,9 +142,26 @@ public class DreamOverlayServiceTest extends SysuiTestCase { // Inform service of overlay view creation. final View view = new View(mContext); - creationCallbackCapture.getValue().onCreated(view, new ConstraintLayout.LayoutParams( + final ConstraintLayout.LayoutParams lp = new ConstraintLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT - )); + ); + creationCallbackCapture.getValue().onCreated(view, lp); + mMainExecutor.runAllReady(); + + // Verify that DreamOverlayContainerViewController is asked to add an overlay for the view. + verify(mDreamOverlayContainerViewController).addOverlay(view, lp); + } + + @Test + public void testDreamOverlayExit() throws Exception { + // Add overlay. + mService.addComplication(mProvider); + mMainExecutor.runAllReady(); + + // Capture interaction callback from overlay creation. + final ArgumentCaptor<ComplicationHost.InteractionCallback> interactionCallbackCapture = + ArgumentCaptor.forClass(ComplicationHost.InteractionCallback.class); + verify(mProvider).onCreateComplication(any(), any(), interactionCallbackCapture.capture()); // Ask service to exit. interactionCallbackCapture.getValue().onExit(); @@ -152,17 +172,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase { } @Test - public void testListening() throws Exception { - final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor, - mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory); - - final IBinder proxy = service.onBind(new Intent()); - final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - - // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); - mMainExecutor.runAllReady(); - + public void testListenerRegisteredWithDreamOverlayStateController() { // Verify overlay service registered as listener with DreamOverlayStateController // and inform callback of addition. final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture = @@ -178,33 +188,11 @@ public class DreamOverlayServiceTest extends SysuiTestCase { } @Test - public void testDreamOverlayStatusBarViewControllerInitialized() throws Exception { - final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor, - mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory); - - final IBinder proxy = service.onBind(new Intent()); - final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - - // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); - mMainExecutor.runAllReady(); - - verify(mDreamOverlayStatusBarViewController).init(); - } - - @Test - public void testRootViewAttachListenerIsAddedToDreamOverlayContentView() throws Exception { - final DreamOverlayService service = new DreamOverlayService(mContext, mMainExecutor, - mDreamOverlayStateController, mDreamOverlayStatusBarViewComponentFactory); - - final IBinder proxy = service.onBind(new Intent()); - final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - - // Inform the overlay service of dream starting. - overlay.startDream(mWindowParams, mDreamOverlayCallback); - mMainExecutor.runAllReady(); - - verify(mDreamOverlayContentView).addOnAttachStateChangeListener( - any(View.OnAttachStateChangeListener.class)); + public void testOnDestroyRemovesOverlayStateCallback() { + final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture = + ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class); + verify(mDreamOverlayStateController).addCallback(callbackCapture.capture()); + mService.onDestroy(); + verify(mDreamOverlayStateController).removeCallback(callbackCapture.getValue()); } } |