Merge "Extracting buttons below expanded QS to a separate view and controller" into sc-v2-dev am: 0f7c56a233
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15425036 Change-Id: I0a04b906b19eaf77d149b95e27096103b924069e
This commit is contained in:
104
packages/SystemUI/res-keyguard/layout/qs_footer_actions.xml
Normal file
104
packages/SystemUI/res-keyguard/layout/qs_footer_actions.xml
Normal file
@@ -0,0 +1,104 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
** Copyright 2021, 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.
|
||||
-->
|
||||
<com.android.systemui.qs.QSFooterActionsView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/qs_footer_actions_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@android:id/edit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:contentDescription="@string/accessibility_quick_settings_edit"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/qs_footer_icon_padding"
|
||||
android:src="@*android:drawable/ic_mode_edit"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.phone.MultiUserSwitch
|
||||
android:id="@+id/multi_user_switch"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
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_gravity="center"
|
||||
android:scaleType="centerInside" />
|
||||
</com.android.systemui.statusbar.phone.MultiUserSwitch>
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@+id/pm_lite"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/qs_footer_icon_padding"
|
||||
android:src="@*android:drawable/ic_lock_power_off"
|
||||
android:contentDescription="@string/accessibility_quick_settings_power_menu"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
|
||||
android:id="@+id/settings_button_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
android:layout_weight="1"
|
||||
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_gravity="center"
|
||||
android:contentDescription="@string/accessibility_quick_settings_settings"
|
||||
android:background="@drawable/qs_footer_action_chip_background_borderless"
|
||||
android:padding="@dimen/qs_footer_icon_padding"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_settings"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@+id/tuner_icon"
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_marginBottom="@dimen/qs_footer_icon_padding"
|
||||
android:src="@drawable/tuner"
|
||||
android:tint="?android:attr/textColorTertiary"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
|
||||
|
||||
</com.android.systemui.qs.QSFooterActionsView>
|
||||
@@ -68,93 +68,8 @@
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/qs_footer_actions_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:gravity="center_vertical">
|
||||
<include layout="@layout/qs_footer_actions"/>
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@android:id/edit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:contentDescription="@string/accessibility_quick_settings_edit"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/qs_footer_icon_padding"
|
||||
android:src="@*android:drawable/ic_mode_edit"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.phone.MultiUserSwitch
|
||||
android:id="@+id/multi_user_switch"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
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_gravity="center"
|
||||
android:scaleType="centerInside" />
|
||||
</com.android.systemui.statusbar.phone.MultiUserSwitch>
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@+id/pm_lite"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
android:clickable="true"
|
||||
android:clipToPadding="false"
|
||||
android:focusable="true"
|
||||
android:padding="@dimen/qs_footer_icon_padding"
|
||||
android:src="@*android:drawable/ic_lock_power_off"
|
||||
android:contentDescription="@string/accessibility_quick_settings_power_menu"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
|
||||
android:id="@+id/settings_button_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/qs_footer_action_button_size"
|
||||
android:background="@drawable/qs_footer_action_chip_background"
|
||||
android:layout_weight="1"
|
||||
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_gravity="center"
|
||||
android:contentDescription="@string/accessibility_quick_settings_settings"
|
||||
android:background="@drawable/qs_footer_action_chip_background_borderless"
|
||||
android:padding="@dimen/qs_footer_icon_padding"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_settings"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@+id/tuner_icon"
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="8dp"
|
||||
android:layout_gravity="center_horizontal|bottom"
|
||||
android:layout_marginBottom="@dimen/qs_footer_icon_padding"
|
||||
android:src="@drawable/tuner"
|
||||
android:tint="?android:attr/textColorTertiary"
|
||||
android:visibility="invisible" />
|
||||
|
||||
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</com.android.systemui.qs.QSFooterView>
|
||||
|
||||
@@ -34,11 +34,6 @@ public interface QSFooter {
|
||||
*/
|
||||
void setExpanded(boolean expanded);
|
||||
|
||||
/**
|
||||
* Returns the full height of the footer.
|
||||
*/
|
||||
int getHeight();
|
||||
|
||||
/**
|
||||
* Sets the percentage amount that the quick settings has been expanded.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.android.systemui.qs
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.UserManager
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.internal.jank.InteractionJankMonitor
|
||||
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.systemui.R
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator
|
||||
import com.android.systemui.globalactions.GlobalActionsDialogLite
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.plugins.FalsingManager
|
||||
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitchController
|
||||
import com.android.systemui.statusbar.phone.SettingsButton
|
||||
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.ViewController
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
class QSFooterActionsController @Inject constructor(
|
||||
view: QSFooterActionsView,
|
||||
private val qsPanelController: QSPanelController,
|
||||
private val activityStarter: ActivityStarter,
|
||||
private val userManager: UserManager,
|
||||
private val userInfoController: UserInfoController,
|
||||
private val multiUserSwitchController: MultiUserSwitchController,
|
||||
private val deviceProvisionedController: DeviceProvisionedController,
|
||||
private val falsingManager: FalsingManager,
|
||||
private val metricsLogger: MetricsLogger,
|
||||
private val tunerService: TunerService,
|
||||
private val globalActionsDialog: GlobalActionsDialogLite,
|
||||
private val uiEventLogger: UiEventLogger,
|
||||
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean
|
||||
) : ViewController<QSFooterActionsView>(view) {
|
||||
|
||||
private var listening: Boolean = false
|
||||
var expanded = false
|
||||
set(value) {
|
||||
field = value
|
||||
mView.setExpanded(value, isTunerEnabled(),
|
||||
multiUserSwitchController.isMultiUserEnabled)
|
||||
}
|
||||
|
||||
private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
|
||||
private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
|
||||
private val editButton: View = view.findViewById(android.R.id.edit)
|
||||
private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
|
||||
|
||||
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
|
||||
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
|
||||
mView.onUserInfoChanged(picture, isGuestUser)
|
||||
}
|
||||
|
||||
private val onClickListener = View.OnClickListener { v ->
|
||||
// Don't do anything until views are unhidden. Don't do anything if the tap looks
|
||||
// suspicious.
|
||||
if (!expanded || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
return@OnClickListener
|
||||
}
|
||||
if (v === settingsButton) {
|
||||
if (!deviceProvisionedController.isCurrentUserSetup) {
|
||||
// If user isn't setup just unlock the device and dump them back at SUW.
|
||||
activityStarter.postQSRunnableDismissingKeyguard {}
|
||||
return@OnClickListener
|
||||
}
|
||||
metricsLogger.action(
|
||||
if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
|
||||
else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH)
|
||||
if (settingsButton.isTunerClick) {
|
||||
activityStarter.postQSRunnableDismissingKeyguard {
|
||||
if (isTunerEnabled()) {
|
||||
tunerService.showResetRequest {
|
||||
// Relaunch settings so that the tuner disappears.
|
||||
startSettingsActivity()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(context, R.string.tuner_toast, Toast.LENGTH_LONG).show()
|
||||
tunerService.isTunerEnabled = true
|
||||
}
|
||||
startSettingsActivity()
|
||||
}
|
||||
} else {
|
||||
startSettingsActivity()
|
||||
}
|
||||
} else if (v === powerMenuLite) {
|
||||
uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
|
||||
globalActionsDialog.showOrHideDialog(false, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
multiUserSwitchController.init()
|
||||
}
|
||||
|
||||
private fun startSettingsActivity() {
|
||||
val animationController = settingsButtonContainer?.let {
|
||||
ActivityLaunchAnimator.Controller.fromView(
|
||||
it,
|
||||
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON)
|
||||
}
|
||||
activityStarter.startActivity(Intent(Settings.ACTION_SETTINGS),
|
||||
true /* dismissShade */, animationController)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public override fun onViewAttached() {
|
||||
if (showPMLiteButton) {
|
||||
powerMenuLite.visibility = View.VISIBLE
|
||||
powerMenuLite.setOnClickListener(onClickListener)
|
||||
} else {
|
||||
powerMenuLite.visibility = View.GONE
|
||||
}
|
||||
settingsButton.setOnClickListener(onClickListener)
|
||||
editButton.setOnClickListener(View.OnClickListener { view: View? ->
|
||||
if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
return@OnClickListener
|
||||
}
|
||||
activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) }
|
||||
})
|
||||
|
||||
mView.updateEverything(isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled)
|
||||
}
|
||||
|
||||
override fun onViewDetached() {
|
||||
setListening(false)
|
||||
}
|
||||
|
||||
fun setListening(listening: Boolean) {
|
||||
if (this.listening == listening) {
|
||||
return
|
||||
}
|
||||
this.listening = listening
|
||||
if (this.listening) {
|
||||
userInfoController.addCallback(onUserInfoChangedListener)
|
||||
} else {
|
||||
userInfoController.removeCallback(onUserInfoChangedListener)
|
||||
}
|
||||
}
|
||||
|
||||
fun disable(state2: Int) {
|
||||
mView.disable(state2, isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled)
|
||||
}
|
||||
|
||||
fun setExpansion(headerExpansionFraction: Float) {
|
||||
mView.setExpansion(headerExpansionFraction)
|
||||
}
|
||||
|
||||
fun updateAnimator(width: Int, numTiles: Int) {
|
||||
mView.updateAnimator(width, numTiles)
|
||||
}
|
||||
|
||||
fun setKeyguardShowing() {
|
||||
mView.setKeyguardShowing()
|
||||
}
|
||||
|
||||
private fun isTunerEnabled() = tunerService.isTunerEnabled
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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
|
||||
|
||||
import android.app.StatusBarManager
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.RippleDrawable
|
||||
import android.os.UserManager
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.settingslib.drawable.UserIconDrawable
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitch
|
||||
import com.android.systemui.statusbar.phone.SettingsButton
|
||||
|
||||
/**
|
||||
* Quick Settings bottom buttons placed in footer (aka utility bar) - always visible in expanded QS,
|
||||
* in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
|
||||
* edit tiles, power off and conditionally: user switch and tuner
|
||||
*/
|
||||
class QSFooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
|
||||
private lateinit var settingsContainer: View
|
||||
private lateinit var settingsButton: SettingsButton
|
||||
private lateinit var multiUserSwitch: MultiUserSwitch
|
||||
private lateinit var multiUserAvatar: ImageView
|
||||
private lateinit var tunerIcon: View
|
||||
private lateinit var editTilesButton: View
|
||||
|
||||
private var settingsCogAnimator: TouchAnimator? = null
|
||||
|
||||
private var qsDisabled = false
|
||||
private var isExpanded = false
|
||||
private var expansionAmount = 0f
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
editTilesButton = requireViewById(android.R.id.edit)
|
||||
settingsButton = findViewById(R.id.settings_button)
|
||||
settingsContainer = findViewById(R.id.settings_button_container)
|
||||
multiUserSwitch = findViewById(R.id.multi_user_switch)
|
||||
multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar)
|
||||
tunerIcon = requireViewById(R.id.tuner_icon)
|
||||
|
||||
// RenderThread is doing more harm than good when touching the header (to expand quick
|
||||
// settings), so disable it for this view
|
||||
if (settingsButton.background is RippleDrawable) {
|
||||
(settingsButton.background as RippleDrawable).setForceSoftware(true)
|
||||
}
|
||||
updateResources()
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
}
|
||||
|
||||
fun updateAnimator(width: Int, numTiles: Int) {
|
||||
val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
|
||||
mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
|
||||
val remaining = (width - numTiles * size) / (numTiles - 1)
|
||||
val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
|
||||
val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
|
||||
settingsCogAnimator = TouchAnimator.Builder()
|
||||
.addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
|
||||
.addFloat(settingsButton, "rotation", -120f, 0f)
|
||||
.build()
|
||||
setExpansion(expansionAmount)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
updateResources()
|
||||
}
|
||||
|
||||
override fun onRtlPropertiesChanged(layoutDirection: Int) {
|
||||
super.onRtlPropertiesChanged(layoutDirection)
|
||||
updateResources()
|
||||
}
|
||||
|
||||
private fun updateResources() {
|
||||
val tunerIconTranslation = mContext.resources
|
||||
.getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation).toFloat()
|
||||
tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
|
||||
}
|
||||
|
||||
fun setKeyguardShowing() {
|
||||
setExpansion(expansionAmount)
|
||||
}
|
||||
|
||||
fun setExpanded(expanded: Boolean, isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
|
||||
if (isExpanded == expanded) return
|
||||
isExpanded = expanded
|
||||
updateEverything(isTunerEnabled, multiUserEnabled)
|
||||
}
|
||||
|
||||
fun setExpansion(headerExpansionFraction: Float) {
|
||||
expansionAmount = headerExpansionFraction
|
||||
if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
|
||||
}
|
||||
|
||||
fun disable(state2: Int, isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
|
||||
val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
|
||||
if (disabled == qsDisabled) return
|
||||
qsDisabled = disabled
|
||||
updateEverything(isTunerEnabled, multiUserEnabled)
|
||||
}
|
||||
|
||||
fun updateEverything(isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
|
||||
post {
|
||||
updateVisibilities(isTunerEnabled, multiUserEnabled)
|
||||
updateClickabilities()
|
||||
isClickable = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateClickabilities() {
|
||||
multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
|
||||
editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
|
||||
settingsButton.isClickable = settingsButton.visibility == VISIBLE
|
||||
}
|
||||
|
||||
private fun updateVisibilities(isTunerEnabled: Boolean, multiUserEnabled: Boolean) {
|
||||
settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE
|
||||
tunerIcon.visibility = if (isTunerEnabled) VISIBLE else INVISIBLE
|
||||
multiUserSwitch.visibility = if (showUserSwitcher(multiUserEnabled)) VISIBLE else GONE
|
||||
val isDemo = UserManager.isDeviceInDemoMode(context)
|
||||
settingsButton.visibility = if (isDemo || !isExpanded) INVISIBLE else VISIBLE
|
||||
}
|
||||
|
||||
private fun showUserSwitcher(multiUserEnabled: Boolean): Boolean {
|
||||
return isExpanded && multiUserEnabled
|
||||
}
|
||||
|
||||
fun onUserInfoChanged(picture: Drawable?, isGuestUser: Boolean) {
|
||||
var pictureToSet = picture
|
||||
if (picture != null && isGuestUser && picture !is UserIconDrawable) {
|
||||
pictureToSet = picture.constantState.newDrawable(resources).mutate()
|
||||
pictureToSet.setColorFilter(
|
||||
Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
|
||||
PorterDuff.Mode.SRC_IN)
|
||||
}
|
||||
multiUserAvatar.setImageDrawable(pictureToSet)
|
||||
}
|
||||
}
|
||||
@@ -21,60 +21,40 @@ import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.accessibility.AccessibilityNodeInfo;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.settingslib.Utils;
|
||||
import com.android.settingslib.development.DevelopmentSettingsEnabler;
|
||||
import com.android.settingslib.drawable.UserIconDrawable;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.qs.TouchAnimator.Builder;
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitch;
|
||||
import com.android.systemui.statusbar.phone.SettingsButton;
|
||||
|
||||
/** */
|
||||
/**
|
||||
* Footer of expanded Quick Settings, tiles page indicator, (optionally) build number and
|
||||
* {@link QSFooterActionsView}
|
||||
*/
|
||||
public class QSFooterView extends FrameLayout {
|
||||
private SettingsButton mSettingsButton;
|
||||
protected View mSettingsContainer;
|
||||
private PageIndicator mPageIndicator;
|
||||
private TextView mBuildText;
|
||||
private boolean mShouldShowBuildText;
|
||||
|
||||
private boolean mQsDisabled;
|
||||
|
||||
private boolean mExpanded;
|
||||
|
||||
private boolean mListening;
|
||||
|
||||
protected MultiUserSwitch mMultiUserSwitch;
|
||||
private ImageView mMultiUserAvatar;
|
||||
private View mActionsContainer;
|
||||
|
||||
protected TouchAnimator mFooterAnimator;
|
||||
|
||||
private boolean mQsDisabled;
|
||||
private boolean mExpanded;
|
||||
private float mExpansionAmount;
|
||||
|
||||
protected View mEdit;
|
||||
private TouchAnimator mSettingsCogAnimator;
|
||||
|
||||
private View mActionsContainer;
|
||||
private View mTunerIcon;
|
||||
private int mTunerIconTranslation;
|
||||
private boolean mShouldShowBuildText;
|
||||
|
||||
private OnClickListener mExpandClickListener;
|
||||
|
||||
@@ -94,27 +74,11 @@ public class QSFooterView extends FrameLayout {
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mEdit = requireViewById(android.R.id.edit);
|
||||
|
||||
mPageIndicator = findViewById(R.id.footer_page_indicator);
|
||||
|
||||
mSettingsButton = findViewById(R.id.settings_button);
|
||||
mSettingsContainer = findViewById(R.id.settings_button_container);
|
||||
|
||||
mMultiUserSwitch = findViewById(R.id.multi_user_switch);
|
||||
mMultiUserAvatar = mMultiUserSwitch.findViewById(R.id.multi_user_avatar);
|
||||
|
||||
mActionsContainer = requireViewById(R.id.qs_footer_actions_container);
|
||||
mBuildText = findViewById(R.id.build);
|
||||
mTunerIcon = requireViewById(R.id.tuner_icon);
|
||||
|
||||
// RenderThread is doing more harm than good when touching the header (to expand quick
|
||||
// settings), so disable it for this view
|
||||
if (mSettingsButton.getBackground() instanceof RippleDrawable) {
|
||||
((RippleDrawable) mSettingsButton.getBackground()).setForceSoftware(true);
|
||||
}
|
||||
updateResources();
|
||||
|
||||
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
|
||||
setBuildText();
|
||||
}
|
||||
@@ -137,18 +101,7 @@ public class QSFooterView extends FrameLayout {
|
||||
}
|
||||
}
|
||||
|
||||
void updateAnimator(int width, int numTiles) {
|
||||
int size = mContext.getResources().getDimensionPixelSize(R.dimen.qs_quick_tile_size)
|
||||
- mContext.getResources().getDimensionPixelSize(R.dimen.qs_tile_padding);
|
||||
int remaining = (width - numTiles * size) / (numTiles - 1);
|
||||
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
|
||||
|
||||
mSettingsCogAnimator = new Builder()
|
||||
.addFloat(mSettingsButton, "translationX",
|
||||
isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
|
||||
.addFloat(mSettingsButton, "rotation", -120, 0)
|
||||
.build();
|
||||
|
||||
void updateExpansion() {
|
||||
setExpansion(mExpansionAmount);
|
||||
}
|
||||
|
||||
@@ -158,20 +111,11 @@ public class QSFooterView extends FrameLayout {
|
||||
updateResources();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRtlPropertiesChanged(int layoutDirection) {
|
||||
super.onRtlPropertiesChanged(layoutDirection);
|
||||
updateResources();
|
||||
}
|
||||
|
||||
private void updateResources() {
|
||||
updateFooterAnimator();
|
||||
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
|
||||
lp.bottomMargin = getResources().getDimensionPixelSize(R.dimen.qs_footers_margin_bottom);
|
||||
setLayoutParams(lp);
|
||||
mTunerIconTranslation = mContext.getResources()
|
||||
.getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation);
|
||||
mTunerIcon.setTranslationX(isLayoutRtl() ? -mTunerIconTranslation : mTunerIconTranslation);
|
||||
}
|
||||
|
||||
private void updateFooterAnimator() {
|
||||
@@ -197,17 +141,15 @@ public class QSFooterView extends FrameLayout {
|
||||
mExpandClickListener = onClickListener;
|
||||
}
|
||||
|
||||
void setExpanded(boolean expanded, boolean isTunerEnabled, boolean multiUserEnabled) {
|
||||
void setExpanded(boolean expanded) {
|
||||
if (mExpanded == expanded) return;
|
||||
mExpanded = expanded;
|
||||
updateEverything(isTunerEnabled, multiUserEnabled);
|
||||
updateEverything();
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setExpansion(float headerExpansionFraction) {
|
||||
mExpansionAmount = headerExpansionFraction;
|
||||
if (mSettingsCogAnimator != null) mSettingsCogAnimator.setPosition(headerExpansionFraction);
|
||||
|
||||
if (mFooterAnimator != null) {
|
||||
mFooterAnimator.setPosition(headerExpansionFraction);
|
||||
}
|
||||
@@ -228,14 +170,6 @@ public class QSFooterView extends FrameLayout {
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
/** */
|
||||
public void setListening(boolean listening) {
|
||||
if (listening == mListening) {
|
||||
return;
|
||||
}
|
||||
mListening = listening;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performAccessibilityAction(int action, Bundle arguments) {
|
||||
if (action == AccessibilityNodeInfo.ACTION_EXPAND) {
|
||||
@@ -253,50 +187,26 @@ public class QSFooterView extends FrameLayout {
|
||||
info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND);
|
||||
}
|
||||
|
||||
void disable(int state2, boolean isTunerEnabled, boolean multiUserEnabled) {
|
||||
void disable(int state2) {
|
||||
final boolean disabled = (state2 & DISABLE2_QUICK_SETTINGS) != 0;
|
||||
if (disabled == mQsDisabled) return;
|
||||
mQsDisabled = disabled;
|
||||
updateEverything(isTunerEnabled, multiUserEnabled);
|
||||
updateEverything();
|
||||
}
|
||||
|
||||
void updateEverything(boolean isTunerEnabled, boolean multiUserEnabled) {
|
||||
void updateEverything() {
|
||||
post(() -> {
|
||||
updateVisibilities(isTunerEnabled, multiUserEnabled);
|
||||
updateVisibilities();
|
||||
updateClickabilities();
|
||||
setClickable(false);
|
||||
});
|
||||
}
|
||||
|
||||
private void updateClickabilities() {
|
||||
mMultiUserSwitch.setClickable(mMultiUserSwitch.getVisibility() == View.VISIBLE);
|
||||
mEdit.setClickable(mEdit.getVisibility() == View.VISIBLE);
|
||||
mSettingsButton.setClickable(mSettingsButton.getVisibility() == View.VISIBLE);
|
||||
mBuildText.setLongClickable(mBuildText.getVisibility() == View.VISIBLE);
|
||||
}
|
||||
|
||||
private void updateVisibilities(boolean isTunerEnabled, boolean multiUserEnabled) {
|
||||
mSettingsContainer.setVisibility(mQsDisabled ? View.GONE : View.VISIBLE);
|
||||
mTunerIcon.setVisibility(isTunerEnabled ? View.VISIBLE : View.INVISIBLE);
|
||||
final boolean isDemo = UserManager.isDeviceInDemoMode(mContext);
|
||||
mMultiUserSwitch.setVisibility(
|
||||
showUserSwitcher(multiUserEnabled) ? View.VISIBLE : View.GONE);
|
||||
mSettingsButton.setVisibility(isDemo || !mExpanded ? View.INVISIBLE : View.VISIBLE);
|
||||
|
||||
private void updateVisibilities() {
|
||||
mBuildText.setVisibility(mExpanded && mShouldShowBuildText ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
private boolean showUserSwitcher(boolean multiUserEnabled) {
|
||||
return mExpanded && multiUserEnabled;
|
||||
}
|
||||
|
||||
void onUserInfoChanged(Drawable picture, boolean isGuestUser) {
|
||||
if (picture != null && isGuestUser && !(picture instanceof UserIconDrawable)) {
|
||||
picture = picture.getConstantState().newDrawable(getResources()).mutate();
|
||||
picture.setColorFilter(
|
||||
Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
|
||||
Mode.SRC_IN);
|
||||
}
|
||||
mMultiUserAvatar.setImageDrawable(picture);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,39 +16,19 @@
|
||||
|
||||
package com.android.systemui.qs;
|
||||
|
||||
import static com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.UserManager;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.internal.jank.InteractionJankMonitor;
|
||||
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.systemui.R;
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator;
|
||||
import com.android.systemui.globalactions.GlobalActionsDialogLite;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.qs.dagger.QSScope;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitchController;
|
||||
import com.android.systemui.statusbar.phone.SettingsButton;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Controller for {@link QSFooterView}.
|
||||
@@ -56,137 +36,45 @@ import javax.inject.Named;
|
||||
@QSScope
|
||||
public class QSFooterViewController extends ViewController<QSFooterView> implements QSFooter {
|
||||
|
||||
private final UserManager mUserManager;
|
||||
private final UserInfoController mUserInfoController;
|
||||
private final ActivityStarter mActivityStarter;
|
||||
private final DeviceProvisionedController mDeviceProvisionedController;
|
||||
private final UserTracker mUserTracker;
|
||||
private final QSPanelController mQsPanelController;
|
||||
private final QuickQSPanelController mQuickQSPanelController;
|
||||
private final TunerService mTunerService;
|
||||
private final MetricsLogger mMetricsLogger;
|
||||
private final FalsingManager mFalsingManager;
|
||||
private final MultiUserSwitchController mMultiUserSwitchController;
|
||||
private final SettingsButton mSettingsButton;
|
||||
private final View mSettingsButtonContainer;
|
||||
private final QSFooterActionsController mQsFooterActionsController;
|
||||
private final TextView mBuildText;
|
||||
private final View mEdit;
|
||||
private final PageIndicator mPageIndicator;
|
||||
private final View mPowerMenuLite;
|
||||
private final boolean mShowPMLiteButton;
|
||||
private final GlobalActionsDialogLite mGlobalActionsDialog;
|
||||
private final UiEventLogger mUiEventLogger;
|
||||
|
||||
private final UserInfoController.OnUserInfoChangedListener mOnUserInfoChangedListener =
|
||||
new UserInfoController.OnUserInfoChangedListener() {
|
||||
@Override
|
||||
public void onUserInfoChanged(String name, Drawable picture, String userAccount) {
|
||||
boolean isGuestUser = mUserManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser());
|
||||
mView.onUserInfoChanged(picture, isGuestUser);
|
||||
}
|
||||
};
|
||||
|
||||
private final View.OnClickListener mSettingsOnClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// Don't do anything until views are unhidden. Don't do anything if the tap looks
|
||||
// suspicious.
|
||||
if (!mExpanded || mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (v == mSettingsButton) {
|
||||
if (!mDeviceProvisionedController.isCurrentUserSetup()) {
|
||||
// If user isn't setup just unlock the device and dump them back at SUW.
|
||||
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
|
||||
});
|
||||
return;
|
||||
}
|
||||
mMetricsLogger.action(
|
||||
mExpanded ? MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
|
||||
: MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH);
|
||||
if (mSettingsButton.isTunerClick()) {
|
||||
mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
|
||||
if (isTunerEnabled()) {
|
||||
mTunerService.showResetRequest(
|
||||
() -> {
|
||||
// Relaunch settings so that the tuner disappears.
|
||||
startSettingsActivity();
|
||||
});
|
||||
} else {
|
||||
Toast.makeText(getContext(), R.string.tuner_toast,
|
||||
Toast.LENGTH_LONG).show();
|
||||
mTunerService.setTunerEnabled(true);
|
||||
}
|
||||
startSettingsActivity();
|
||||
|
||||
});
|
||||
} else {
|
||||
startSettingsActivity();
|
||||
}
|
||||
} else if (v == mPowerMenuLite) {
|
||||
mUiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
|
||||
mGlobalActionsDialog.showOrHideDialog(false, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private boolean mListening;
|
||||
private boolean mExpanded;
|
||||
|
||||
@Inject
|
||||
QSFooterViewController(QSFooterView view, UserManager userManager,
|
||||
UserInfoController userInfoController, ActivityStarter activityStarter,
|
||||
DeviceProvisionedController deviceProvisionedController, UserTracker userTracker,
|
||||
QSFooterViewController(QSFooterView view,
|
||||
UserTracker userTracker,
|
||||
QSPanelController qsPanelController,
|
||||
MultiUserSwitchController multiUserSwitchController,
|
||||
QuickQSPanelController quickQSPanelController,
|
||||
TunerService tunerService, MetricsLogger metricsLogger, FalsingManager falsingManager,
|
||||
@Named(PM_LITE_ENABLED) boolean showPMLiteButton,
|
||||
GlobalActionsDialogLite globalActionsDialog, UiEventLogger uiEventLogger) {
|
||||
QSFooterActionsController qsFooterActionsController) {
|
||||
super(view);
|
||||
mUserManager = userManager;
|
||||
mUserInfoController = userInfoController;
|
||||
mActivityStarter = activityStarter;
|
||||
mDeviceProvisionedController = deviceProvisionedController;
|
||||
mUserTracker = userTracker;
|
||||
mQsPanelController = qsPanelController;
|
||||
mQuickQSPanelController = quickQSPanelController;
|
||||
mTunerService = tunerService;
|
||||
mMetricsLogger = metricsLogger;
|
||||
mFalsingManager = falsingManager;
|
||||
mMultiUserSwitchController = multiUserSwitchController;
|
||||
mQsFooterActionsController = qsFooterActionsController;
|
||||
|
||||
mSettingsButton = mView.findViewById(R.id.settings_button);
|
||||
mSettingsButtonContainer = mView.findViewById(R.id.settings_button_container);
|
||||
mBuildText = mView.findViewById(R.id.build);
|
||||
mEdit = mView.findViewById(android.R.id.edit);
|
||||
mPageIndicator = mView.findViewById(R.id.footer_page_indicator);
|
||||
mPowerMenuLite = mView.findViewById(R.id.pm_lite);
|
||||
mShowPMLiteButton = showPMLiteButton;
|
||||
mGlobalActionsDialog = globalActionsDialog;
|
||||
mUiEventLogger = uiEventLogger;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
super.onInit();
|
||||
mMultiUserSwitchController.init();
|
||||
mQsFooterActionsController.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
if (mShowPMLiteButton) {
|
||||
mPowerMenuLite.setVisibility(View.VISIBLE);
|
||||
mPowerMenuLite.setOnClickListener(mSettingsOnClickListener);
|
||||
} else {
|
||||
mPowerMenuLite.setVisibility(View.GONE);
|
||||
}
|
||||
mView.addOnLayoutChangeListener(
|
||||
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
|
||||
mView.updateAnimator(
|
||||
right - left, mQuickQSPanelController.getNumQuickTiles()));
|
||||
mSettingsButton.setOnClickListener(mSettingsOnClickListener);
|
||||
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
|
||||
mView.updateExpansion();
|
||||
mQsFooterActionsController.updateAnimator(right - left,
|
||||
mQuickQSPanelController.getNumQuickTiles());
|
||||
}
|
||||
);
|
||||
|
||||
mBuildText.setOnLongClickListener(view -> {
|
||||
CharSequence buildText = mBuildText.getText();
|
||||
if (!TextUtils.isEmpty(buildText)) {
|
||||
@@ -200,17 +88,8 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
mEdit.setOnClickListener(view -> {
|
||||
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
return;
|
||||
}
|
||||
mActivityStarter.postQSRunnableDismissingKeyguard(() ->
|
||||
mQsPanelController.showEdit(view));
|
||||
});
|
||||
|
||||
mQsPanelController.setFooterPageIndicator(mPageIndicator);
|
||||
mView.updateEverything(isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
|
||||
mView.updateEverything();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -225,38 +104,25 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
|
||||
|
||||
@Override
|
||||
public void setExpanded(boolean expanded) {
|
||||
mExpanded = expanded;
|
||||
mView.setExpanded(
|
||||
expanded, isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight() {
|
||||
return mView.getHeight();
|
||||
mQsFooterActionsController.setExpanded(expanded);
|
||||
mView.setExpanded(expanded);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpansion(float expansion) {
|
||||
mView.setExpansion(expansion);
|
||||
mQsFooterActionsController.setExpansion(expansion);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setListening(boolean listening) {
|
||||
if (mListening == listening) {
|
||||
return;
|
||||
}
|
||||
|
||||
mListening = listening;
|
||||
if (mListening) {
|
||||
mUserInfoController.addCallback(mOnUserInfoChangedListener);
|
||||
} else {
|
||||
mUserInfoController.removeCallback(mOnUserInfoChangedListener);
|
||||
}
|
||||
mQsFooterActionsController.setListening(listening);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyguardShowing(boolean keyguardShowing) {
|
||||
mView.setKeyguardShowing();
|
||||
mQsFooterActionsController.setKeyguardShowing();
|
||||
}
|
||||
|
||||
/** */
|
||||
@@ -267,19 +133,7 @@ public class QSFooterViewController extends ViewController<QSFooterView> impleme
|
||||
|
||||
@Override
|
||||
public void disable(int state1, int state2, boolean animate) {
|
||||
mView.disable(state2, isTunerEnabled(), mMultiUserSwitchController.isMultiUserEnabled());
|
||||
}
|
||||
|
||||
private void startSettingsActivity() {
|
||||
ActivityLaunchAnimator.Controller animationController =
|
||||
mSettingsButtonContainer != null ? ActivityLaunchAnimator.Controller.fromView(
|
||||
mSettingsButtonContainer,
|
||||
InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) : null;
|
||||
mActivityStarter.startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS),
|
||||
true /* dismissShade */, animationController);
|
||||
}
|
||||
|
||||
private boolean isTunerEnabled() {
|
||||
return mTunerService.isTunerEnabled();
|
||||
mView.disable(state2);
|
||||
mQsFooterActionsController.disable(state2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.android.systemui.dagger.qualifiers.RootView;
|
||||
import com.android.systemui.plugins.qs.QS;
|
||||
import com.android.systemui.qs.QSContainerImpl;
|
||||
import com.android.systemui.qs.QSFooter;
|
||||
import com.android.systemui.qs.QSFooterActionsView;
|
||||
import com.android.systemui.qs.QSFooterView;
|
||||
import com.android.systemui.qs.QSFooterViewController;
|
||||
import com.android.systemui.qs.QSFragment;
|
||||
@@ -120,6 +121,12 @@ public interface QSFragmentModule {
|
||||
return view.findViewById(R.id.qs_footer);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
static QSFooterActionsView providesQSFooterActionsView(@RootView View view) {
|
||||
return view.findViewById(R.id.qs_footer_actions_container);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@QSScope
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.android.systemui.qs
|
||||
|
||||
import com.android.systemui.R
|
||||
import android.os.UserManager
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.logging.MetricsLogger
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.internal.logging.testing.FakeMetricsLogger
|
||||
import com.android.systemui.Dependency
|
||||
import com.android.systemui.classifier.FalsingManagerFake
|
||||
import com.android.systemui.globalactions.GlobalActionsDialogLite
|
||||
import com.android.systemui.plugins.ActivityStarter
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitchController
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController
|
||||
import com.android.systemui.statusbar.policy.UserInfoController
|
||||
import com.android.systemui.tuner.TunerService
|
||||
import com.android.systemui.utils.leaks.FakeTunerService
|
||||
import com.android.systemui.utils.leaks.LeakCheckedTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyBoolean
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.MockitoAnnotations
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
@SmallTest
|
||||
class QSFooterActionsControllerTest : LeakCheckedTest() {
|
||||
@Mock
|
||||
private lateinit var userManager: UserManager
|
||||
@Mock
|
||||
private lateinit var activityStarter: ActivityStarter
|
||||
@Mock
|
||||
private lateinit var deviceProvisionedController: DeviceProvisionedController
|
||||
@Mock
|
||||
private lateinit var userInfoController: UserInfoController
|
||||
@Mock
|
||||
private lateinit var qsPanelController: QSPanelController
|
||||
@Mock
|
||||
private lateinit var multiUserSwitchController: MultiUserSwitchController
|
||||
@Mock
|
||||
private lateinit var globalActionsDialog: GlobalActionsDialogLite
|
||||
@Mock
|
||||
private lateinit var uiEventLogger: UiEventLogger
|
||||
@Mock
|
||||
private lateinit var controller: QSFooterActionsController
|
||||
|
||||
private val metricsLogger: MetricsLogger = FakeMetricsLogger()
|
||||
private lateinit var view: QSFooterActionsView
|
||||
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
|
||||
val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
|
||||
|
||||
view = LayoutInflater.from(context)
|
||||
.inflate(R.layout.qs_footer_actions, null) as QSFooterActionsView
|
||||
|
||||
controller = QSFooterActionsController(view, qsPanelController, activityStarter,
|
||||
userManager, userInfoController, multiUserSwitchController,
|
||||
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
|
||||
globalActionsDialog, uiEventLogger, showPMLiteButton = true)
|
||||
controller.init()
|
||||
controller.onViewAttached()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLogPowerMenuClick() {
|
||||
controller.expanded = true
|
||||
falsingManager.setFalseTap(false)
|
||||
|
||||
view.findViewById<View>(R.id.pm_lite).performClick()
|
||||
// Verify clicks are logged
|
||||
verify(uiEventLogger, Mockito.times(1))
|
||||
.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSettings_UserNotSetup() {
|
||||
whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
|
||||
view.findViewById<View>(R.id.settings_button).performClick()
|
||||
// Verify Settings wasn't launched.
|
||||
verify<ActivityStarter>(activityStarter, Mockito.never()).startActivity(any(), anyBoolean())
|
||||
}
|
||||
}
|
||||
@@ -18,16 +18,11 @@ package com.android.systemui.qs;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.os.UserManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
@@ -35,21 +30,8 @@ import android.widget.TextView;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.logging.MetricsLogger;
|
||||
import com.android.internal.logging.UiEventLogger;
|
||||
import com.android.internal.logging.testing.FakeMetricsLogger;
|
||||
import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.classifier.FalsingManagerFake;
|
||||
import com.android.systemui.globalactions.GlobalActionsDialogLite;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitchController;
|
||||
import com.android.systemui.statusbar.phone.SettingsButton;
|
||||
import com.android.systemui.statusbar.policy.DeviceProvisionedController;
|
||||
import com.android.systemui.statusbar.policy.UserInfoController;
|
||||
import com.android.systemui.tuner.TunerService;
|
||||
import com.android.systemui.utils.leaks.FakeTunerService;
|
||||
import com.android.systemui.utils.leaks.LeakCheckedTest;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -67,14 +49,6 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
|
||||
@Mock
|
||||
private QSFooterView mView;
|
||||
@Mock
|
||||
private UserManager mUserManager;
|
||||
@Mock
|
||||
private ActivityStarter mActivityStarter;
|
||||
@Mock
|
||||
private DeviceProvisionedController mDeviceProvisionedController;
|
||||
@Mock
|
||||
private UserInfoController mUserInfoController;
|
||||
@Mock
|
||||
private UserTracker mUserTracker;
|
||||
@Mock
|
||||
private QSPanelController mQSPanelController;
|
||||
@@ -82,36 +56,19 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
|
||||
private ClipboardManager mClipboardManager;
|
||||
@Mock
|
||||
private QuickQSPanelController mQuickQSPanelController;
|
||||
private FakeTunerService mFakeTunerService;
|
||||
private MetricsLogger mMetricsLogger = new FakeMetricsLogger();
|
||||
private FalsingManagerFake mFalsingManager;
|
||||
|
||||
@Mock
|
||||
private SettingsButton mSettingsButton;
|
||||
@Mock
|
||||
private TextView mBuildText;
|
||||
@Mock
|
||||
private View mEdit;
|
||||
@Mock
|
||||
private MultiUserSwitchController mMultiUserSwitchController;
|
||||
@Mock
|
||||
private View mPowerMenuLiteView;
|
||||
@Mock
|
||||
private GlobalActionsDialogLite mGlobalActionsDialog;
|
||||
@Mock
|
||||
private UiEventLogger mUiEventLogger;
|
||||
private QSFooterActionsController mQSFooterActionsController;
|
||||
|
||||
private QSFooterViewController mController;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mFalsingManager = new FalsingManagerFake();
|
||||
|
||||
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
|
||||
|
||||
mFakeTunerService = (FakeTunerService) Dependency.get(TunerService.class);
|
||||
|
||||
mContext.addMockSystemService(ClipboardManager.class, mClipboardManager);
|
||||
|
||||
when(mView.getContext()).thenReturn(mContext);
|
||||
@@ -119,16 +76,10 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
|
||||
when(mUserTracker.getUserContext()).thenReturn(mContext);
|
||||
|
||||
when(mView.isAttachedToWindow()).thenReturn(true);
|
||||
when(mView.findViewById(R.id.settings_button)).thenReturn(mSettingsButton);
|
||||
when(mView.findViewById(R.id.build)).thenReturn(mBuildText);
|
||||
when(mView.findViewById(android.R.id.edit)).thenReturn(mEdit);
|
||||
when(mView.findViewById(R.id.pm_lite)).thenReturn(mPowerMenuLiteView);
|
||||
|
||||
mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
|
||||
mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
|
||||
mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
|
||||
mMetricsLogger, mFalsingManager, false, mGlobalActionsDialog,
|
||||
mUiEventLogger);
|
||||
mController = new QSFooterViewController(mView, mUserTracker, mQSPanelController,
|
||||
mQuickQSPanelController, mQSFooterActionsController);
|
||||
|
||||
mController.init();
|
||||
}
|
||||
@@ -148,40 +99,4 @@ public class QSFooterViewControllerTest extends LeakCheckedTest {
|
||||
verify(mClipboardManager).setPrimaryClip(captor.capture());
|
||||
assertThat(captor.getValue().getItemAt(0).getText()).isEqualTo(text);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettings_UserNotSetup() {
|
||||
ArgumentCaptor<View.OnClickListener> onClickCaptor =
|
||||
ArgumentCaptor.forClass(View.OnClickListener.class);
|
||||
verify(mSettingsButton).setOnClickListener(onClickCaptor.capture());
|
||||
|
||||
when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(false);
|
||||
|
||||
onClickCaptor.getValue().onClick(mSettingsButton);
|
||||
// Verify Settings wasn't launched.
|
||||
verify(mActivityStarter, never()).startActivity(any(), anyBoolean());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLogPowerMenuClick() {
|
||||
// Enable power menu button
|
||||
mController = new QSFooterViewController(mView, mUserManager, mUserInfoController,
|
||||
mActivityStarter, mDeviceProvisionedController, mUserTracker, mQSPanelController,
|
||||
mMultiUserSwitchController, mQuickQSPanelController, mFakeTunerService,
|
||||
mMetricsLogger, new FalsingManagerFake(), true, mGlobalActionsDialog,
|
||||
mUiEventLogger);
|
||||
mController.init();
|
||||
mController.setExpanded(true);
|
||||
mFalsingManager.setFalseTap(false);
|
||||
|
||||
ArgumentCaptor<View.OnClickListener> onClickCaptor =
|
||||
ArgumentCaptor.forClass(View.OnClickListener.class);
|
||||
verify(mPowerMenuLiteView).setOnClickListener(onClickCaptor.capture());
|
||||
|
||||
onClickCaptor.getValue().onClick(mPowerMenuLiteView);
|
||||
|
||||
// Verify clicks are logged
|
||||
verify(mUiEventLogger, times(1))
|
||||
.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user