Merge changes Id9289826,If9cc89df
* changes: Put security footers in new footer Implement correct dimens for new footer
This commit is contained in:
committed by
Android (Google) Code Review
commit
b42de79509
95
packages/SystemUI/res-keyguard/layout/fgs_footer.xml
Normal file
95
packages/SystemUI/res-keyguard/layout/fgs_footer.xml
Normal file
@@ -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>
|
||||
@@ -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" />
|
||||
|
||||
22
packages/SystemUI/res/drawable/new_fgs_dot.xml
Normal file
22
packages/SystemUI/res/drawable/new_fgs_dot.xml
Normal file
@@ -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>
|
||||
@@ -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">
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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]-->
|
||||
|
||||
@@ -40,6 +40,7 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
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 java.util.concurrent.Executor
|
||||
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 @@ class FgsManagerController @Inject constructor(
|
||||
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 @@ class FgsManagerController @Inject constructor(
|
||||
val numPackagesAfter = getNumRunningPackagesLocked()
|
||||
|
||||
if (numPackagesAfter != numPackagesBefore) {
|
||||
changesSinceDialog = true
|
||||
onNumberOfPackagesChangedListeners.forEach {
|
||||
backgroundExecutor.execute { it.onNumberOfPackagesChanged(numPackagesAfter) }
|
||||
}
|
||||
@@ -210,6 +216,7 @@ class FgsManagerController @Inject constructor(
|
||||
this.dialog = dialog
|
||||
|
||||
dialog.setOnDismissListener {
|
||||
changesSinceDialog = false
|
||||
synchronized(lock) {
|
||||
this.dialog = null
|
||||
updateAppItemsLocked()
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.os.UserManager
|
||||
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.MetricsLogger
|
||||
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.DeviceProvisionedController
|
||||
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 @@ import javax.inject.Named
|
||||
* 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 @@ class FooterActionsController @Inject constructor(
|
||||
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 @@ class FooterActionsController @Inject constructor(
|
||||
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 @@ class FooterActionsController @Inject constructor(
|
||||
|
||||
override fun onInit() {
|
||||
multiUserSwitchController.init()
|
||||
fgsManagerFooterController.init()
|
||||
}
|
||||
|
||||
private fun updateVisibility() {
|
||||
@@ -178,9 +183,46 @@ class FooterActionsController @Inject constructor(
|
||||
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 @@ class FooterActionsController @Inject constructor(
|
||||
} else {
|
||||
userInfoController.removeCallback(onUserInfoChangedListener)
|
||||
}
|
||||
if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
|
||||
fgsManagerFooterController.setListening(listening)
|
||||
securityFooterController.setListening(listening)
|
||||
}
|
||||
}
|
||||
|
||||
fun disable(state2: Int) {
|
||||
|
||||
@@ -211,9 +211,13 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
|
||||
// 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);
|
||||
|
||||
@@ -20,12 +20,17 @@ import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOO
|
||||
|
||||
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 @@ import javax.inject.Named;
|
||||
/**
|
||||
* 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 @@ public class QSFgsManagerFooter implements View.OnClickListener,
|
||||
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;
|
||||
@@ -88,6 +118,12 @@ public class QSFgsManagerFooter implements View.OnClickListener,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnVisibilityChangedListener(
|
||||
@Nullable OnVisibilityChangedListener onVisibilityChangedListener) {
|
||||
mVisibilityChangedListener = onVisibilityChangedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mFgsManagerController.showDialog(mRootView);
|
||||
@@ -103,11 +139,19 @@ public class QSFgsManagerFooter implements View.OnClickListener,
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -110,6 +110,7 @@ public class QSPanel extends LinearLayout implements Tunable {
|
||||
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 @@ public class QSPanel extends LinearLayout implements Tunable {
|
||||
}
|
||||
}
|
||||
|
||||
void setUseNewFooter(boolean useNewFooter) {
|
||||
mUseNewFooter = useNewFooter;
|
||||
}
|
||||
|
||||
protected void setHorizontalContentContainerClipping() {
|
||||
mHorizontalContentContainer.setClipChildren(true);
|
||||
mHorizontalContentContainer.setClipToPadding(false);
|
||||
@@ -368,11 +373,12 @@ public class QSPanel extends LinearLayout implements Tunable {
|
||||
|
||||
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) {
|
||||
|
||||
@@ -31,6 +31,8 @@ import com.android.internal.logging.MetricsLogger;
|
||||
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 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
private final BrightnessController mBrightnessController;
|
||||
private final BrightnessSliderController mBrightnessSliderController;
|
||||
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
private boolean mGridContentVisible = true;
|
||||
|
||||
@@ -104,7 +107,7 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
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 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
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 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
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 @@ public class QSPanelController extends QSPanelControllerBase<QSPanel> {
|
||||
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.
|
||||
|
||||
@@ -80,6 +80,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
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.settings.UserTracker;
|
||||
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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
@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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
}
|
||||
}
|
||||
|
||||
@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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
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 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
mFooterText.setText(mFooterTextContent);
|
||||
}
|
||||
mRootView.setVisibility(mIsVisible || DEBUG_FORCE_VISIBLE ? View.VISIBLE : View.GONE);
|
||||
if (mVisibilityChangedListener != null) {
|
||||
mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -814,7 +839,6 @@ class QSSecurityFooter implements OnClickListener, DialogInterface.OnClickListen
|
||||
} catch (Throwable t) {
|
||||
final String error = "Error in " + name;
|
||||
Log.w(TAG, error, t);
|
||||
mHost.warn(error, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,6 @@ public interface QSFragmentModule {
|
||||
@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);
|
||||
}
|
||||
}
|
||||
@@ -102,17 +102,21 @@ class NotificationsQSContainerController @Inject constructor(
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -65,6 +65,17 @@ class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
|
||||
|
||||
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 @@ class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
|
||||
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 @@ class DualHeightHorizontalLinearLayout @JvmOverloads constructor(
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
textView = findViewById(textViewId)
|
||||
textView = findViewById<TextView>(textViewId)?.also {
|
||||
originalMaxLines = it.maxLines
|
||||
}
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration?) {
|
||||
|
||||
@@ -64,6 +64,10 @@ class FooterActionsControllerTest : LeakCheckedTest() {
|
||||
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 @@ class FooterActionsControllerTest : LeakCheckedTest() {
|
||||
|
||||
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()
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.android.internal.logging.testing.UiEventLoggerFake;
|
||||
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 @@ public class QSPanelControllerTest extends SysuiTestCase {
|
||||
Resources mResources;
|
||||
@Mock
|
||||
Configuration mConfiguration;
|
||||
@Mock
|
||||
FeatureFlags mFeatureFlags;
|
||||
|
||||
private QSPanelController mController;
|
||||
|
||||
@@ -133,7 +136,7 @@ public class QSPanelControllerTest extends SysuiTestCase {
|
||||
mTunerService, mQSTileHost, mQSCustomizerController, true, mMediaHost,
|
||||
mQSTileRevealControllerFactory, mDumpManager, mMetricsLogger, mUiEventLogger,
|
||||
mQSLogger, mBrightnessControllerFactory, mToggleSliderViewControllerFactory,
|
||||
mFalsingManager, mCommandQueue
|
||||
mFalsingManager, mCommandQueue, mFeatureFlags
|
||||
);
|
||||
|
||||
mController.init();
|
||||
|
||||
@@ -162,6 +162,20 @@ class QSPanelTest : SysuiTestCase() {
|
||||
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 }
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@ import static junit.framework.Assert.assertNotNull;
|
||||
|
||||
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 android.widget.TextView;
|
||||
|
||||
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 com.android.systemui.statusbar.policy.SecurityController;
|
||||
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 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
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 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testNoClickWhenGone() {
|
||||
QSTileHost mockHost = mock(QSTileHost.class);
|
||||
mFooter.setHostEnvironment(mockHost);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
@@ -660,7 +668,7 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
mFooter.onClick(mFooter.getView());
|
||||
|
||||
// Proxy for dialog being created
|
||||
verify(mockHost, never()).collapsePanels();
|
||||
verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -699,6 +707,16 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
|
||||
}
|
||||
|
||||
@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);
|
||||
@@ -707,12 +725,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
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 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
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);
|
||||
|
||||
@@ -114,13 +114,15 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
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 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
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 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
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 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
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 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
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 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testDetailShowingInSplitShade() {
|
||||
notificationsQSContainerController.splitShadeEnabled = true
|
||||
notificationsQSContainerController.setDetailShowing(true)
|
||||
useNewFooter(false)
|
||||
|
||||
given(taskbarVisible = false,
|
||||
@@ -357,7 +365,6 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
insets = windowInsets().withStableBottom())
|
||||
then(expectedContainerPadding = 0)
|
||||
|
||||
notificationsQSContainerController.setDetailShowing(true)
|
||||
// should not influence spacing
|
||||
given(taskbarVisible = false,
|
||||
navigationMode = BUTTONS_NAVIGATION,
|
||||
@@ -368,6 +375,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun testDetailShowingInSplitShade_newFooter() {
|
||||
notificationsQSContainerController.splitShadeEnabled = true
|
||||
notificationsQSContainerController.setDetailShowing(true)
|
||||
useNewFooter(true)
|
||||
|
||||
given(taskbarVisible = false,
|
||||
@@ -375,7 +383,6 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
insets = windowInsets().withStableBottom())
|
||||
then(expectedContainerPadding = 0)
|
||||
|
||||
notificationsQSContainerController.setDetailShowing(true)
|
||||
// should not influence spacing
|
||||
given(taskbarVisible = false,
|
||||
navigationMode = BUTTONS_NAVIGATION,
|
||||
@@ -417,7 +424,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() {
|
||||
val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
|
||||
if (newFooter) {
|
||||
verify(notificationsQSContainer)
|
||||
.setQSContainerPaddingBottom(expectedNotificationsMargin)
|
||||
.setQSContainerPaddingBottom(expectedQsPadding)
|
||||
} else {
|
||||
verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user