Merge "Add unfiltered access to the dream smartspace targets, in order to extract weather data." into tm-qpr-dev

This commit is contained in:
Lucas Silva
2022-06-13 21:52:06 +00:00
committed by Android (Google) Code Review
3 changed files with 59 additions and 28 deletions

View File

@@ -33,9 +33,9 @@ import com.android.systemui.CoreStartable;
import com.android.systemui.R; import com.android.systemui.R;
import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent; import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent;
import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener; import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
import com.android.systemui.util.ViewController; import com.android.systemui.util.ViewController;
import javax.inject.Inject; import javax.inject.Inject;
@@ -73,7 +73,6 @@ public class DreamWeatherComplication implements Complication {
* {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI. * {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI.
*/ */
public static class Registrant extends CoreStartable { public static class Registrant extends CoreStartable {
private final LockscreenSmartspaceController mSmartSpaceController;
private final DreamOverlayStateController mDreamOverlayStateController; private final DreamOverlayStateController mDreamOverlayStateController;
private final DreamWeatherComplication mComplication; private final DreamWeatherComplication mComplication;
@@ -82,20 +81,16 @@ public class DreamWeatherComplication implements Complication {
*/ */
@Inject @Inject
public Registrant(Context context, public Registrant(Context context,
LockscreenSmartspaceController smartspaceController,
DreamOverlayStateController dreamOverlayStateController, DreamOverlayStateController dreamOverlayStateController,
DreamWeatherComplication dreamWeatherComplication) { DreamWeatherComplication dreamWeatherComplication) {
super(context); super(context);
mSmartSpaceController = smartspaceController;
mDreamOverlayStateController = dreamOverlayStateController; mDreamOverlayStateController = dreamOverlayStateController;
mComplication = dreamWeatherComplication; mComplication = dreamWeatherComplication;
} }
@Override @Override
public void start() { public void start() {
if (mSmartSpaceController.isEnabled()) { mDreamOverlayStateController.addComplication(mComplication);
mDreamOverlayStateController.addComplication(mComplication);
}
} }
} }
@@ -134,7 +129,7 @@ public class DreamWeatherComplication implements Complication {
* ViewController to contain value/logic associated with a Weather Complication View. * ViewController to contain value/logic associated with a Weather Complication View.
*/ */
static class DreamWeatherViewController extends ViewController<TextView> { static class DreamWeatherViewController extends ViewController<TextView> {
private final LockscreenSmartspaceController mSmartSpaceController; private final DreamSmartspaceController mSmartSpaceController;
private final ActivityStarter mActivityStarter; private final ActivityStarter mActivityStarter;
private final String mSmartspaceTrampolineActivityComponent; private final String mSmartspaceTrampolineActivityComponent;
private SmartspaceTargetListener mSmartspaceTargetListener; private SmartspaceTargetListener mSmartspaceTargetListener;
@@ -144,7 +139,7 @@ public class DreamWeatherComplication implements Complication {
@Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view, @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
@Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) String smartspaceTrampoline, @Named(SMARTSPACE_TRAMPOLINE_ACTIVITY_COMPONENT) String smartspaceTrampoline,
ActivityStarter activityStarter, ActivityStarter activityStarter,
LockscreenSmartspaceController smartspaceController DreamSmartspaceController smartspaceController
) { ) {
super(view); super(view);
mActivityStarter = activityStarter; mActivityStarter = activityStarter;
@@ -192,12 +187,14 @@ public class DreamWeatherComplication implements Complication {
}); });
} }
}); });
mSmartSpaceController.addListener(mSmartspaceTargetListener); // We need to use an unfiltered listener here since weather is filtered from showing
// in the dream smartspace.
mSmartSpaceController.addUnfilteredListener(mSmartspaceTargetListener);
} }
@Override @Override
protected void onViewDetached() { protected void onViewDetached() {
mSmartSpaceController.removeListener(mSmartspaceTargetListener); mSmartSpaceController.removeUnfilteredListener(mSmartspaceTargetListener);
} }
} }
} }

View File

