From 2704e9f6de53c7284949924ffd0a35fb7b5d8213 Mon Sep 17 00:00:00 2001 From: Darrell Shi Date: Thu, 10 Feb 2022 23:31:28 +0000 Subject: [PATCH] Implement ComplicationTypesUpdater. The ComplicationTypesUpdater observes user settings for complication types shown on the dream overlay, and pushes updates to the DreamOverlayStateController. Test: atest ComplicationTypesUpdaterTest Test: locally on device by observing the complications update during dream while changing complication types through adb command: adb shell settings put secure screensaver_enabled_complications [1,2,3..] Fix: 217800355 Change-Id: I2d1e95c42a8092f79ef8db4128e970db96732509 --- .../systemui/dreams/DreamOverlayService.java | 7 -- .../ComplicationTypesUpdater.java | 85 +++++++++++++ .../systemui/dreams/dagger/DreamModule.java | 11 ++ .../dreams/dagger/DreamOverlayComponent.java | 4 - .../dreams/dagger/DreamOverlayModule.java | 8 -- .../dreams/DreamOverlayServiceTest.java | 31 ----- .../ComplicationTypesUpdaterTest.java | 113 ++++++++++++++++++ 7 files changed, 209 insertions(+), 50 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java index 4696eed9b3d7e..4dacf654496cb 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java @@ -31,10 +31,8 @@ import androidx.lifecycle.ViewModelStore; import com.android.internal.policy.PhoneWindow; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; -import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.Complication; -import com.android.systemui.dreams.complication.ComplicationUtils; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; @@ -59,7 +57,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ // content area). private final DreamOverlayContainerViewController mDreamOverlayContainerViewController; private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - private final DreamBackend mDreamBackend; // A reference to the {@link Window} used to hold the dream overlay. private Window mWindow; @@ -112,7 +109,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ setCurrentState(Lifecycle.State.CREATED); mLifecycleRegistry = component.getLifecycleRegistry(); mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor(); - mDreamBackend = component.getDreamBackend(); mDreamOverlayTouchMonitor.init(); } @@ -136,9 +132,6 @@ public class DreamOverlayService extends android.service.dreams.DreamOverlayServ public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) { setCurrentState(Lifecycle.State.STARTED); mExecutor.execute(() -> { - mStateController.setAvailableComplicationTypes( - ComplicationUtils.convertComplicationTypes( - mDreamBackend.getEnabledComplications())); addOverlayWindowLocked(layoutParams); setCurrentState(Lifecycle.State.RESUMED); mStateController.setOverlayActive(true); diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java new file mode 100644 index 0000000000000..83249aa324d1b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationTypesUpdater.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 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.complication; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.UserHandle; +import android.provider.Settings; + +import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.CoreStartable; +import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.util.settings.SecureSettings; + +import java.util.concurrent.Executor; + +import javax.inject.Inject; + +/** + * {@link ComplicationTypesUpdater} observes the state of available complication types set by the + * user, and pushes updates to {@link DreamOverlayStateController}. + */ +@SysUISingleton +public class ComplicationTypesUpdater extends CoreStartable { + private final DreamBackend mDreamBackend; + private final Executor mExecutor; + private final SecureSettings mSecureSettings; + + private final DreamOverlayStateController mDreamOverlayStateController; + + @Inject + ComplicationTypesUpdater(Context context, + DreamBackend dreamBackend, + @Main Executor executor, + SecureSettings secureSettings, + DreamOverlayStateController dreamOverlayStateController) { + super(context); + + mDreamBackend = dreamBackend; + mExecutor = executor; + mSecureSettings = secureSettings; + mDreamOverlayStateController = dreamOverlayStateController; + } + + @Override + public void start() { + final ContentObserver settingsObserver = new ContentObserver(null /*handler*/) { + @Override + public void onChange(boolean selfChange) { + mExecutor.execute(() -> mDreamOverlayStateController.setAvailableComplicationTypes( + getAvailableComplicationTypes())); + } + }; + + mSecureSettings.registerContentObserverForUser( + Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS, + settingsObserver, + UserHandle.myUserId()); + settingsObserver.onChange(false); + } + + /** + * Returns complication types that are currently available by user setting. + */ + @Complication.ComplicationType + private int getAvailableComplicationTypes() { + return ComplicationUtils.convertComplicationTypes(mDreamBackend.getEnabledComplications()); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java index d8af9e5f1f4af..c61f7968c5955 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java @@ -16,10 +16,14 @@ package com.android.systemui.dreams.dagger; +import android.content.Context; + +import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule; import com.android.systemui.dreams.touch.dagger.DreamTouchModule; import dagger.Module; +import dagger.Provides; /** * Dagger Module providing Communal-related functionality. @@ -32,4 +36,11 @@ import dagger.Module; DreamOverlayComponent.class, }) public interface DreamModule { + /** + * Provides an instance of the dream backend. + */ + @Provides + static DreamBackend providesDreamBackend(Context context) { + return DreamBackend.getInstance(context); + } } \ No newline at end of file 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 60278a9da7eba..05ab9015feccc 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java @@ -22,7 +22,6 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import androidx.lifecycle.ViewModelStore; -import com.android.settingslib.dream.DreamBackend; import com.android.systemui.dreams.DreamOverlayContainerViewController; import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.complication.dagger.ComplicationModule; @@ -69,7 +68,4 @@ public interface DreamOverlayComponent { /** Builds a {@link DreamOverlayTouchMonitor} */ DreamOverlayTouchMonitor getDreamOverlayTouchMonitor(); - - /** Builds a ${@link DreamBackend} */ - DreamBackend getDreamBackend(); } 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 efa063f4dfc87..4eb5cb97607a0 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java @@ -17,7 +17,6 @@ package com.android.systemui.dreams.dagger; import android.content.ContentResolver; -import android.content.Context; import android.content.res.Resources; import android.os.Handler; import android.view.LayoutInflater; @@ -28,7 +27,6 @@ import androidx.lifecycle.LifecycleOwner; import androidx.lifecycle.LifecycleRegistry; import com.android.internal.util.Preconditions; -import com.android.settingslib.dream.DreamBackend; import com.android.systemui.R; import com.android.systemui.battery.BatteryMeterView; import com.android.systemui.battery.BatteryMeterViewController; @@ -149,10 +147,4 @@ public abstract class DreamOverlayModule { static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) { return lifecycleOwner.getLifecycle(); } - - @Provides - @DreamOverlayComponent.DreamOverlayScope - static DreamBackend providesDreamBackend(Context context) { - return DreamBackend.getInstance(context); - } } 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 529a16319cb55..58ffbfa763281 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java @@ -37,9 +37,7 @@ import androidx.lifecycle.LifecycleRegistry; import androidx.test.filters.SmallTest; import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.settingslib.dream.DreamBackend; import com.android.systemui.SysuiTestCase; -import com.android.systemui.dreams.complication.Complication; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor; import com.android.systemui.util.concurrency.FakeExecutor; @@ -53,10 +51,6 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Set; - @SmallTest @RunWith(AndroidTestingRunner.class) public class DreamOverlayServiceTest extends SysuiTestCase { @@ -101,9 +95,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase { @Mock DreamOverlayStateController mStateController; - @Mock - DreamBackend mDreamBackend; - DreamOverlayService mService; @Before @@ -119,8 +110,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase { .thenReturn(mLifecycleRegistry); when(mDreamOverlayComponent.getDreamOverlayTouchMonitor()) .thenReturn(mDreamOverlayTouchMonitor); - when(mDreamOverlayComponent.getDreamBackend()) - .thenReturn(mDreamBackend); when(mDreamOverlayComponentFactory .create(any(), any())) .thenReturn(mDreamOverlayComponent); @@ -173,26 +162,6 @@ public class DreamOverlayServiceTest extends SysuiTestCase { assertThat(mService.shouldShowComplications()).isFalse(); } - @Test - public void testSetAvailableComplicationTypes() throws Exception { - final Set enabledComplications = new HashSet<>( - Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME, - DreamBackend.COMPLICATION_TYPE_DATE, - DreamBackend.COMPLICATION_TYPE_WEATHER)); - when(mDreamBackend.getEnabledComplications()).thenReturn(enabledComplications); - - final IBinder proxy = mService.onBind(new Intent()); - final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - - overlay.startDream(mWindowParams, mDreamOverlayCallback); - mMainExecutor.runAllReady(); - - final int expectedTypes = - Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE - | Complication.COMPLICATION_TYPE_WEATHER; - verify(mStateController).setAvailableComplicationTypes(expectedTypes); - } - @Test public void testDestroy() { mService.onDestroy(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java new file mode 100644 index 0000000000000..09976e0e61923 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationTypesUpdaterTest.java @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2022 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.complication; + +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.database.ContentObserver; +import android.os.UserHandle; +import android.provider.Settings; +import android.testing.AndroidTestingRunner; + +import androidx.test.filters.SmallTest; + +import com.android.settingslib.dream.DreamBackend; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.util.concurrency.FakeExecutor; +import com.android.systemui.util.settings.SecureSettings; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +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; + +import java.util.Arrays; +import java.util.HashSet; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class ComplicationTypesUpdaterTest extends SysuiTestCase { + @Mock + private Context mContext; + @Mock + private DreamBackend mDreamBackend; + @Mock + private SecureSettings mSecureSettings; + @Mock + private DreamOverlayStateController mDreamOverlayStateController; + @Captor + private ArgumentCaptor mSettingsObserverCaptor; + + private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + + private ComplicationTypesUpdater mController; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>()); + + mController = new ComplicationTypesUpdater(mContext, mDreamBackend, mExecutor, + mSecureSettings, mDreamOverlayStateController); + } + + @Test + public void testPushUpdateToDreamOverlayStateControllerImmediatelyOnStart() { + // DreamOverlayStateController shouldn't be updated before start(). + verify(mDreamOverlayStateController, never()).setAvailableComplicationTypes(anyInt()); + + mController.start(); + mExecutor.runAllReady(); + + // DreamOverlayStateController updated immediately on start(). + verify(mDreamOverlayStateController).setAvailableComplicationTypes(anyInt()); + } + + @Test + public void testPushUpdateToDreamOverlayStateControllerOnChange() { + mController.start(); + mExecutor.runAllReady(); + + when(mDreamBackend.getEnabledComplications()).thenReturn(new HashSet<>(Arrays.asList( + DreamBackend.COMPLICATION_TYPE_TIME, DreamBackend.COMPLICATION_TYPE_WEATHER, + DreamBackend.COMPLICATION_TYPE_AIR_QUALITY))); + final ContentObserver settingsObserver = captureSettingsObserver(); + settingsObserver.onChange(false); + mExecutor.runAllReady(); + + verify(mDreamOverlayStateController).setAvailableComplicationTypes( + Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_WEATHER + | Complication.COMPLICATION_TYPE_AIR_QUALITY); + } + + private ContentObserver captureSettingsObserver() { + verify(mSecureSettings).registerContentObserverForUser( + eq(Settings.Secure.SCREENSAVER_ENABLED_COMPLICATIONS), + mSettingsObserverCaptor.capture(), eq(UserHandle.myUserId())); + return mSettingsObserverCaptor.getValue(); + } +}