Merge "Enable smartspace" into sc-dev am: 83bfac2e6d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13952009
Change-Id: Iee8446e1c60b5c71c50690d66b4b8c80941188d8
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 85ecb1c..3adff12 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -275,6 +275,8 @@
<!-- Permission to make accessibility service access Bubbles -->
<uses-permission android:name="android.permission.ADD_TRUSTED_DISPLAY" />
+ <!-- Can control and display smartspace content -->
+ <uses-permission android:name="android.permission.MANAGE_SMARTSPACE" />
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index f8a9a045..c090d8a 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -16,7 +16,9 @@
package com.android.systemui.plugins;
+import android.app.smartspace.SmartspaceTarget;
import android.os.Parcelable;
+import android.view.ViewGroup;
import com.android.systemui.plugins.annotations.ProvidesInterface;
@@ -36,9 +38,23 @@
/** Unregister a listener. */
void unregisterListener(SmartspaceTargetListener listener);
+ /**
+ * Create a view to be shown within the parent. Do not add the view, as the parent
+ * will be responsible for correctly setting the LayoutParams
+ */
+ SmartspaceView getView(ViewGroup parent);
+
+ /** Updates Smartspace data and propagates it to any listeners. */
+ void onTargetsAvailable(List<SmartspaceTarget> targets);
+
/** Provides Smartspace data to registered listeners. */
interface SmartspaceTargetListener {
/** Each Parcelable is a SmartspaceTarget that represents a card. */
void onSmartspaceTargetsUpdated(List<? extends Parcelable> targets);
}
+
+ /** View to which this plugin can be registered, in order to get updates. */
+ interface SmartspaceView {
+ void registerDataProvider(BcSmartspaceDataPlugin plugin);
+ }
}
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 0763109..c39db94 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -50,4 +50,6 @@
<bool name="flag_charging_ripple">false</bool>
<bool name="flag_ongoing_call_status_bar_chip">false</bool>
+
+ <bool name="flag_smartspace">false</bool>
</resources>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 0675200..24b7cd1 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -16,8 +16,15 @@
package com.android.keyguard;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
import android.app.WallpaperManager;
+import android.app.smartspace.SmartspaceConfig;
+import android.app.smartspace.SmartspaceManager;
+import android.app.smartspace.SmartspaceSession;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Resources;
import android.provider.Settings;
import android.text.TextUtils;
@@ -25,6 +32,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
@@ -32,8 +40,12 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.ClockPlugin;
+import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -43,6 +55,7 @@
import java.util.Locale;
import java.util.TimeZone;
+import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -68,6 +81,13 @@
private AnimatableClockController mNewLockScreenLargeClockViewController;
private FrameLayout mNewLockScreenLargeClockFrame;
+ private PluginManager mPluginManager;
+ private boolean mIsSmartspaceEnabled;
+ PluginListener mPluginListener;
+ private Executor mUiExecutor;
+ private SmartspaceSession mSmartspaceSession;
+ private SmartspaceSession.Callback mSmartspaceCallback;
+
private int mLockScreenMode = KeyguardUpdateMonitor.LOCK_SCREEN_MODE_NORMAL;
private final StatusBarStateController.StateListener mStateListener =
@@ -96,6 +116,9 @@
private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;
private String mTimeFormat;
+ // If set, will replace keyguard_status_area
+ private BcSmartspaceDataPlugin.SmartspaceView mSmartspaceView;
+
@Inject
public KeyguardClockSwitchController(
KeyguardClockSwitch keyguardClockSwitch,
@@ -105,7 +128,10 @@
KeyguardSliceViewController keyguardSliceViewController,
NotificationIconAreaController notificationIconAreaController,
ContentResolver contentResolver,
- BroadcastDispatcher broadcastDispatcher) {
+ BroadcastDispatcher broadcastDispatcher,
+ PluginManager pluginManager,
+ FeatureFlags featureFlags,
+ @Main Executor uiExecutor) {
super(keyguardClockSwitch);
mResources = resources;
mStatusBarStateController = statusBarStateController;
@@ -115,6 +141,9 @@
mNotificationIconAreaController = notificationIconAreaController;
mBroadcastDispatcher = broadcastDispatcher;
mTimeFormat = Settings.System.getString(contentResolver, Settings.System.TIME_12_24);
+ mPluginManager = pluginManager;
+ mIsSmartspaceEnabled = featureFlags.isSmartspaceEnabled();
+ mUiExecutor = uiExecutor;
}
/**
@@ -137,6 +166,63 @@
updateAodIcons();
mNewLockScreenClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view);
mNewLockScreenLargeClockFrame = mView.findViewById(R.id.new_lockscreen_clock_view_large);
+
+ // If a smartspace plugin is detected, replace the existing smartspace
+ // (keyguard_status_area), and initialize a new session
+ mPluginListener = new PluginListener<BcSmartspaceDataPlugin>() {
+
+ @Override
+ public void onPluginConnected(BcSmartspaceDataPlugin plugin, Context pluginContext) {
+ if (!mIsSmartspaceEnabled) return;
+
+ View ksa = mView.findViewById(R.id.keyguard_status_area);
+ int ksaIndex = mView.indexOfChild(ksa);
+ ksa.setVisibility(View.GONE);
+
+ mSmartspaceView = plugin.getView(mView);
+ mSmartspaceView.registerDataProvider(plugin);
+
+ RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
+ MATCH_PARENT, WRAP_CONTENT);
+ lp.addRule(RelativeLayout.BELOW, R.id.new_lockscreen_clock_view);
+ mView.addView((View) mSmartspaceView, ksaIndex, lp);
+
+ View nic = mView.findViewById(
+ com.android.systemui.R.id.left_aligned_notification_icon_container);
+ lp = (RelativeLayout.LayoutParams) nic.getLayoutParams();
+ lp.addRule(RelativeLayout.BELOW, ((View) mSmartspaceView).getId());
+ nic.setLayoutParams(lp);
+
+ createSmartspaceSession(plugin);
+ }
+
+ @Override
+ public void onPluginDisconnected(BcSmartspaceDataPlugin plugin) {
+ if (!mIsSmartspaceEnabled) return;
+
+ mView.removeView((View) mSmartspaceView);
+ mView.findViewById(R.id.keyguard_status_area).setVisibility(View.VISIBLE);
+
+ View nic = mView.findViewById(
+ com.android.systemui.R.id.left_aligned_notification_icon_container);
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams)
+ nic.getLayoutParams();
+ lp.addRule(RelativeLayout.BELOW, R.id.keyguard_status_area);
+ nic.setLayoutParams(lp);
+
+ mSmartspaceView = null;
+ }
+
+ private void createSmartspaceSession(BcSmartspaceDataPlugin plugin) {
+ mSmartspaceSession = getContext().getSystemService(SmartspaceManager.class)
+ .createSmartspaceSession(
+ new SmartspaceConfig.Builder(getContext(), "lockscreen").build());
+ mSmartspaceCallback = targets -> plugin.onTargetsAvailable(targets);
+ mSmartspaceSession.registerSmartspaceUpdates(mUiExecutor, mSmartspaceCallback);
+ mSmartspaceSession.requestSmartspaceUpdate();
+ }
+ };
+ mPluginManager.addPluginListener(mPluginListener, BcSmartspaceDataPlugin.class, false);
}
@Override
@@ -147,6 +233,13 @@
mStatusBarStateController.removeCallback(mStateListener);
mColorExtractor.removeOnColorsChangedListener(mColorsListener);
mView.setClockPlugin(null, mStatusBarStateController.getState());
+
+ if (mSmartspaceSession != null) {
+ mSmartspaceSession.unregisterSmartspaceUpdates(mSmartspaceCallback);
+ mSmartspaceSession.destroy();
+ mSmartspaceSession = null;
+ }
+ mPluginManager.removePluginListener(mPluginListener);
}
/**
@@ -222,6 +315,11 @@
PropertyAnimator.setProperty(mNewLockScreenLargeClockFrame, AnimatableProperty.SCALE_Y,
scale, props, animate);
}
+
+ if (mSmartspaceView != null) {
+ PropertyAnimator.setProperty((View) mSmartspaceView, AnimatableProperty.TRANSLATION_X,
+ x, props, animate);
+ }
mKeyguardSliceViewController.updatePosition(x, props, animate);
mNotificationIconAreaController.updatePosition(x, props, animate);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index f51fbed..ec3a857 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -97,4 +97,8 @@
public boolean isOngoingCallStatusBarChipEnabled() {
return mFlagReader.isEnabled(R.bool.flag_ongoing_call_status_bar_chip);
}
+
+ public boolean isSmartspaceEnabled() {
+ return mFlagReader.isEnabled(R.bool.flag_smartspace);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index 70a7b7a..0fcd79b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -18,26 +18,34 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import android.widget.RelativeLayout;
import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.colorextraction.SysuiColorExtractor;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
import com.android.systemui.statusbar.phone.NotificationIconContainer;
@@ -50,6 +58,8 @@
import org.mockito.MockitoAnnotations;
import org.mockito.verification.VerificationMode;
+import java.util.concurrent.Executor;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockSwitchControllerTest extends SysuiTestCase {
@@ -78,6 +88,12 @@
ContentResolver mContentResolver;
@Mock
BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private PluginManager mPluginManager;
+ @Mock
+ private FeatureFlags mFeatureFlags;
+ @Mock
+ private Executor mExecutor;
private KeyguardClockSwitchController mController;
@@ -87,6 +103,8 @@
when(mView.findViewById(com.android.systemui.R.id.left_aligned_notification_icon_container))
.thenReturn(mNotificationIcons);
+ when(mView.getContext()).thenReturn(getContext());
+ when(mFeatureFlags.isSmartspaceEnabled()).thenReturn(true);
when(mView.isAttachedToWindow()).thenReturn(true);
when(mResources.getString(anyInt())).thenReturn("h:mm");
mController = new KeyguardClockSwitchController(
@@ -98,7 +116,10 @@
mKeyguardSliceViewController,
mNotificationIconAreaController,
mContentResolver,
- mBroadcastDispatcher);
+ mBroadcastDispatcher,
+ mPluginManager,
+ mFeatureFlags,
+ mExecutor);
when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
@@ -182,6 +203,45 @@
verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
}
+ @Test
+ public void testSmartspacePluginConnectedRemovesKeyguardStatusArea() {
+ mController.init();
+
+ View statusArea = mock(View.class);
+ when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
+
+ View nic = mock(View.class);
+ when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
+ when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
+
+ BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
+ TestView view = mock(TestView.class);
+ when(plugin.getView(any())).thenReturn(view);
+
+ mController.mPluginListener.onPluginConnected(plugin, mContext);
+ verify(statusArea).setVisibility(View.GONE);
+ }
+
+ @Test
+ public void testSmartspacePluginDisconnectedShowsKeyguardStatusArea() {
+ mController.init();
+
+ View statusArea = mock(View.class);
+ when(mView.findViewById(R.id.keyguard_status_area)).thenReturn(statusArea);
+
+ View nic = mock(View.class);
+ when(mView.findViewById(R.id.left_aligned_notification_icon_container)).thenReturn(nic);
+ when(nic.getLayoutParams()).thenReturn(mock(RelativeLayout.LayoutParams.class));
+
+ BcSmartspaceDataPlugin plugin = mock(BcSmartspaceDataPlugin.class);
+ TestView view = mock(TestView.class);
+ when(plugin.getView(any())).thenReturn(view);
+
+ mController.mPluginListener.onPluginConnected(plugin, mContext);
+ mController.mPluginListener.onPluginDisconnected(plugin);
+ verify(statusArea).setVisibility(View.VISIBLE);
+ }
+
private void verifyAttachment(VerificationMode times) {
verify(mClockManager, times).addOnClockChangedListener(
any(ClockManager.ClockChangedListener.class));
@@ -191,4 +251,12 @@
any(ColorExtractor.OnColorsChangedListener.class));
verify(mView, times).updateColors(mGradientColors);
}
+
+ private static class TestView extends View implements BcSmartspaceDataPlugin.SmartspaceView {
+ TestView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void registerDataProvider(BcSmartspaceDataPlugin plugin) { }
+ }
}