Hide the status bar dream overlay when low light is active.

We pipe the component name of the currently active dream into the
overlay service, and when the active dream is lowlight, we hide the
status bar.

Test: atest DreamOverlayStateControllerTest
Test: atest DreamOverlayStatusBarViewControllerTest
Test: manually on device by entering/exiting low light and ensuring the
status bar is hidden
Fixes: 245386662

Change-Id: I22973538bea19ef108360a319c70c878cfd81800
This commit is contained in:
Lucas Silva
2022-09-20 23:32:02 -04:00
parent 591f54ba80
commit 0bfb405bd3
10 changed files with 151 additions and 7 deletions

View File

@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
@@ -36,6 +37,7 @@ public abstract class DreamOverlayService extends Service {
private static final String TAG = "DreamOverlayService";
private static final boolean DEBUG = false;
private boolean mShowComplications;
private ComponentName mDreamComponent;
private IDreamOverlay mDreamOverlay = new IDreamOverlay.Stub() {
@Override
@@ -56,6 +58,8 @@ public abstract class DreamOverlayService extends Service {
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();
}
@@ -84,4 +88,12 @@ public abstract class DreamOverlayService extends Service {
public final boolean shouldShowComplications() {
return mShowComplications;
}
/**
* Returns the active dream component.
* @hide
*/
public final ComponentName getDreamComponent() {
return mDreamComponent;
}
}

View File

@@ -217,6 +217,12 @@ public class DreamService extends Service implements Window.Callback {
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.
* @hide
@@ -271,6 +277,7 @@ public class DreamService extends Service implements Window.Callback {
overlayIntent.setComponent(overlayService);
overlayIntent.putExtra(EXTRA_SHOW_COMPLICATIONS,
fetchShouldShowComplications(context, serviceInfo));
overlayIntent.putExtra(EXTRA_DREAM_COMPONENT, dreamService);
context.bindService(overlayIntent,
this, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE);

View File

@@ -124,6 +124,7 @@ android_library {
"dagger2",
"jsr330",
"lottie",
"LowLightDreamLib",
],
manifest: "AndroidManifest.xml",
@@ -227,6 +228,7 @@ android_library {
"dagger2",
"jsr330",
"WindowManager-Shell",
"LowLightDreamLib",
],
libs: [
"android.test.runner",

View File

@@ -16,6 +16,7 @@
package com.android.systemui.dreams;
import android.content.ComponentName;
import android.content.Context;
import android.graphics.drawable.ColorDrawable;
import android.util.Log;
@@ -26,11 +27,13 @@ import android.view.WindowInsets;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ViewModelStore;
import com.android.dream.lowlight.dagger.LowLightDreamModule;
import com.android.internal.logging.UiEvent;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.policy.PhoneWindow;
@@ -44,6 +47,7 @@ import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import javax.inject.Named;
/**
* The {@link DreamOverlayService} is responsible for placing an overlay on top of a dream. The
@@ -62,6 +66,8 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
// content area).
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Nullable
private final ComponentName mLowLightDreamComponent;
private final UiEventLogger mUiEventLogger;
// A reference to the {@link Window} used to hold the dream overlay.
@@ -125,10 +131,13 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
DreamOverlayComponent.Factory dreamOverlayComponentFactory,
DreamOverlayStateController stateController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
UiEventLogger uiEventLogger) {
UiEventLogger uiEventLogger,
@Nullable @Named(LowLightDreamModule.LOW_LIGHT_DREAM_COMPONENT)
ComponentName lowLightDreamComponent) {
mContext = context;
mExecutor = executor;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mLowLightDreamComponent = lowLightDreamComponent;
mKeyguardUpdateMonitor.registerCallback(mKeyguardCallback);
mStateController = stateController;
mUiEventLogger = uiEventLogger;
@@ -155,6 +164,7 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
windowManager.removeView(mWindow.getDecorView());
}
mStateController.setOverlayActive(false);
mStateController.setLowLightActive(false);
mDestroyed = true;
super.onDestroy();
}
@@ -163,6 +173,9 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ
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(() -> {
if (mDestroyed) {
// The task could still be executed after the service has been destroyed. Bail if

View File

@@ -50,6 +50,7 @@ public class DreamOverlayStateController implements
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
public static final int STATE_DREAM_OVERLAY_ACTIVE = 1 << 0;
public static final int STATE_LOW_LIGHT_ACTIVE = 1 << 1;
private static final int OP_CLEAR_STATE = 1;
private static final int OP_SET_STATE = 2;
@@ -193,6 +194,14 @@ public class DreamOverlayStateController implements
return containsState(STATE_DREAM_OVERLAY_ACTIVE);
}
/**
* Returns whether low light mode is active.
* @return {@code true} if in low light mode, {@code false} otherwise.
*/
public boolean isLowLightActive() {
return containsState(STATE_LOW_LIGHT_ACTIVE);
}
private boolean containsState(int state) {
return (mState & state) != 0;
}
@@ -221,6 +230,14 @@ public class DreamOverlayStateController implements
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_DREAM_OVERLAY_ACTIVE);
}
/**
* Sets whether low light mode is active.
* @param active {@code true} if low light mode is active, {@code false} otherwise.
*/
public void setLowLightActive(boolean active) {
modifyState(active ? OP_SET_STATE : OP_CLEAR_STATE, STATE_LOW_LIGHT_ACTIVE);
}
/**
* Returns the available complication types.
*/

View File

@@ -20,7 +20,6 @@ import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDING;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.StatusBarManager;
import android.content.res.Resources;
@@ -36,6 +35,8 @@ import android.text.format.DateFormat;
import android.util.PluralsMessageFormatter;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayStatusBarItemsProvider.StatusBarItem;
@@ -73,6 +74,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private final Optional<DreamOverlayNotificationCountProvider>
mDreamOverlayNotificationCountProvider;
private final ZenModeController mZenModeController;
private final DreamOverlayStateController mDreamOverlayStateController;
private final StatusBarWindowStateController mStatusBarWindowStateController;
private final DreamOverlayStatusBarItemsProvider mStatusBarItemsProvider;
private final Executor mMainExecutor;
private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems =
@@ -102,6 +105,14 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
}
};
private final DreamOverlayStateController.Callback mDreamOverlayStateCallback =
new DreamOverlayStateController.Callback() {
@Override
public void onStateChanged() {
updateLowLightState();
}
};
private final IndividualSensorPrivacyController.Callback mSensorCallback =
(sensor, blocked) -> updateMicCameraBlockedStatusIcon();
@@ -140,7 +151,8 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
Optional<DreamOverlayNotificationCountProvider> dreamOverlayNotificationCountProvider,
ZenModeController zenModeController,
StatusBarWindowStateController statusBarWindowStateController,
DreamOverlayStatusBarItemsProvider statusBarItemsProvider) {
DreamOverlayStatusBarItemsProvider statusBarItemsProvider,
DreamOverlayStateController dreamOverlayStateController) {
super(view);
mResources = resources;
mMainExecutor = mainExecutor;
@@ -151,8 +163,10 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mDateFormatUtil = dateFormatUtil;
mSensorPrivacyController = sensorPrivacyController;
mDreamOverlayNotificationCountProvider = dreamOverlayNotificationCountProvider;
mStatusBarWindowStateController = statusBarWindowStateController;
mStatusBarItemsProvider = statusBarItemsProvider;
mZenModeController = zenModeController;
mDreamOverlayStateController = dreamOverlayStateController;
// Register to receive show/hide updates for the system status bar. Our custom status bar
// needs to hide when the system status bar is showing to ovoid overlapping status bars.
@@ -180,6 +194,9 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
updateLowLightState();
mTouchInsetSession.addViewToTracking(mView);
}
@@ -193,6 +210,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
provider -> provider.removeCallback(mNotificationCountCallback));
mStatusBarItemsProvider.removeCallback(mStatusBarItemsProviderCallback);
mView.removeAllExtraStatusBarItemViews();
mDreamOverlayStateController.removeCallback(mDreamOverlayStateCallback);
mTouchInsetSession.clear();
mIsAttached = false;
@@ -217,6 +235,15 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
hasAlarm ? buildAlarmContentDescription(alarm) : null);
}
private void updateLowLightState() {
int visibility = View.VISIBLE;
if (mDreamOverlayStateController.isLowLightActive()
|| mStatusBarWindowStateController.windowIsShowing()) {
visibility = View.INVISIBLE;
}
mView.setVisibility(visibility);
}
private String buildAlarmContentDescription(AlarmManager.AlarmClockInfo alarm) {
final String skeleton = mDateFormatUtil.is24HourFormat() ? "EHm" : "Ehma";
final String pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton);
@@ -272,7 +299,7 @@ public class DreamOverlayStatusBarViewController extends ViewController<DreamOve
private void onSystemStatusBarStateChanged(@StatusBarManager.WindowVisibleState int state) {
mMainExecutor.execute(() -> {
if (!mIsAttached) {
if (!mIsAttached || mDreamOverlayStateController.isLowLightActive()) {
return;
}

View File

@@ -19,6 +19,7 @@ package com.android.systemui.dreams.dagger;
import android.content.Context;
import android.content.res.Resources;
import com.android.dream.lowlight.dagger.LowLightDreamModule;
import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -37,6 +38,7 @@ import dagger.Provides;
*/
@Module(includes = {
RegisteredComplicationsModule.class,
LowLightDreamModule.class,
},
subcomponents = {
DreamOverlayComponent.class,

View File

@@ -23,8 +23,10 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
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;
@@ -57,6 +59,8 @@ import org.mockito.MockitoAnnotations;
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DreamOverlayServiceTest extends SysuiTestCase {
private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package",
"lowlight");
private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock);
@@ -129,7 +133,8 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
mDreamOverlayComponentFactory,
mStateController,
mKeyguardUpdateMonitor,
mUiEventLogger);
mUiEventLogger,
LOW_LIGHT_COMPONENT);
}
@Test
@@ -203,6 +208,22 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
assertThat(mService.shouldShowComplications()).isTrue();
}
@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);
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);
mMainExecutor.runAllReady();
verify(mStateController).setLowLightActive(true);
}
@Test
public void testDestroy() {
mService.onDestroy();
@@ -211,6 +232,7 @@ public class DreamOverlayServiceTest extends SysuiTestCase {
verify(mKeyguardUpdateMonitor).removeCallback(any());
verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED);
verify(mStateController).setOverlayActive(false);
verify(mStateController).setLowLightActive(false);
}
@Test

