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
This commit is contained in:
Darrell Shi
2022-02-10 23:31:28 +00:00
parent dd0811a649
commit 2704e9f6de
7 changed files with 209 additions and 50 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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