Do not allow complications when dream overlay is not enabled.

Complications are currently only hosted by the dream overlay. Dependent
logic that listens on the DreamOverlayStateController for changes around
complications should not be triggered when the dream overlay is not
enabled. This changelist addresses this by preventing the
DreamOverlayStateController from accepting complications when the
overlay is not enabled (component wise).

This change also null checks a complication view retrieved from the
view holder before adding it to the layout.

Test: atest DreamOverlayStateControllerTest
Test: atest ComplicationHostViewControllerTest
Fixes: 259859096
Change-Id: Ifa1b4a9da0070a949e422003d3fd4e9ca484a16e
This commit is contained in:
Bryce Lee
2022-12-09 16:30:23 -08:00
parent 2e5bc09147
commit 225ce63f0b
6 changed files with 112 additions and 15 deletions

View File

@@ -16,6 +16,8 @@
package com.android.systemui.dreams;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_SERVICE_COMPONENT;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -35,6 +37,7 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.dagger.qualifiers.Main;
import javax.inject.Inject;
import javax.inject.Named;
/**
* {@link DreamOverlayRegistrant} is responsible for telling system server that SystemUI should be
@@ -98,12 +101,13 @@ public class DreamOverlayRegistrant implements CoreStartable {
}
@Inject
public DreamOverlayRegistrant(Context context, @Main Resources resources) {
public DreamOverlayRegistrant(Context context, @Main Resources resources,
@Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName dreamOverlayServiceComponent) {
mContext = context;
mResources = resources;
mDreamManager = IDreamManager.Stub.asInterface(
ServiceManager.getService(DreamService.DREAM_SERVICE));
mOverlayServiceComponent = new ComponentName(mContext, DreamOverlayService.class);
mOverlayServiceComponent = dreamOverlayServiceComponent;
}
@Override

View File

@@ -16,6 +16,8 @@
package com.android.systemui.dreams;
import static com.android.systemui.dreams.dagger.DreamModule.DREAM_OVERLAY_ENABLED;
import android.service.dreams.DreamService;
import android.util.Log;
@@ -37,6 +39,7 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Named;
/**
* {@link DreamOverlayStateController} is the source of truth for Dream overlay configurations and
@@ -83,6 +86,7 @@ public class DreamOverlayStateController implements
}
private final Executor mExecutor;
private final boolean mOverlayEnabled;
private final ArrayList<Callback> mCallbacks = new ArrayList<>();
@Complication.ComplicationType
@@ -94,14 +98,27 @@ public class DreamOverlayStateController implements
@VisibleForTesting
@Inject
public DreamOverlayStateController(@Main Executor executor) {
public DreamOverlayStateController(@Main Executor executor,
@Named(DREAM_OVERLAY_ENABLED) boolean overlayEnabled) {
mExecutor = executor;
mOverlayEnabled = overlayEnabled;
if (DEBUG) {
Log.d(TAG, "Dream overlay enabled:" + mOverlayEnabled);
}
}
/**
* Adds a complication to be included on the dream overlay.
*/
public void addComplication(Complication complication) {
if (!mOverlayEnabled) {
if (DEBUG) {
Log.d(TAG,
"Ignoring adding complication due to overlay disabled:" + complication);
}
return;
}
mExecutor.execute(() -> {
if (mComplications.add(complication)) {
if (DEBUG) {
@@ -116,6 +133,14 @@ public class DreamOverlayStateController implements
* Removes a complication from inclusion on the dream overlay.
*/
public void removeComplication(Complication complication) {
if (!mOverlayEnabled) {
if (DEBUG) {
Log.d(TAG,
"Ignoring removing complication due to overlay disabled:" + complication);
}
return;
}
mExecutor.execute(() -> {
if (mComplications.remove(complication)) {
if (DEBUG) {
@@ -193,7 +218,7 @@ public class DreamOverlayStateController implements
* @return {@code true} if overlay is active, {@code false} otherwise.
*/
public boolean isOverlayActive() {
return containsState(STATE_DREAM_OVERLAY_ACTIVE);
return mOverlayEnabled && containsState(STATE_DREAM_OVERLAY_ACTIVE);
}
/**

View File

@@ -138,19 +138,27 @@ public class ComplicationHostViewController extends ViewController<ConstraintLay
final ComplicationId id = complication.getId();
final Complication.ViewHolder viewHolder = complication.getComplication()
.createView(complication);
final View view = viewHolder.getView();
if (view == null) {
Log.e(TAG, "invalid complication view. null view supplied by ViewHolder");
return;
}
// Complications to be added before dream entry animations are finished are set
// to invisible and are animated in.
if (!mEntryAnimationsFinished) {
viewHolder.getView().setVisibility(View.INVISIBLE);
view.setVisibility(View.INVISIBLE);
}
mComplications.put(id, viewHolder);
if (viewHolder.getView().getParent() != null) {
if (view.getParent() != null) {
Log.e(TAG, "View for complication "
+ complication.getComplication().getClass()
+ " already has a parent. Make sure not to reuse complication "
+ "views!");
}
mLayoutEngine.addComplication(id, viewHolder.getView(),
mLayoutEngine.addComplication(id, view,
viewHolder.getLayoutParams(), viewHolder.getCategory());
});
}

View File

@@ -16,7 +16,9 @@
package com.android.systemui.dreams.dagger;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import com.android.dream.lowlight.dagger.LowLightDreamModule;
@@ -24,6 +26,7 @@ import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.DreamOverlayNotificationCountProvider;
import com.android.systemui.dreams.DreamOverlayService;
import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
import java.util.Optional;
@@ -45,9 +48,34 @@ import dagger.Provides;
})
public interface DreamModule {
String DREAM_ONLY_ENABLED_FOR_DOCK_USER = "dream_only_enabled_for_dock_user";
String DREAM_OVERLAY_SERVICE_COMPONENT = "dream_overlay_service_component";
String DREAM_OVERLAY_ENABLED = "dream_overlay_enabled";
String DREAM_SUPPORTED = "dream_supported";
/**
* Provides the dream component
*/
@Provides
@Named(DREAM_OVERLAY_SERVICE_COMPONENT)
static ComponentName providesDreamOverlayService(Context context) {
return new ComponentName(context, DreamOverlayService.class);
}
/**
* Provides whether dream overlay is enabled.
*/
@Provides
@Named(DREAM_OVERLAY_ENABLED)
static Boolean providesDreamOverlayEnabled(PackageManager packageManager,
@Named(DREAM_OVERLAY_SERVICE_COMPONENT) ComponentName component) {
try {
return packageManager.getServiceInfo(component, PackageManager.GET_META_DATA).enabled;
} catch (PackageManager.NameNotFoundException e) {
return false;
}
}
/**
* Provides an instance of the dream backend.
*/

View File

@@ -63,7 +63,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Test
public void testStateChange_overlayActive() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
mExecutor, true);
stateController.addCallback(mCallback);
stateController.setOverlayActive(true);
mExecutor.runAllReady();
@@ -85,7 +85,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Test
public void testCallback() {
final DreamOverlayStateController stateController = new DreamOverlayStateController(
mExecutor);
mExecutor, true);
stateController.addCallback(mCallback);
// Add complication and verify callback is notified.
@@ -111,7 +111,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Test
public void testNotifyOnCallbackAdd() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
new DreamOverlayStateController(mExecutor, true);
stateController.addComplication(mComplication);
mExecutor.runAllReady();
@@ -122,10 +122,25 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
verify(mCallback, times(1)).onComplicationsChanged();
}
@Test
public void testNotifyOnCallbackAddOverlayDisabled() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor, false);
stateController.addComplication(mComplication);
mExecutor.runAllReady();
// Verify callback occurs on add when an overlay is already present.
stateController.addCallback(mCallback);
mExecutor.runAllReady();
verify(mCallback, never()).onComplicationsChanged();
}
@Test
public void testComplicationFilteringWhenShouldShowComplications() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
new DreamOverlayStateController(mExecutor, true);
stateController.setShouldShowComplications(true);
final Complication alwaysAvailableComplication = Mockito.mock(Complication.class);
@@ -165,7 +180,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Test
public void testComplicationFilteringWhenShouldHideComplications() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
new DreamOverlayStateController(mExecutor, true);
stateController.setShouldShowComplications(true);
final Complication alwaysAvailableComplication = Mockito.mock(Complication.class);
@@ -212,7 +227,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
public void testComplicationWithNoTypeNotFiltered() {
final Complication complication = Mockito.mock(Complication.class);
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
new DreamOverlayStateController(mExecutor, true);
stateController.addComplication(complication);
mExecutor.runAllReady();
assertThat(stateController.getComplications(true).contains(complication))
@@ -222,7 +237,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Test
public void testNotifyLowLightChanged() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
new DreamOverlayStateController(mExecutor, true);
stateController.addCallback(mCallback);
mExecutor.runAllReady();
@@ -238,7 +253,7 @@ public class DreamOverlayStateControllerTest extends SysuiTestCase {
@Test
public void testNotifyEntryAnimationsFinishedChanged() {
final DreamOverlayStateController stateController =
new DreamOverlayStateController(mExecutor);
new DreamOverlayStateController(mExecutor, true);
stateController.addCallback(mCallback);
mExecutor.runAllReady();

View File

@@ -15,6 +15,8 @@
*/
package com.android.systemui.dreams.complication;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
@@ -138,6 +140,21 @@ public class ComplicationHostViewControllerTest extends SysuiTestCase {
verify(mLayoutEngine).removeComplication(eq(mComplicationId));
}
@Test
public void testMalformedComplicationAddition() {
final Observer<Collection<ComplicationViewModel>> observer =
captureComplicationViewModelsObserver();
// Add a complication and ensure it is added to the view.
final HashSet<ComplicationViewModel> complications = new HashSet<>(
Collections.singletonList(mComplicationViewModel));
when(mViewHolder.getView()).thenReturn(null);
observer.onChanged(complications);
verify(mLayoutEngine, never()).addComplication(any(), any(), any(), anyInt());
}
@Test
public void testNewComplicationsBeforeEntryAnimationsFinishSetToInvisible() {
final Observer<Collection<ComplicationViewModel>> observer =