Merge changes Id9289826,If9cc89df
* changes:
Put security footers in new footer
Implement correct dimens for new footer
diff --git a/packages/SystemUI/res-keyguard/layout/fgs_footer.xml b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
new file mode 100644
index 0000000..5343411
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/fgs_footer.xml
@@ -0,0 +1,95 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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:layout_width="0dp"
+ android:layout_height="@dimen/qs_security_footer_single_line_height"
+ android:layout_weight="1"
+ android:gravity="center"
+ android:clickable="true"
+ android:visibility="gone">
+
+ <LinearLayout
+ android:id="@+id/fgs_text_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/new_qs_footer_action_inset"
+ android:background="@drawable/qs_security_footer_background"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:paddingHorizontal="@dimen/qs_footer_padding"
+ >
+
+ <ImageView
+ android:id="@+id/primary_footer_icon"
+ android:layout_width="@dimen/qs_footer_icon_size"
+ android:layout_height="@dimen/qs_footer_icon_size"
+ android:gravity="start"
+ android:layout_marginEnd="12dp"
+ android:contentDescription="@null"
+ android:src="@drawable/ic_info_outline"
+ android:tint="?android:attr/textColorSecondary" />
+
+ <TextView
+ android:id="@+id/footer_text"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:maxLines="1"
+ android:ellipsize="end"
+ android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
+ android:textColor="?android:attr/textColorSecondary"/>
+
+ <ImageView
+ android:id="@+id/footer_icon"
+ android:layout_width="@dimen/qs_footer_icon_size"
+ android:layout_height="@dimen/qs_footer_icon_size"
+ android:layout_marginStart="8dp"
+ android:contentDescription="@null"
+ android:src="@*android:drawable/ic_chevron_end"
+ android:autoMirrored="true"
+ android:tint="?android:attr/textColorSecondary" />
+ </LinearLayout>
+
+ <FrameLayout
+ android:id="@+id/fgs_number_container"
+ android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:background="@drawable/qs_footer_action_circle"
+ android:focusable="true"
+ android:visibility="gone">
+
+ <TextView
+ android:id="@+id/fgs_number"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
+ android:layout_gravity="center"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="18sp"/>
+ <ImageView
+ android:id="@+id/fgs_new"
+ android:layout_width="12dp"
+ android:layout_height="12dp"
+ android:scaleType="fitCenter"
+ android:layout_gravity="bottom|end"
+ android:src="@drawable/new_fgs_dot"
+ android:contentDescription="@string/fgs_dot_content_description"
+ />
+ </FrameLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml b/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
index 95bdd89..4884df7 100644
--- a/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
@@ -20,34 +20,41 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
- android:layout_height="@dimen/qs_footer_height"
+ android:layout_height="@dimen/new_footer_height"
+ android:elevation="@dimen/qs_panel_elevation"
+ android:paddingTop="8dp"
+ android:paddingBottom="4dp"
+ android:background="@drawable/qs_footer_actions_background"
android:gravity="center_vertical"
android:layout_gravity="bottom"
>
- <View
- android:layout_height="1dp"
+ <LinearLayout
+ android:id="@+id/security_footers_container"
+ android:orientation="horizontal"
+ android:layout_height="@dimen/qs_footer_action_button_size"
android:layout_width="0dp"
android:layout_weight="1"
- />
+ />
+ <!-- Negative margin equal to -->
<LinearLayout
android:layout_height="match_parent"
android:layout_width="wrap_content"
+ android:layout_marginEnd="@dimen/new_qs_footer_action_inset_negative"
>
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
android:background="@drawable/qs_footer_action_circle"
android:focusable="true">
<ImageView
android:id="@+id/multi_user_avatar"
- android:layout_width="@dimen/multi_user_avatar_expanded_size"
- android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_width="@dimen/qs_footer_icon_size"
+ android:layout_height="@dimen/qs_footer_icon_size"
android:layout_gravity="center"
android:scaleType="centerInside" />
</com.android.systemui.statusbar.phone.MultiUserSwitch>
@@ -56,19 +63,17 @@
android:id="@+id/settings_button_container"
android:layout_width="@dimen/qs_footer_action_button_size"
android:layout_height="@dimen/qs_footer_action_button_size"
- android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
android:background="@drawable/qs_footer_action_circle"
android:clipChildren="false"
android:clipToPadding="false">
<com.android.systemui.statusbar.phone.SettingsButton
android:id="@+id/settings_button"
- android:layout_width="match_parent"
- android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_width="@dimen/qs_footer_icon_size"
+ android:layout_height="@dimen/qs_footer_icon_size"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:contentDescription="@string/accessibility_quick_settings_settings"
- android:padding="@dimen/qs_footer_icon_padding"
android:scaleType="centerInside"
android:src="@drawable/ic_settings"
android:tint="?android:attr/textColorPrimary" />
diff --git a/packages/SystemUI/res/drawable/new_fgs_dot.xml b/packages/SystemUI/res/drawable/new_fgs_dot.xml
new file mode 100644
index 0000000..759ddaf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/new_fgs_dot.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 2022, 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval"
+ android:width="12dp"
+ android:height="12dp">
+ <solid android:color="@*android:color/red" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
index f54c30f..d057f5f 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
@@ -15,10 +15,7 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="@dimen/qs_footer_action_inset"
- android:insetBottom="@dimen/qs_footer_action_inset"
- android:insetLeft="@dimen/qs_footer_action_inset"
- android:insetRight="@dimen/qs_footer_action_inset">
+ android:inset="@dimen/new_qs_footer_action_inset">
<ripple
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
index 1a323bc..944061c 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -15,10 +15,7 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="@dimen/qs_footer_action_inset"
- android:insetBottom="@dimen/qs_footer_action_inset"
- android:insetLeft="@dimen/qs_footer_action_inset"
- android:insetRight="@dimen/qs_footer_action_inset">
+ android:inset="@dimen/new_qs_footer_action_inset">
<ripple
android:color="?android:attr/colorControlHighlight">
<item android:id="@android:id/mask">
diff --git a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
new file mode 100644
index 0000000..c9517cd9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<inset xmlns:android="http://schemas.android.com/apk/res/android">
+ <shape>
+ <solid android:color="?attr/underSurfaceColor"/>
+ <corners android:topLeftRadius="@dimen/qs_corner_radius"
+ android:topRightRadius="@dimen/qs_corner_radius"/>
+ </shape>
+</inset>
diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
index 860d23b..381af50 100644
--- a/packages/SystemUI/res/drawable/qs_security_footer_background.xml
+++ b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
@@ -15,8 +15,8 @@
~ limitations under the License.
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:insetTop="@dimen/qs_security_footer_background_inset"
- android:insetBottom="@dimen/qs_security_footer_background_inset"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset"
>
<ripple
android:color="?android:attr/colorControlHighlight">
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index 22abd0c..85b33cc 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -25,7 +25,7 @@
android:id="@+id/expanded_qs_scroll_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:elevation="4dp"
+ android:elevation="@dimen/qs_panel_elevation"
android:importantForAccessibility="no"
android:scrollbars="none"
android:clipChildren="false"
@@ -55,7 +55,7 @@
android:id="@+id/container_stub"
android:inflatedId="@+id/qs_footer_actions"
android:layout="@layout/new_footer_actions"
- android:layout_height="@dimen/qs_footer_height"
+ android:layout_height="@dimen/new_footer_height"
android:layout_width="match_parent"
android:layout_gravity="bottom"
/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 268088c..fe79f27 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -329,6 +329,9 @@
etc. -->
<dimen name="qs_footer_height">48dp</dimen>
+ <!-- 40dp (circles) + 8dp (circle padding) + 8dp (top) + 4dp (bottom) -->
+ <dimen name="new_footer_height">60dp</dimen>
+
<!-- The size of each of the icon buttons in the QS footer -->
<dimen name="qs_footer_action_button_size">48dp</dimen>
@@ -336,6 +339,9 @@
<!-- (48dp - 44dp) / 2 -->
<dimen name="qs_footer_action_inset">2dp</dimen>
+ <!-- (48dp - 40dp) / 2 -->
+ <dimen name="new_qs_footer_action_inset">4dp</dimen>
+ <dimen name="new_qs_footer_action_inset_negative">-4dp</dimen>
<!-- Margins on each side of QS Footer -->
<dimen name="qs_footer_margin">2dp</dimen>
@@ -491,7 +497,8 @@
<dimen name="qs_tile_text_size">14sp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
<dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
- <dimen name="qs_panel_padding_bottom">@dimen/qs_footer_height</dimen>
+ <dimen name="qs_panel_elevation">4dp</dimen>
+ <dimen name="qs_panel_padding_bottom">@dimen/new_footer_height</dimen>
<dimen name="qs_panel_padding_top">48dp</dimen>
<dimen name="qs_detail_header_padding">0dp</dimen>
<dimen name="qs_detail_image_width">56dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 53aebda..d39e295 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2375,6 +2375,9 @@
<item quantity="one"><xliff:g id="count" example="1">%s</xliff:g> active app</item>
<item quantity="other"><xliff:g id="count" example="2">%s</xliff:g> active apps</item>
</plurals>
+ <!-- Content description for a dot indicator in the running application indicating that there
+ is new information [CHAR LIMIT=NONE] -->
+ <string name="fgs_dot_content_description">New information</string>
<!-- Title for dialog listing applications currently running [CHAR LIMIT=NONE]-->
<string name="fgs_manager_dialog_title">Active apps</string>
<!-- Label of the button to stop an app from running [CHAR LIMIT=12]-->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 58ebe89..bbe0a99 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -40,6 +40,7 @@
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
import com.android.systemui.R
import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -50,6 +51,7 @@
import javax.inject.Inject
import kotlin.math.max
+@SysUISingleton
class FgsManagerController @Inject constructor(
private val context: Context,
@Main private val mainExecutor: Executor,
@@ -65,6 +67,9 @@
private val LOG_TAG = FgsManagerController::class.java.simpleName
}
+ var changesSinceDialog = false
+ private set
+
private var isAvailable = false
private val lock = Any()
@@ -137,6 +142,7 @@
val numPackagesAfter = getNumRunningPackagesLocked()
if (numPackagesAfter != numPackagesBefore) {
+ changesSinceDialog = true
onNumberOfPackagesChangedListeners.forEach {
backgroundExecutor.execute { it.onNumberOfPackagesChanged(numPackagesAfter) }
}
@@ -210,6 +216,7 @@
this.dialog = dialog
dialog.setOnDismissListener {
+ changesSinceDialog = false
synchronized(lock) {
this.dialog = null
updateAppItemsLocked()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 4aedbc9..77feb90 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -22,6 +22,8 @@
import android.provider.Settings
import android.provider.Settings.Global.USER_SWITCHER_ENABLED
import android.view.View
+import android.view.ViewGroup
+import android.widget.LinearLayout
import android.widget.Toast
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.InteractionJankMonitor
@@ -29,7 +31,6 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.flags.FeatureFlags
@@ -46,6 +47,7 @@
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import com.android.systemui.tuner.TunerService
+import com.android.systemui.util.DualHeightHorizontalLinearLayout
import com.android.systemui.util.ViewController
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
@@ -57,7 +59,7 @@
* determined by [buttonsVisibleState]
*/
@QSScope
-class FooterActionsController @Inject constructor(
+internal class FooterActionsController @Inject constructor(
view: FooterActionsView,
multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val activityStarter: ActivityStarter,
@@ -65,6 +67,8 @@
private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
private val deviceProvisionedController: DeviceProvisionedController,
+ private val securityFooterController: QSSecurityFooter,
+ private val fgsManagerFooterController: QSFgsManagerFooter,
private val falsingManager: FalsingManager,
private val metricsLogger: MetricsLogger,
private val tunerService: TunerService,
@@ -90,15 +94,15 @@
updateVisibility()
}
- init {
- view.elevation = resources.displayMetrics.density * 4f
- view.setBackgroundColor(Utils.getColorAttrDefaultColor(context, R.attr.underSurfaceColor))
- }
-
private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
+ private val securityFootersContainer: ViewGroup? =
+ view.findViewById(R.id.security_footers_container)
private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
+ private val securityFootersSeparator = View(context).apply {
+ visibility = View.GONE
+ }
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -151,6 +155,7 @@
override fun onInit() {
multiUserSwitchController.init()
+ fgsManagerFooterController.init()
}
private fun updateVisibility() {
@@ -178,9 +183,46 @@
powerMenuLite.visibility = View.GONE
}
settingsButton.setOnClickListener(onClickListener)
+ if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ val securityFooter = securityFooterController.view as DualHeightHorizontalLinearLayout
+ securityFootersContainer?.addView(securityFooter)
+ val separatorWidth = resources.getDimensionPixelSize(R.dimen.new_qs_footer_action_inset)
+ securityFootersContainer?.addView(securityFootersSeparator, separatorWidth, 1)
+ reformatForNewFooter(securityFooter)
+ val fgsFooter = fgsManagerFooterController.view
+ securityFootersContainer?.addView(fgsFooter)
+
+ val visibilityListener =
+ VisibilityChangedDispatcher.OnVisibilityChangedListener { visibility ->
+ if (visibility == View.GONE) {
+ securityFootersSeparator.visibility = View.GONE
+ } else if (securityFooter.visibility == View.VISIBLE &&
+ fgsFooter.visibility == View.VISIBLE) {
+ securityFootersSeparator.visibility = View.VISIBLE
+ } else {
+ securityFootersSeparator.visibility = View.GONE
+ }
+ fgsManagerFooterController
+ .setCollapsed(securityFooter.visibility == View.VISIBLE)
+ }
+ securityFooterController.setOnVisibilityChangedListener(visibilityListener)
+ fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener)
+ }
updateView()
}
+ private fun reformatForNewFooter(view: DualHeightHorizontalLinearLayout) {
+ // This is only necessary while things are flagged as the view could be attached in two
+ // different locations.
+ (view.layoutParams as LinearLayout.LayoutParams).apply {
+ bottomMargin = 0
+ width = 0
+ weight = 1f
+ marginEnd = resources.getDimensionPixelSize(R.dimen.new_qs_footer_action_inset)
+ }
+ view.alwaysSingleLine = true
+ }
+
private fun updateView() {
mView.updateEverything(isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled)
}
@@ -201,6 +243,10 @@
} else {
userInfoController.removeCallback(onUserInfoChangedListener)
}
+ if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ fgsManagerFooterController.setListening(listening)
+ securityFooterController.setListening(listening)
+ }
}
fun disable(state2: Int) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index 7800027..707313f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -211,9 +211,13 @@
// Some views are always full width or have dependent padding
continue;
}
- LayoutParams lp = (LayoutParams) view.getLayoutParams();
- lp.rightMargin = mSideMargins;
- lp.leftMargin = mSideMargins;
+ if (!(view instanceof FooterActionsView)) {
+ // Only padding for FooterActionsView, no margin. That way, the background goes
+ // all the way to the edge.
+ LayoutParams lp = (LayoutParams) view.getLayoutParams();
+ lp.rightMargin = mSideMargins;
+ lp.leftMargin = mSideMargins;
+ }
if (view == mQSPanelContainer) {
// QS panel lays out some of its content full width
qsPanelController.setContentMargins(mContentPadding, mContentPadding);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
index 55d4a53..0fe9095 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
@@ -20,12 +20,17 @@
import android.content.Context;
import android.view.View;
+import android.view.ViewGroup;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.TextView;
+import androidx.annotation.Nullable;
+
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.dagger.QSScope;
import java.util.concurrent.Executor;
@@ -35,9 +40,11 @@
/**
* Footer entry point for the foreground service manager
*/
+@QSScope
public class QSFgsManagerFooter implements View.OnClickListener,
FgsManagerController.OnDialogDismissedListener,
- FgsManagerController.OnNumberOfPackagesChangedListener {
+ FgsManagerController.OnNumberOfPackagesChangedListener,
+ VisibilityChangedDispatcher {
private final View mRootView;
private final TextView mFooterText;
@@ -50,20 +57,43 @@
private boolean mIsInitialized = false;
private int mNumPackages;
+ private final View mTextContainer;
+ private final View mNumberContainer;
+ private final TextView mNumberView;
+ private final ImageView mDotView;
+
+ @Nullable
+ private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener;
+
@Inject
QSFgsManagerFooter(@Named(QS_FGS_MANAGER_FOOTER_VIEW) View rootView,
@Main Executor mainExecutor, @Background Executor executor,
FgsManagerController fgsManagerController) {
mRootView = rootView;
mFooterText = mRootView.findViewById(R.id.footer_text);
- ImageView icon = mRootView.findViewById(R.id.primary_footer_icon);
- icon.setImageResource(R.drawable.ic_info_outline);
+ mTextContainer = mRootView.findViewById(R.id.fgs_text_container);
+ mNumberContainer = mRootView.findViewById(R.id.fgs_number_container);
+ mNumberView = mRootView.findViewById(R.id.fgs_number);
+ mDotView = mRootView.findViewById(R.id.fgs_new);
mContext = rootView.getContext();
mMainExecutor = mainExecutor;
mExecutor = executor;
mFgsManagerController = fgsManagerController;
}
+ /**
+ * Whether to show the footer in collapsed mode (just a number) or not (text).
+ * @param collapsed
+ */
+ public void setCollapsed(boolean collapsed) {
+ mTextContainer.setVisibility(collapsed ? View.GONE : View.VISIBLE);
+ mNumberContainer.setVisibility(collapsed ? View.VISIBLE : View.GONE);
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) mRootView.getLayoutParams();
+ lp.width = collapsed ? ViewGroup.LayoutParams.WRAP_CONTENT : 0;
+ lp.weight = collapsed ? 0f : 1f;
+ mRootView.setLayoutParams(lp);
+ }
+
public void init() {
if (mIsInitialized) {
return;
@@ -89,6 +119,12 @@
}
@Override
+ public void setOnVisibilityChangedListener(
+ @Nullable OnVisibilityChangedListener onVisibilityChangedListener) {
+ mVisibilityChangedListener = onVisibilityChangedListener;
+ }
+
+ @Override
public void onClick(View view) {
mFgsManagerController.showDialog(mRootView);
}
@@ -103,11 +139,19 @@
public void handleRefreshState() {
mMainExecutor.execute(() -> {
- mFooterText.setText(mContext.getResources().getQuantityString(
- R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages));
+ CharSequence text = mContext.getResources().getQuantityString(
+ R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages);
+ mFooterText.setText(text);
+ mNumberView.setText(Integer.toString(mNumPackages));
+ mNumberView.setContentDescription(text);
if (mFgsManagerController.shouldUpdateFooterVisibility()) {
mRootView.setVisibility(mNumPackages > 0
&& mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE);
+ mDotView.setVisibility(
+ mFgsManagerController.getChangesSinceDialog() ? View.VISIBLE : View.GONE);
+ if (mVisibilityChangedListener != null) {
+ mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
+ }
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index 6b515c8..7c04cd4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -110,6 +110,7 @@
private float mSquishinessFraction = 1f;
private final ArrayMap<View, Integer> mChildrenLayoutTop = new ArrayMap<>();
private final Rect mClippingRect = new Rect();
+ private boolean mUseNewFooter = false;
public QSPanel(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -151,6 +152,10 @@
}
}
+ void setUseNewFooter(boolean useNewFooter) {
+ mUseNewFooter = useNewFooter;
+ }
+
protected void setHorizontalContentContainerClipping() {
mHorizontalContentContainer.setClipChildren(true);
mHorizontalContentContainer.setClipToPadding(false);
@@ -368,11 +373,12 @@
protected void updatePadding() {
final Resources res = mContext.getResources();
- int padding = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
+ int paddingTop = res.getDimensionPixelSize(R.dimen.qs_panel_padding_top);
+ // Bottom padding only when there's a new footer with its height.
setPaddingRelative(getPaddingStart(),
- padding,
+ paddingTop,
getPaddingEnd(),
- res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom));
+ mUseNewFooter ? res.getDimensionPixelSize(R.dimen.qs_panel_padding_bottom) : 0);
}
void addOnConfigurationChangedListener(OnConfigurationChangedListener listener) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index 8f268b5..418c4ae 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -31,6 +31,8 @@
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.FalsingManager;
@@ -67,6 +69,7 @@
private final BrightnessController mBrightnessController;
private final BrightnessSliderController mBrightnessSliderController;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
+ private final FeatureFlags mFeatureFlags;
private boolean mGridContentVisible = true;
@@ -104,7 +107,7 @@
DumpManager dumpManager, MetricsLogger metricsLogger, UiEventLogger uiEventLogger,
QSLogger qsLogger, BrightnessController.Factory brightnessControllerFactory,
BrightnessSliderController.Factory brightnessSliderFactory,
- FalsingManager falsingManager, CommandQueue commandQueue) {
+ FalsingManager falsingManager, CommandQueue commandQueue, FeatureFlags featureFlags) {
super(view, qstileHost, qsCustomizerController, usingMediaPlayer, mediaHost,
metricsLogger, uiEventLogger, qsLogger, dumpManager);
mQSFgsManagerFooter = qsFgsManagerFooter;
@@ -114,13 +117,14 @@
mQsTileRevealControllerFactory = qsTileRevealControllerFactory;
mFalsingManager = falsingManager;
mCommandQueue = commandQueue;
- mQsSecurityFooter.setHostEnvironment(qstileHost);
mBrightnessSliderController = brightnessSliderFactory.create(getContext(), mView);
mView.setBrightnessView(mBrightnessSliderController.getRootView());
mBrightnessController = brightnessControllerFactory.create(mBrightnessSliderController);
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
+ mFeatureFlags = featureFlags;
+ view.setUseNewFooter(featureFlags.isEnabled(Flags.NEW_FOOTER));
}
@Override
@@ -150,8 +154,10 @@
refreshAllTiles();
}
mView.addOnConfigurationChangedListener(mOnConfigurationChangedListener);
- mView.setFgsManagerFooter(mQSFgsManagerFooter.getView());
- mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
+ if (!mFeatureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ mView.setSecurityFooter(mQsSecurityFooter.getView(), mShouldUseSplitNotificationShade);
+ mView.setFgsManagerFooter(mQSFgsManagerFooter.getView());
+ }
switchTileLayout(true);
mBrightnessMirrorHandler.onQsPanelAttached();
@@ -192,8 +198,10 @@
refreshAllTiles();
}
- mQSFgsManagerFooter.setListening(listening);
- mQsSecurityFooter.setListening(listening);
+ if (!mFeatureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ mQSFgsManagerFooter.setListening(listening);
+ mQsSecurityFooter.setListening(listening);
+ }
// Set the listening as soon as the QS fragment starts listening regardless of the
//expansion, so it will update the current brightness before the slider is visible.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 9e17c12..fb55cd2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -80,6 +80,7 @@
import com.android.internal.util.FrameworkStatsLog;
import com.android.systemui.FontSizeUtils;
import com.android.systemui.R;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -88,11 +89,14 @@
import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.SecurityController;
+import java.util.concurrent.atomic.AtomicBoolean;
+
import javax.inject.Inject;
import javax.inject.Named;
@QSScope
-class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener {
+class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListener,
+ VisibilityChangedDispatcher {
protected static final String TAG = "QSSecurityFooter";
protected static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean DEBUG_FORCE_VISIBLE = false;
@@ -107,11 +111,16 @@
private final ActivityStarter mActivityStarter;
private final Handler mMainHandler;
private final UserTracker mUserTracker;
+ private final DialogLaunchAnimator mDialogLaunchAnimator;
+
+ private final AtomicBoolean mShouldUseSettingsButton = new AtomicBoolean(false);
private AlertDialog mDialog;
- private QSTileHost mHost;
protected H mHandler;
+ // Does it move between footer and header? Remove this once all the flagging is removed
+ private boolean mIsMovable = true;
+
private boolean mIsVisible;
@Nullable
private CharSequence mFooterTextContent = null;
@@ -119,10 +128,14 @@
@Nullable
private Drawable mPrimaryFooterIconDrawable;
+ @Nullable
+ private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener;
+
@Inject
QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView,
UserTracker userTracker, @Main Handler mainHandler, ActivityStarter activityStarter,
- SecurityController securityController, @Background Looper bgLooper) {
+ SecurityController securityController, DialogLaunchAnimator dialogLaunchAnimator,
+ @Background Looper bgLooper) {
mRootView = rootView;
mRootView.setOnClickListener(this);
mFooterText = mRootView.findViewById(R.id.footer_text);
@@ -135,10 +148,7 @@
mSecurityController = securityController;
mHandler = new H(bgLooper);
mUserTracker = userTracker;
- }
-
- public void setHostEnvironment(QSTileHost host) {
- mHost = host;
+ mDialogLaunchAnimator = dialogLaunchAnimator;
}
public void setListening(boolean listening) {
@@ -150,23 +160,31 @@
}
}
+ @Override
+ public void setOnVisibilityChangedListener(
+ @Nullable OnVisibilityChangedListener onVisibilityChangedListener) {
+ mVisibilityChangedListener = onVisibilityChangedListener;
+ }
+
public void onConfigurationChanged() {
FontSizeUtils.updateFontSize(mFooterText, R.dimen.qs_tile_text_size);
- Resources r = mContext.getResources();
+ if (mIsMovable) {
+ Resources r = mContext.getResources();
- mFooterText.setMaxLines(r.getInteger(R.integer.qs_security_footer_maxLines));
- int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
- mRootView.setPaddingRelative(padding, padding, padding, padding);
+ mFooterText.setMaxLines(r.getInteger(R.integer.qs_security_footer_maxLines));
+ int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
+ mRootView.setPaddingRelative(padding, padding, padding, padding);
- int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
- lp.bottomMargin = bottomMargin;
- lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
- ? MATCH_PARENT : WRAP_CONTENT;
- mRootView.setLayoutParams(lp);
+ int bottomMargin = r.getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) mRootView.getLayoutParams();
+ lp.bottomMargin = bottomMargin;
+ lp.width = r.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
+ ? MATCH_PARENT : WRAP_CONTENT;
+ mRootView.setLayoutParams(lp);
+ }
mRootView.setBackground(mContext.getDrawable(R.drawable.qs_security_footer_background));
}
@@ -455,23 +473,27 @@
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_NEGATIVE) {
final Intent intent = new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS);
- mDialog.dismiss();
+ dialog.dismiss();
// This dismisses the shade on opening the activity
mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
}
}
private void createDialog() {
- mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme
- mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
- mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
- mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getNegativeButton(), this);
+ mShouldUseSettingsButton.set(false);
+ final View view = createDialogView();
+ mMainHandler.post(() -> {
+ mDialog = new SystemUIDialog(mContext, 0); // Use mContext theme
+ mDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
+ mDialog.setButton(DialogInterface.BUTTON_POSITIVE, getPositiveButton(), this);
+ mDialog.setButton(DialogInterface.BUTTON_NEGATIVE,
+ mShouldUseSettingsButton.get() ? getSettingsButton() : getNegativeButton(),
+ this);
- mDialog.setView(createDialogView());
+ mDialog.setView(view);
- mDialog.show();
- mDialog.getWindow().setLayout(MATCH_PARENT,
- ViewGroup.LayoutParams.WRAP_CONTENT);
+ mDialogLaunchAnimator.showFromView(mDialog, mRootView);
+ });
}
@VisibleForTesting
@@ -510,7 +532,7 @@
TextView deviceManagementWarning =
(TextView) dialogView.findViewById(R.id.device_management_warning);
deviceManagementWarning.setText(managementMessage);
- mDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getSettingsButton(), this);
+ mShouldUseSettingsButton.set(true);
}
// ca certificate section
@@ -782,6 +804,9 @@
mFooterText.setText(mFooterTextContent);
}
mRootView.setVisibility(mIsVisible || DEBUG_FORCE_VISIBLE ? View.VISIBLE : View.GONE);
+ if (mVisibilityChangedListener != null) {
+ mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
+ }
}
};
@@ -814,7 +839,6 @@
} catch (Throwable t) {
final String error = "Error in " + name;
Log.w(TAG, error, t);
- mHost.warn(error, t);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/VisibilityChangedDispatcher.kt b/packages/SystemUI/src/com/android/systemui/qs/VisibilityChangedDispatcher.kt
new file mode 100644
index 0000000..73362ce
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/VisibilityChangedDispatcher.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2022 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.qs
+
+/**
+ * Dispatches events that set the visibility from the controller.
+ */
+interface VisibilityChangedDispatcher {
+
+ fun setOnVisibilityChangedListener(onVisibilityChangedListener: OnVisibilityChangedListener?)
+
+ fun interface OnVisibilityChangedListener {
+ fun onVisibilityChanged(visibility: Int)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index fdf9ae0..2780b16 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -202,6 +202,6 @@
@QSThemedContext LayoutInflater layoutInflater,
QSPanel qsPanel
) {
- return layoutInflater.inflate(R.layout.quick_settings_security_footer, qsPanel, false);
+ return layoutInflater.inflate(R.layout.fgs_footer, qsPanel, false);
}
}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
index b457ebf..7c9e597 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -102,17 +102,21 @@
private fun updateBottomSpacing() {
val (containerPadding, notificationsMargin) = calculateBottomSpacing()
var qsScrollPaddingBottom = 0
- if (!(splitShadeEnabled || isQSCustomizing || isQSDetailShowing || isGestureNavigation ||
- taskbarVisible)) {
+ val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
+ if (!newFooter && !(splitShadeEnabled || isQSCustomizing || isQSDetailShowing ||
+ isGestureNavigation || taskbarVisible)) {
// no taskbar, portrait, navigation buttons enabled:
// padding is needed so QS can scroll up over bottom insets - to reach the point when
// the whole QS is above bottom insets
qsScrollPaddingBottom = bottomStableInsets
+ } else if (newFooter && !(isQSCustomizing || isQSDetailShowing)) {
+ // With the new footer, we also want this padding in the bottom in these cases
+ qsScrollPaddingBottom = bottomStableInsets
}
mView.setPadding(0, 0, 0, containerPadding)
mView.setNotificationsMarginBottom(notificationsMargin)
- if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
- mView.setQSContainerPaddingBottom(notificationsMargin)
+ if (newFooter) {
+ mView.setQSContainerPaddingBottom(qsScrollPaddingBottom)
} else {
mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt b/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt
index 0e04871..cfceefa 100644
--- a/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/DualHeightHorizontalLinearLayout.kt
@@ -65,6 +65,17 @@
private var initialPadding = mPaddingTop // All vertical padding is the same
+ private var originalMaxLines = 1
+ var alwaysSingleLine: Boolean = false
+ set(value) {
+ field = value
+ if (field) {
+ textView?.setSingleLine()
+ } else {
+ textView?.maxLines = originalMaxLines
+ }
+ }
+
init {
if (orientation != HORIZONTAL) {
throw IllegalStateException("This view should always have horizontal orientation")
@@ -120,7 +131,7 @@
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
textView?.let { tv ->
- if (tv.lineCount < 2) {
+ if (tv.lineCount < 2 || alwaysSingleLine) {
setMeasuredDimension(measuredWidth, singleLineHeightPx)
mPaddingBottom = 0
mPaddingTop = 0
@@ -133,7 +144,9 @@
override fun onFinishInflate() {
super.onFinishInflate()
- textView = findViewById(textViewId)
+ textView = findViewById<TextView>(textViewId)?.also {
+ originalMaxLines = it.maxLines
+ }
}
override fun onConfigurationChanged(newConfig: Configuration?) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index f5fa0d0..91a9f9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -64,6 +64,10 @@
private lateinit var uiEventLogger: UiEventLogger
@Mock
private lateinit var featureFlags: FeatureFlags
+ @Mock
+ private lateinit var securityFooterController: QSSecurityFooter
+ @Mock
+ private lateinit var fgsManagerController: QSFgsManagerFooter
private lateinit var controller: FooterActionsController
@@ -90,7 +94,8 @@
controller = FooterActionsController(view, multiUserSwitchControllerFactory,
activityStarter, userManager, userTracker, userInfoController,
- deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
+ deviceProvisionedController, securityFooterController, fgsManagerController,
+ falsingManager, metricsLogger, fakeTunerService,
globalActionsDialog, uiEventLogger, showPMLiteButton = true, fakeSettings,
Handler(testableLooper.looper), featureFlags)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
index b5ce706..b2ca62f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelControllerTest.java
@@ -36,6 +36,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.customize.QSCustomizerController;
@@ -106,6 +107,8 @@
Resources mResources;
@Mock
Configuration mConfiguration;
+ @Mock
+ FeatureFlags mFeatureFlags;
private QSPanelController mController;
@@ -133,7 +136,7 @@
mTunerService, mQSTileHost, mQSCustomizerController, true, mMediaHost,
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
- mFalsingManager, mCommandQueue
+ mFalsingManager, mCommandQueue, mFeatureFlags
);
mController.init();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index 3500c18..4ae19332 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -162,6 +162,20 @@
assertThat(mQsPanel.indexOfChild(mQsPanel.mSecurityFooter)).isEqualTo(-1)
}
+ @Test
+ fun testBottomPadding() {
+ mQsPanel.setUseNewFooter(false)
+
+ mQsPanel.updatePadding()
+ assertThat(mQsPanel.paddingBottom).isEqualTo(0)
+
+ mQsPanel.setUseNewFooter(true)
+
+ mQsPanel.updatePadding()
+ assertThat(mQsPanel.paddingBottom)
+ .isEqualTo(mContext.resources.getDimensionPixelSize(R.dimen.new_footer_height))
+ }
+
private fun getNewOrientationConfig(@Configuration.Orientation newOrientation: Int) =
context.resources.configuration.apply { orientation = newOrientation }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 770cf2c..2b7fa42 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -22,12 +22,14 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.pm.UserInfo;
@@ -50,6 +52,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.policy.SecurityController;
@@ -57,10 +60,13 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.atomic.AtomicInteger;
+
/*
* Compile and run the whole SystemUI test suite:
runtest --path frameworks/base/packages/SystemUI/tests
@@ -94,20 +100,24 @@
private UserTracker mUserTracker;
@Mock
private ActivityStarter mActivityStarter;
+ @Mock
+ private DialogLaunchAnimator mDialogLaunchAnimator;
+
+ private TestableLooper mTestableLooper;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- Looper looper = TestableLooper.get(this).getLooper();
+ mTestableLooper = TestableLooper.get(this);
+ Looper looper = mTestableLooper.getLooper();
when(mUserTracker.getUserInfo()).thenReturn(mock(UserInfo.class));
mRootView = (ViewGroup) new LayoutInflaterBuilder(mContext)
.replace("ImageView", TestableImageView.class)
.build().inflate(R.layout.quick_settings_security_footer, null, false);
mFooter = new QSSecurityFooter(mRootView, mUserTracker, new Handler(looper),
- mActivityStarter, mSecurityController, looper);
+ mActivityStarter, mSecurityController, mDialogLaunchAnimator, looper);
mFooterText = mRootView.findViewById(R.id.footer_text);
mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
- mFooter.setHostEnvironment(null);
when(mSecurityController.getDeviceOwnerComponentOnAnyUser())
.thenReturn(DEVICE_OWNER_COMPONENT);
@@ -650,8 +660,6 @@
@Test
public void testNoClickWhenGone() {
- QSTileHost mockHost = mock(QSTileHost.class);
- mFooter.setHostEnvironment(mockHost);
mFooter.refreshState();
TestableLooper.get(this).processAllMessages();
@@ -660,7 +668,7 @@
mFooter.onClick(mFooter.getView());
// Proxy for dialog being created
- verify(mockHost, never()).collapsePanels();
+ verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
}
@Test
@@ -700,6 +708,16 @@
}
@Test
+ public void testDialogUsesDialogLauncher() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ mFooter.onClick(mRootView);
+
+ mTestableLooper.processAllMessages();
+
+ verify(mDialogLaunchAnimator).showFromView(any(), eq(mRootView));
+ }
+
+ @Test
public void testCreateDialogViewForFinancedDevice() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerOrganizationName())
@@ -707,12 +725,6 @@
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
- // Initialize AlertDialog which sets the text for the negative button, which is used when
- // creating the dialog for a financed device.
- mFooter.showDeviceMonitoringDialog();
- // The above statement would display the Quick Settings dialog which requires user input,
- // so simulate the press to continue with the unit test (otherwise, it is stuck).
- mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE);
View view = mFooter.createDialogView();
TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle);
@@ -727,6 +739,49 @@
mFooter.getSettingsButton());
}
+ @Test
+ public void testFinancedDeviceUsesSettingsButtonText() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ mFooter.showDeviceMonitoringDialog();
+ ArgumentCaptor<AlertDialog> dialogCaptor = ArgumentCaptor.forClass(AlertDialog.class);
+
+ mTestableLooper.processAllMessages();
+ verify(mDialogLaunchAnimator).showFromView(dialogCaptor.capture(), any());
+
+ AlertDialog dialog = dialogCaptor.getValue();
+ dialog.create();
+
+ assertEquals(mFooter.getSettingsButton(),
+ dialog.getButton(DialogInterface.BUTTON_NEGATIVE).getText());
+
+ dialog.dismiss();
+ }
+
+ @Test
+ public void testVisibilityListener() {
+ final AtomicInteger lastVisibility = new AtomicInteger(-1);
+ VisibilityChangedDispatcher.OnVisibilityChangedListener listener =
+ (VisibilityChangedDispatcher.OnVisibilityChangedListener) lastVisibility::set;
+
+ mFooter.setOnVisibilityChangedListener(listener);
+
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ mFooter.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(View.VISIBLE, lastVisibility.get());
+
+ when(mSecurityController.isDeviceManaged()).thenReturn(false);
+ mFooter.refreshState();
+ mTestableLooper.processAllMessages();
+ assertEquals(View.GONE, lastVisibility.get());
+ }
+
+
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index bbb2346..00af446 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -114,13 +114,15 @@
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
- expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = STABLE_INSET_BOTTOM,
- expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -150,13 +152,15 @@
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
- then(expectedContainerPadding = 0)
+ then(expectedContainerPadding = 0,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
- expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -190,7 +194,8 @@
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withCutout().withStableBottom())
then(expectedContainerPadding = 0,
- expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -217,12 +222,14 @@
given(taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
- then(expectedContainerPadding = 0)
+ then(expectedContainerPadding = 0,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = true,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
- then(expectedContainerPadding = STABLE_INSET_BOTTOM)
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM,
+ expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
@@ -259,7 +266,7 @@
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withCutout().withStableBottom())
- then(expectedContainerPadding = CUTOUT_HEIGHT)
+ then(expectedContainerPadding = CUTOUT_HEIGHT, expectedQsPadding = STABLE_INSET_BOTTOM)
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
@@ -350,6 +357,7 @@
@Test
fun testDetailShowingInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
+ notificationsQSContainerController.setDetailShowing(true)
useNewFooter(false)
given(taskbarVisible = false,
@@ -357,7 +365,6 @@
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
- notificationsQSContainerController.setDetailShowing(true)
// should not influence spacing
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
@@ -368,6 +375,7 @@
@Test
fun testDetailShowingInSplitShade_newFooter() {
notificationsQSContainerController.splitShadeEnabled = true
+ notificationsQSContainerController.setDetailShowing(true)
useNewFooter(true)
given(taskbarVisible = false,
@@ -375,7 +383,6 @@
insets = windowInsets().withStableBottom())
then(expectedContainerPadding = 0)
- notificationsQSContainerController.setDetailShowing(true)
// should not influence spacing
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
@@ -417,7 +424,7 @@
val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
if (newFooter) {
verify(notificationsQSContainer)
- .setQSContainerPaddingBottom(expectedNotificationsMargin)
+ .setQSContainerPaddingBottom(expectedQsPadding)
} else {
verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
}