Loading packages/SystemUI/docs/plugin_hooks.md +5 −0 Original line number Diff line number Diff line Loading @@ -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. Loading packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * 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) {}; } packages/SystemUI/res/layout/status_bar_expanded.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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> packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml 0 → 100644 +29 −0 Original line number Diff line number Diff line <?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 packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * 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 } Loading
packages/SystemUI/docs/plugin_hooks.md +5 −0 Original line number Diff line number Diff line Loading @@ -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. Loading
packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java 0 → 100644 +52 −0 Original line number Diff line number Diff line /* * 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) {}; }
packages/SystemUI/res/layout/status_bar_expanded.xml +2 −0 Original line number Diff line number Diff line Loading @@ -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>
packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml 0 → 100644 +29 −0 Original line number Diff line number Diff line <?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
packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt 0 → 100644 +92 −0 Original line number Diff line number Diff line /* * 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 }