View File

@@ -218,4 +218,20 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
assertThat(stateController.getComplications(true).contains(complication))
.isTrue();
}
@Test
public void testNotifyLowLightChanged() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
stateController.addCallback(mCallback);
mExecutor.runAllReady();
assertThat(stateController.isLowLightActive()).isFalse();
stateController.setLowLightActive(true);
mExecutor.runAllReady();
verify(mCallback, times(1)).onStateChanged();
assertThat(stateController.isLowLightActive()).isTrue();
}
}

View File

@@ -24,6 +24,7 @@ import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -101,6 +102,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
DreamOverlayStatusBarItemsProvider.StatusBarItem mStatusBarItem;
@Mock
View mStatusBarItemView;
@Mock
DreamOverlayStateController mDreamOverlayStateController;
private final Executor mMainExecutor = Runnable::run;
@@ -126,7 +129,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
Optional.of(mDreamOverlayNotificationCountProvider),
mZenModeController,
mStatusBarWindowStateController,
mDreamOverlayStatusBarItemsProvider);
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController);
}
@Test
@@ -137,6 +141,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mZenModeController).addCallback(any());
verify(mDreamOverlayNotificationCountProvider).addCallback(any());
verify(mDreamOverlayStatusBarItemsProvider).addCallback(any());
verify(mDreamOverlayStateController).addCallback(any());
}
@Test
@@ -266,7 +271,8 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
Optional.empty(),
mZenModeController,
mStatusBarWindowStateController,
mDreamOverlayStatusBarItemsProvider);
mDreamOverlayStatusBarItemsProvider,
mDreamOverlayStateController);
controller.onViewAttached();
verify(mView, never()).showIcon(
eq(DreamOverlayStatusBarView.STATUS_ICON_NOTIFICATIONS), eq(true), any());
@@ -305,6 +311,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mZenModeController).removeCallback(any());
verify(mDreamOverlayNotificationCountProvider).removeCallback(any());
verify(mDreamOverlayStatusBarItemsProvider).removeCallback(any());
verify(mDreamOverlayStateController).removeCallback(any());
}
@Test
@@ -458,6 +465,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
@Test
public void testStatusBarShownWhenSystemStatusBarHidden() {
mController.onViewAttached();
reset(mView);
final ArgumentCaptor<StatusBarWindowStateListener>
callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
@@ -471,6 +479,7 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
public void testUnattachedStatusBarVisibilityUnchangedWhenSystemStatusBarHidden() {
mController.onViewAttached();
mController.onViewDetached();
reset(mView);
final ArgumentCaptor<StatusBarWindowStateListener>
callbackCapture = ArgumentCaptor.forClass(StatusBarWindowStateListener.class);
@@ -493,4 +502,21 @@ public class DreamOverlayStatusBarViewControllerTest extends SysuiTestCase {
verify(mView).setExtraStatusBarItemViews(List.of(mStatusBarItemView));
}
@Test
public void testLowLightHidesStatusBar() {
when(mDreamOverlayStateController.isLowLightActive()).thenReturn(true);
mController.onViewAttached();
verify(mView).setVisibility(View.INVISIBLE);
reset(mView);
when(mDreamOverlayStateController.isLowLightActive()).thenReturn(false);
final ArgumentCaptor<DreamOverlayStateController.Callback> callbackCapture =
ArgumentCaptor.forClass(DreamOverlayStateController.Callback.class);
verify(mDreamOverlayStateController).addCallback(callbackCapture.capture());
callbackCapture.getValue().onStateChanged();
verify(mView).setVisibility(View.VISIBLE);
}
}