Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eabe3bf0 authored by Darrell Shi's avatar Darrell Shi Committed by Android (Google) Code Review
Browse files

Merge "Redraw dream overlay when a new dream is connected." into tm-qpr-dev

parents 2689585c 51afda7a
Loading
Loading
Loading
Loading
+4 −5
Original line number Diff line number Diff line
@@ -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);
        }
    };
@@ -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();
    }

+12 −22
Original line number Diff line number Diff line
@@ -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.
     *
@@ -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;

@@ -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();
    }

@@ -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,
@@ -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);
                                }
+6 −2
Original line number Diff line number Diff line
@@ -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);
}
+54 −29
Original line number Diff line number Diff line
@@ -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 =
@@ -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 {
@@ -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,
@@ -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) {
@@ -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;
        });
    }

@@ -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() {
@@ -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;
    }
}
+102 −25
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;

@@ -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);

@@ -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);
@@ -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,
@@ -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);
@@ -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());
@@ -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();
@@ -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);
@@ -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();
@@ -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();
    }
}