@@ -19,6 +19,7 @@ package com.android.systemui.dreams.smartspace
import android.app.smartspace.SmartspaceConfig import android.app.smartspace.SmartspaceConfig
import android.app.smartspace.SmartspaceManager import android.app.smartspace.SmartspaceManager
import android.app.smartspace.SmartspaceSession import android.app.smartspace.SmartspaceSession
import android.app.smartspace.SmartspaceTarget
import android.content.Context import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.util.Log import android.util.Log
@@ -66,7 +67,9 @@ class DreamSmartspaceController @Inject constructor(
private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null) private var targetFilter: SmartspaceTargetFilter? = optionalTargetFilter.orElse(null)
// A shadow copy of listeners is maintained to track whether the session should remain open. // A shadow copy of listeners is maintained to track whether the session should remain open.
private var listeners = mutableSetOf<BcSmartspaceDataPlugin.SmartspaceTargetListener>() private var listeners = mutableSetOf<SmartspaceTargetListener>()
private var unfilteredListeners = mutableSetOf<SmartspaceTargetListener>()
// Smartspace can be used on multiple displays, such as when the user casts their screen // Smartspace can be used on multiple displays, such as when the user casts their screen
private var smartspaceViews = mutableSetOf<SmartspaceView>() private var smartspaceViews = mutableSetOf<SmartspaceView>()
@@ -113,6 +116,7 @@ class DreamSmartspaceController @Inject constructor(
private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets -> private val sessionListener = SmartspaceSession.OnTargetsAvailableListener { targets ->
execution.assertIsMainThread() execution.assertIsMainThread()
onTargetsAvailableUnfiltered(targets)
val filteredTargets = targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true } val filteredTargets = targets.filter { targetFilter?.filterSmartspaceTarget(it) ?: true }
plugin?.onTargetsAvailable(filteredTargets) plugin?.onTargetsAvailable(filteredTargets)
} }
@@ -137,7 +141,7 @@ class DreamSmartspaceController @Inject constructor(
private fun buildView(parent: ViewGroup): View? { private fun buildView(parent: ViewGroup): View? {
return if (plugin != null) { return if (plugin != null) {
var view = smartspaceViewComponentFactory.create(parent, plugin, stateChangeListener) var view = smartspaceViewComponentFactory.create(parent, plugin, stateChangeListener)
.getView() .getView()
if (view !is View) { if (view !is View) {
return null return null
} }
@@ -151,7 +155,8 @@ class DreamSmartspaceController @Inject constructor(
} }
private fun hasActiveSessionListeners(): Boolean { private fun hasActiveSessionListeners(): Boolean {
return smartspaceViews.isNotEmpty() || listeners.isNotEmpty() return smartspaceViews.isNotEmpty() || listeners.isNotEmpty() ||
unfilteredListeners.isNotEmpty()
} }
private fun connectSession() { private fun connectSession() {
@@ -164,13 +169,15 @@ class DreamSmartspaceController @Inject constructor(
} }
val newSession = smartspaceManager.createSmartspaceSession( val newSession = smartspaceManager.createSmartspaceSession(
SmartspaceConfig.Builder(context, "dream").build()) SmartspaceConfig.Builder(context, "dream").build()
)
Log.d(TAG, "Starting smartspace session for dream") Log.d(TAG, "Starting smartspace session for dream")
newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener) newSession.addOnTargetsAvailableListener(uiExecutor, sessionListener)
this.session = newSession this.session = newSession
plugin.registerSmartspaceEventNotifier { plugin.registerSmartspaceEventNotifier {
e -> session?.notifySmartspaceEvent(e) e ->
session?.notifySmartspaceEvent(e)
} }
reloadSmartspace() reloadSmartspace()
@@ -218,4 +225,22 @@ class DreamSmartspaceController @Inject constructor(
private fun reloadSmartspace() { private fun reloadSmartspace() {
session?.requestSmartspaceUpdate() session?.requestSmartspaceUpdate()
} }
private fun onTargetsAvailableUnfiltered(targets: List<SmartspaceTarget>) {
unfilteredListeners.forEach { it.onSmartspaceTargetsUpdated(targets) }
}
/**
* Adds a listener for the raw, unfiltered list of smartspace targets. This should be used
* carefully, as it doesn't filter out targets which the user may not want shown.
*/
fun addUnfilteredListener(listener: SmartspaceTargetListener) {
unfilteredListeners.add(listener)
connectSession()
}
fun removeUnfilteredListener(listener: SmartspaceTargetListener) {
unfilteredListeners.remove(listener)
disconnect()
}
} }

View File

@@ -17,18 +17,19 @@ package com.android.systemui.dreams.complication;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context; import android.content.Context;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.widget.TextView;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase; import com.android.systemui.SysuiTestCase;
import com.android.systemui.dreams.DreamOverlayStateController; import com.android.systemui.dreams.DreamOverlayStateController;
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController; import com.android.systemui.dreams.smartspace.DreamSmartspaceController;
import com.android.systemui.plugins.ActivityStarter;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -39,12 +40,14 @@ import org.mockito.MockitoAnnotations;
@SmallTest @SmallTest
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
public class DreamWeatherComplicationTest extends SysuiTestCase { public class DreamWeatherComplicationTest extends SysuiTestCase {
private static final String TRAMPOLINE_COMPONENT = "TestComponent";
@SuppressWarnings("HidingField") @SuppressWarnings("HidingField")
@Mock @Mock
private Context mContext; private Context mContext;
@Mock @Mock
private LockscreenSmartspaceController mSmartspaceController; private DreamSmartspaceController mDreamSmartspaceController;
@Mock @Mock
private DreamOverlayStateController mDreamOverlayStateController; private DreamOverlayStateController mDreamOverlayStateController;
@@ -58,22 +61,28 @@ public class DreamWeatherComplicationTest extends SysuiTestCase {
} }
/** /**
* Ensures {@link DreamWeatherComplication} is only registered when it is available. * Ensures {@link DreamWeatherComplication} is registered.
*/ */
@Test @Test
public void testComplicationAvailability() { public void testComplicationRegistered() {
when(mSmartspaceController.isEnabled()).thenReturn(false);
final DreamWeatherComplication.Registrant registrant = final DreamWeatherComplication.Registrant registrant =
new DreamWeatherComplication.Registrant( new DreamWeatherComplication.Registrant(
mContext, mContext,
mSmartspaceController,
mDreamOverlayStateController, mDreamOverlayStateController,
mComplication); mComplication);
registrant.start(); registrant.start();
verify(mDreamOverlayStateController, never()).addComplication(any());
when(mSmartspaceController.isEnabled()).thenReturn(true);
registrant.start();
verify(mDreamOverlayStateController).addComplication(eq(mComplication)); verify(mDreamOverlayStateController).addComplication(eq(mComplication));
} }
@Test
public void testGetUnfilteredTargets() {
final DreamWeatherComplication.DreamWeatherViewController controller =
new DreamWeatherComplication.DreamWeatherViewController(mock(
TextView.class), TRAMPOLINE_COMPONENT, mock(ActivityStarter.class),
mDreamSmartspaceController);
controller.onViewAttached();
verify(mDreamSmartspaceController).addUnfilteredListener(any());
controller.onViewDetached();
verify(mDreamSmartspaceController).removeUnfilteredListener(any());
}
} }