diff options
| author | 2019-08-15 15:44:07 -0400 | |
|---|---|---|
| committer | 2019-09-05 12:26:13 -0400 | |
| commit | 3bf518dd98c14860cdae1cea73085d2187cbfdb9 (patch) | |
| tree | 87fd04b0207e0363e0a32173033389bf976d3851 | |
| parent | f7a1488e90c485b2b0cc7d002c2ad286782acb3c (diff) | |
Adding a Plugin to attach things under QQS
This plugin responds to expansion and changes in theme, ui, etc.
Test: manual
Change-Id: I5d866a4fff491aa6273edfb765e538864b270534
7 files changed, 250 insertions, 2 deletions
diff --git a/packages/SystemUI/docs/plugin_hooks.md b/packages/SystemUI/docs/plugin_hooks.md index 9fe2e181971a..2fb0c996111a 100644 --- a/packages/SystemUI/docs/plugin_hooks.md +++ b/packages/SystemUI/docs/plugin_hooks.md @@ -56,6 +56,11 @@ Expected interface: [ClockPlugin](/packages/SystemUI/plugin/src/com/android/syst Use: Allows replacement of the keyguard main clock. +### Action: com.android.systemui.action.PLUGIN_NPV +Expected interface: [NPVPlugin](/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java) + +Use: Attach a view under QQS for prototyping. + # Global plugin dependencies These classes can be accessed by any plugin using PluginDependency as long as they @Requires them. diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java new file mode 100644 index 000000000000..1426266a7048 --- /dev/null +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2019 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.plugins; + +import android.view.View; +import android.widget.FrameLayout; + +import com.android.systemui.plugins.annotations.ProvidesInterface; + +/** + * Plugin to attach custom views under QQS. + * + * A parent view is provided to the plugin to which they can add Views. + * <br> + * The parent is a {@link FrameLayout} with same background as QS and 96dp height. + * + * {@see NPVPluginManager} + * {@see status_bar_expanded_plugin_frame} + */ +@ProvidesInterface(action = NPVPlugin.ACTION, version = NPVPlugin.VERSION) +public interface NPVPlugin extends Plugin { + String ACTION = "com.android.systemui.action.PLUGIN_NPV"; + int VERSION = 1; + + /** + * Attach views to the parent. + * + * @param parent a {@link FrameLayout} to which to attach views. Preferably a root view. + * @return a view attached to parent. + */ + View attachToRoot(FrameLayout parent); + + /** + * Indicate to the plugin when it is listening (QS expanded) + * @param listening + */ + default void setListening(boolean listening) {}; +} diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml index 7a9400887c75..e7c6b2574f5f 100644 --- a/packages/SystemUI/res/layout/status_bar_expanded.xml +++ b/packages/SystemUI/res/layout/status_bar_expanded.xml @@ -111,4 +111,6 @@ android:visibility="invisible" android:background="@drawable/qs_navbar_scrim" /> + <include layout="@layout/status_bar_expanded_plugin_frame"/> + </com.android.systemui.statusbar.phone.NotificationPanelView> diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml new file mode 100644 index 000000000000..4849dfb777ed --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2019 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. +--> + +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/plugin_frame" + android:theme="@style/qs_theme" + android:layout_width="@dimen/qs_panel_width" + android:layout_height="96dp" + android:layout_gravity="center_horizontal" + android:layout_marginTop="@*android:dimen/quick_qs_total_height" + android:layout_marginLeft="@dimen/notification_side_paddings" + android:layout_marginRight="@dimen/notification_side_paddings" + android:visibility="gone" + android:background="@drawable/qs_background_primary"/>
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt new file mode 100644 index 000000000000..7dcc2fcfe2b2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2019 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.statusbar.phone + +import android.content.Context +import android.view.View +import android.widget.FrameLayout +import com.android.systemui.plugins.NPVPlugin +import com.android.systemui.plugins.PluginListener +import com.android.systemui.qs.TouchAnimator +import com.android.systemui.shared.plugins.PluginManager + +/** + * Manages the NPVPlugin view and state + * + * Abstracts NPVPlugin from NPV and helps animate on expansion and respond to changes in Config. + */ +class NPVPluginManager( + var parent: FrameLayout, + val pluginManager: PluginManager +) : PluginListener<NPVPlugin> { + + private var plugin: NPVPlugin? = null + private var animator = createAnimator() + + private fun createAnimator() = TouchAnimator.Builder() + .addFloat(parent, "alpha", 1f, 0f) + .addFloat(parent, "scaleY", 1f, 0f) + .build() + + init { + pluginManager.addPluginListener(NPVPlugin.ACTION, this, NPVPlugin::class.java, false) + parent.pivotY = 0f + } + + override fun onPluginConnected(plugin: NPVPlugin, pluginContext: Context) { + parent.removeAllViews() + plugin.attachToRoot(parent) + this.plugin = plugin + parent.visibility = View.VISIBLE + } + + fun changeVisibility(visibility: Int) { + parent.visibility = if (plugin != null) visibility else View.GONE + } + + fun destroy() { + plugin?.onDestroy() + pluginManager.removePluginListener(this) + } + + override fun onPluginDisconnected(plugin: NPVPlugin) { + if (this.plugin == plugin) { + this.plugin = null + parent.removeAllViews() + parent.visibility = View.GONE + } + } + + fun setListening(listening: Boolean) { + plugin?.setListening(listening) + } + + fun setExpansion(expansion: Float, headerTranslation: Float, heightDiff: Float) { + parent.setTranslationY(expansion * heightDiff + headerTranslation) + if (!expansion.isNaN()) animator.setPosition(expansion) + } + + fun replaceFrameLayout(newParent: FrameLayout) { + newParent.visibility = parent.visibility + parent.removeAllViews() + plugin?.attachToRoot(newParent) + parent = newParent + animator = createAnimator() + } + + fun getHeight() = if (plugin != null) parent.height else 0 +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index a58ba858e518..f5835765bdea 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -393,6 +393,10 @@ public class NotificationPanelView extends PanelView implements private boolean mAllowExpandForSmallExpansion; private Runnable mExpandAfterLayoutRunnable; + private PluginManager mPluginManager; + private FrameLayout mPluginFrame; + private NPVPluginManager mNPVPluginManager; + @Inject public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, InjectionInflationController injectionInflationController, @@ -400,7 +404,8 @@ public class NotificationPanelView extends PanelView implements PulseExpansionHandler pulseExpansionHandler, DynamicPrivacyController dynamicPrivacyController, KeyguardBypassController bypassController, - FalsingManager falsingManager) { + FalsingManager falsingManager, + PluginManager pluginManager) { super(context, attrs); setWillNotDraw(!DEBUG); mInjectionInflationController = injectionInflationController; @@ -431,6 +436,7 @@ public class NotificationPanelView extends PanelView implements }); mBottomAreaShadeAlphaAnimator.setDuration(160); mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT); + mPluginManager = pluginManager; } /** @@ -465,6 +471,9 @@ public class NotificationPanelView extends PanelView implements mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area); mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mLastOrientation = getResources().getConfiguration().orientation; + mPluginFrame = findViewById(R.id.plugin_frame); + mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + initBottomArea(); @@ -584,6 +593,19 @@ public class NotificationPanelView extends PanelView implements lp.gravity = panelGravity; mNotificationStackScroller.setLayoutParams(lp); } + int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); + int topMargin = + res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); + lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); + if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin + || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { + lp.width = qsWidth; + lp.gravity = panelGravity; + lp.leftMargin = sideMargin; + lp.rightMargin = sideMargin; + lp.topMargin = topMargin; + mPluginFrame.setLayoutParams(lp); + } } @Override @@ -650,6 +672,43 @@ public class NotificationPanelView extends PanelView implements if (mOnReinflationListener != null) { mOnReinflationListener.run(); } + reinflatePluginContainer(); + } + + @Override + public void onUiModeChanged() { + reinflatePluginContainer(); + } + + private void reinflatePluginContainer() { + int index = indexOfChild(mPluginFrame); + removeView(mPluginFrame); + mPluginFrame = (FrameLayout) mInjectionInflationController + .injectable(LayoutInflater.from(mContext)).inflate( + R.layout.status_bar_expanded_plugin_frame, + this, + false); + addView(mPluginFrame, index); + + Resources res = getResources(); + int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width); + int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity); + FrameLayout.LayoutParams lp; + int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings); + int topMargin = + res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height); + lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams(); + if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin + || lp.rightMargin != sideMargin || lp.topMargin != topMargin) { + lp.width = qsWidth; + lp.gravity = panelGravity; + lp.leftMargin = sideMargin; + lp.rightMargin = sideMargin; + lp.topMargin = topMargin; + mPluginFrame.setLayoutParams(lp); + } + + mNPVPluginManager.replaceFrameLayout(mPluginFrame); } private void initBottomArea() { @@ -679,6 +738,7 @@ public class NotificationPanelView extends PanelView implements int oldMaxHeight = mQsMaxExpansionHeight; if (mQs != null) { mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); + mQsMinExpansionHeight += mNPVPluginManager.getHeight(); mQsMaxExpansionHeight = mQs.getDesiredHeight(); mNotificationStackScroller.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); @@ -1784,6 +1844,9 @@ public class NotificationPanelView extends PanelView implements mBarState != StatusBarState.KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); updateEmptyShadeView(); + mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD) + ? View.VISIBLE + : View.INVISIBLE); mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling && mQsScrimEnabled ? View.VISIBLE @@ -1839,6 +1902,8 @@ public class NotificationPanelView extends PanelView implements if (mQs == null) return; float qsExpansionFraction = getQsExpansionFraction(); mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); + int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight(); + mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); } @@ -2259,6 +2324,7 @@ public class NotificationPanelView extends PanelView implements appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); } startHeight = -mQs.getQsMinExpansionHeight(); + startHeight -= mNPVPluginManager.getHeight(); } float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)) @@ -2399,6 +2465,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusBar.setListening(listening); if (mQs == null) return; mQs.setListening(listening); + mNPVPluginManager.setListening(listening); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java index a96efd7d2fc2..86ab3a70c3ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java @@ -38,6 +38,7 @@ import com.android.systemui.SystemUIFactory; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.KeyguardAffordanceView; import com.android.systemui.statusbar.NotificationLockscreenUserManager; import com.android.systemui.statusbar.NotificationShelf; @@ -195,7 +196,7 @@ public class NotificationPanelViewTest extends SysuiTestCase { SystemUIFactory.getInstance().getRootComponent()), coordinator, expansionHandler, mock(DynamicPrivacyController.class), bypassController, - mFalsingManager); + mFalsingManager, mock(PluginManager.class)); mNotificationStackScroller = mNotificationStackScrollLayout; mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView; mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar; |