Adding a Plugin to attach things under QQS

This plugin responds to expansion and changes in theme, ui, etc.

Test: manual

Change-Id: I5d866a4fff491aa6273edfb765e538864b270534
This commit is contained in:
Fabian Kozynski
2019-08-15 15:44:07 -04:00
parent f7a1488e90
commit 3bf518dd98
7 changed files with 250 additions and 2 deletions

View File

@@ -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.

View File

@@ -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) {};
}

View File

@@ -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>

View File

@@ -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"/>

View File

@@ -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
}

View File

@@ -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

View File

@@ -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;