diff options
7 files changed, 202 insertions, 8 deletions
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 f8a9a0459673..35423a979cbc 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,25 @@ public interface BcSmartspaceDataPlugin extends Plugin { /** 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 + */ + default SmartspaceView getView(ViewGroup parent) { + return null; + } + + /** 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 834b482a449e..bbf204844e29 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">true</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 0675200f81e2..24b7cd118ed6 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.text.format.DateFormat; 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.R; 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 com.android.systemui.util.ViewController; import java.util.Locale; import java.util.TimeZone; +import java.util.concurrent.Executor; import javax.inject.Inject; @@ -68,6 +81,13 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS 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/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java index 9d00262436e5..cc167b928c3c 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java @@ -46,6 +46,7 @@ public class SystemUIFactory { private GlobalRootComponent mRootComponent; private WMComponent mWMComponent; private SysUIComponent mSysUIComponent; + private boolean mInitializeComponents; public static <T extends SystemUIFactory> T getInstance() { return (T) mFactory; @@ -88,13 +89,13 @@ public class SystemUIFactory { public void init(Context context, boolean fromTest) throws ExecutionException, InterruptedException { // Only initialize components for the main system ui process running as the primary user - final boolean initializeComponents = !fromTest + mInitializeComponents = !fromTest && android.os.Process.myUserHandle().isSystem() && ActivityThread.currentProcessName().equals(ActivityThread.currentPackageName()); mRootComponent = buildGlobalRootComponent(context); // Stand up WMComponent mWMComponent = mRootComponent.getWMComponentBuilder().build(); - if (initializeComponents) { + if (mInitializeComponents) { // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment mWMComponent.init(); @@ -102,7 +103,7 @@ public class SystemUIFactory { // And finally, retrieve whatever SysUI needs from WMShell and build SysUI. SysUIComponent.Builder builder = mRootComponent.getSysUIComponent(); - if (initializeComponents) { + if (mInitializeComponents) { // Only initialize when not starting from tests since this currently initializes some // components that shouldn't be run in the test environment builder = prepareSysUIComponentBuilder(builder, mWMComponent) @@ -134,7 +135,7 @@ public class SystemUIFactory { .setStartingSurface(Optional.ofNullable(null)); } mSysUIComponent = builder.build(); - if (initializeComponents) { + if (mInitializeComponents) { mSysUIComponent.init(); } @@ -160,6 +161,9 @@ public class SystemUIFactory { .build(); } + protected boolean shouldInitializeComponents() { + return mInitializeComponents; + } public GlobalRootComponent getRootComponent() { return mRootComponent; diff --git a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt index 0eab572f315f..b6c2ef1db82a 100644 --- a/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt +++ b/packages/SystemUI/src/com/android/systemui/media/SmartspaceMediaDataProvider.kt @@ -21,7 +21,7 @@ class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin } /** Updates Smartspace data and propagates it to any listeners. */ - fun onTargetsAvailable(targets: List<SmartspaceTarget>) { + override fun onTargetsAvailable(targets: List<SmartspaceTarget>) { // Filter out non-media targets. val mediaTargets = mutableListOf<SmartspaceTarget>() for (target in targets) { @@ -36,4 +36,4 @@ class SmartspaceMediaDataProvider @Inject constructor() : BcSmartspaceDataPlugin it.onSmartspaceTargetsUpdated(smartspaceMediaTargets) } } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java index f51fbedebad2..ec3a857dbc84 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 class FeatureFlags { 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 70a7b7a5acbc..0fcd79b357c3 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 @@ package com.android.keyguard; 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.Mock; 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { ContentResolver mContentResolver; @Mock BroadcastDispatcher mBroadcastDispatcher; + @Mock + private PluginManager mPluginManager; + @Mock + private FeatureFlags mFeatureFlags; + @Mock + private Executor mExecutor; private KeyguardClockSwitchController mController; @@ -87,6 +103,8 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { mKeyguardSliceViewController, mNotificationIconAreaController, mContentResolver, - mBroadcastDispatcher); + mBroadcastDispatcher, + mPluginManager, + mFeatureFlags, + mExecutor); when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE); when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors); @@ -182,6 +203,45 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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) { } + } } |