Merge "Remove old footer actions implementation" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3c86fce3a9
@@ -1,105 +0,0 @@
|
||||
<?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.
|
||||
-->
|
||||
<!-- TODO(b/242040009): Remove this file. -->
|
||||
<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/qs_footer_action_inset"
|
||||
android:background="@drawable/qs_security_footer_background"
|
||||
android:layout_gravity="end"
|
||||
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/fgs_new"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/fgs_dot"
|
||||
android:contentDescription="@string/fgs_dot_content_description"
|
||||
/>
|
||||
|
||||
<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_collapsed_new"
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:layout_gravity="bottom|end"
|
||||
android:src="@drawable/fgs_dot"
|
||||
android:contentDescription="@string/fgs_dot_content_description"
|
||||
/>
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
||||
@@ -16,10 +16,8 @@
|
||||
-->
|
||||
|
||||
<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
|
||||
<!-- TODO(b/242040009): Clean up this file. -->
|
||||
<com.android.systemui.qs.FooterActionsView
|
||||
<LinearLayout
|
||||
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/footer_actions_height"
|
||||
android:elevation="@dimen/qs_panel_elevation"
|
||||
@@ -28,74 +26,4 @@
|
||||
android:background="@drawable/qs_footer_actions_background"
|
||||
android:gravity="center_vertical|end"
|
||||
android:layout_gravity="bottom"
|
||||
>
|
||||
|
||||
<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/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:background="@drawable/qs_footer_action_circle"
|
||||
android:focusable="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/multi_user_avatar"
|
||||
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>
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedFrameLayout
|
||||
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: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="@dimen/qs_footer_icon_size"
|
||||
android:layout_height="@dimen/qs_footer_icon_size"
|
||||
android:layout_gravity="center"
|
||||
android:background="@android:color/transparent"
|
||||
android:focusable="false"
|
||||
android:clickable="false"
|
||||
android:importantForAccessibility="yes"
|
||||
android:contentDescription="@string/accessibility_quick_settings_settings"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_settings"
|
||||
android:tint="?android:attr/textColorPrimary" />
|
||||
|
||||
</com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
|
||||
|
||||
<com.android.systemui.statusbar.AlphaOptimizedImageView
|
||||
android:id="@id/pm_lite"
|
||||
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_color"
|
||||
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="?androidprv:attr/textColorOnAccent" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.android.systemui.qs.FooterActionsView>
|
||||
/>
|
||||
@@ -1,61 +0,0 @@
|
||||
<?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.
|
||||
-->
|
||||
<!-- TODO(b/242040009): Remove this file. -->
|
||||
<LinearLayout
|
||||
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:clickable="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="@dimen/qs_footer_padding"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_gravity="center_vertical|center_horizontal"
|
||||
android:layout_marginEnd="@dimen/qs_footer_action_inset"
|
||||
android:background="@drawable/qs_security_footer_background"
|
||||
>
|
||||
|
||||
<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:tint="?android:attr/textColorSecondary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/footer_text"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:singleLine="true"
|
||||
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>
|
||||
@@ -204,9 +204,6 @@ object Flags {
|
||||
"full_screen_user_switcher"
|
||||
)
|
||||
|
||||
// TODO(b/254512678): Tracking Bug
|
||||
@JvmField val NEW_FOOTER_ACTIONS = releasedFlag(507, "new_footer_actions")
|
||||
|
||||
// TODO(b/244064524): Tracking Bug
|
||||
@JvmField
|
||||
val QS_SECONDARY_DATA_SUB_INFO =
|
||||
|
||||
@@ -122,10 +122,6 @@ interface FgsManagerController {
|
||||
/** Remove a [OnDialogDismissedListener]. */
|
||||
fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener)
|
||||
|
||||
/** Whether we should update the footer visibility. */
|
||||
// TODO(b/242040009): Remove this.
|
||||
fun shouldUpdateFooterVisibility(): Boolean
|
||||
|
||||
@VisibleForTesting
|
||||
fun visibleButtonsCount(): Int
|
||||
|
||||
@@ -375,8 +371,6 @@ class FgsManagerControllerImpl @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldUpdateFooterVisibility() = dialog == null
|
||||
|
||||
override fun showDialog(expandable: Expandable?) {
|
||||
synchronized(lock) {
|
||||
if (dialog == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -16,261 +16,17 @@
|
||||
|
||||
package com.android.systemui.qs
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.Configuration
|
||||
import android.os.Handler
|
||||
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 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.animation.Expandable
|
||||
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.qs.dagger.QSScope
|
||||
import com.android.systemui.settings.UserTracker
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitchController
|
||||
import com.android.systemui.statusbar.policy.ConfigurationController
|
||||
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.util.LargeScreenUtils
|
||||
import com.android.systemui.util.ViewController
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
import javax.inject.Provider
|
||||
|
||||
/**
|
||||
* Manages [FooterActionsView] behaviour, both when it's placed in QS or QQS (split shade).
|
||||
* Main difference between QS and QQS behaviour is condition when buttons should be visible,
|
||||
* determined by [buttonsVisibleState]
|
||||
*/
|
||||
@QSScope
|
||||
// TODO(b/242040009): Remove this file.
|
||||
internal class FooterActionsController @Inject constructor(
|
||||
view: FooterActionsView,
|
||||
multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
|
||||
private val activityStarter: ActivityStarter,
|
||||
private val userManager: UserManager,
|
||||
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 globalActionsDialogProvider: Provider<GlobalActionsDialogLite>,
|
||||
private val uiEventLogger: UiEventLogger,
|
||||
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
|
||||
private val globalSetting: GlobalSettings,
|
||||
private val handler: Handler,
|
||||
private val configurationController: ConfigurationController,
|
||||
) : ViewController<FooterActionsView>(view) {
|
||||
|
||||
private var globalActionsDialog: GlobalActionsDialogLite? = null
|
||||
|
||||
private var lastExpansion = -1f
|
||||
private var listening: Boolean = false
|
||||
private var inSplitShade = false
|
||||
|
||||
private val singleShadeAnimator by lazy {
|
||||
// In single shade, the actions footer should only appear at the end of the expansion,
|
||||
// so that it doesn't overlap with the notifications panel.
|
||||
TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).setStartDelay(0.9f).build()
|
||||
}
|
||||
|
||||
private val splitShadeAnimator by lazy {
|
||||
// The Actions footer view has its own background which is the same color as the qs panel's
|
||||
// background.
|
||||
// We don't want it to fade in at the same time as the rest of the panel, otherwise it is
|
||||
// more opaque than the rest of the panel's background. Only applies to split shade.
|
||||
val alphaAnimator = TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).build()
|
||||
val bgAlphaAnimator =
|
||||
TouchAnimator.Builder()
|
||||
.addFloat(mView, "backgroundAlpha", 0f, 1f)
|
||||
.setStartDelay(0.9f)
|
||||
.build()
|
||||
// In split shade, we want the actions footer to fade in exactly at the same time as the
|
||||
// rest of the shade, as there is no overlap.
|
||||
TouchAnimator.Builder()
|
||||
.addFloat(alphaAnimator, "position", 0f, 1f)
|
||||
.addFloat(bgAlphaAnimator, "position", 0f, 1f)
|
||||
.build()
|
||||
}
|
||||
|
||||
private val animators: TouchAnimator
|
||||
get() = if (inSplitShade) splitShadeAnimator else singleShadeAnimator
|
||||
|
||||
var visible = true
|
||||
set(value) {
|
||||
field = value
|
||||
updateVisibility()
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@VisibleForTesting
|
||||
internal val securityFootersSeparator = View(context).apply { visibility = View.GONE }
|
||||
|
||||
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
|
||||
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
|
||||
mView.onUserInfoChanged(picture, isGuestUser)
|
||||
}
|
||||
|
||||
private val multiUserSetting =
|
||||
object : SettingObserver(
|
||||
globalSetting, handler, USER_SWITCHER_ENABLED, userTracker.userId) {
|
||||
override fun handleValueChanged(value: Int, observedChange: Boolean) {
|
||||
if (observedChange) {
|
||||
updateView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val onClickListener = View.OnClickListener { v ->
|
||||
// Don't do anything if the tap looks suspicious.
|
||||
if (!visible || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
return@OnClickListener
|
||||
}
|
||||
if (v === settingsButtonContainer) {
|
||||
if (!deviceProvisionedController.isCurrentUserSetup) {
|
||||
// If user isn't setup just unlock the device and dump them back at SUW.
|
||||
activityStarter.postQSRunnableDismissingKeyguard {}
|
||||
return@OnClickListener
|
||||
}
|
||||
metricsLogger.action(MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH)
|
||||
startSettingsActivity()
|
||||
} else if (v === powerMenuLite) {
|
||||
uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
|
||||
globalActionsDialog?.showOrHideDialog(false, true, Expandable.fromView(powerMenuLite))
|
||||
}
|
||||
}
|
||||
|
||||
private val configurationListener =
|
||||
object : ConfigurationController.ConfigurationListener {
|
||||
override fun onConfigChanged(newConfig: Configuration?) {
|
||||
updateResources()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateResources() {
|
||||
inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
|
||||
}
|
||||
|
||||
override fun onInit() {
|
||||
multiUserSwitchController.init()
|
||||
securityFooterController.init()
|
||||
fgsManagerFooterController.init()
|
||||
}
|
||||
|
||||
private fun updateVisibility() {
|
||||
val previousVisibility = mView.visibility
|
||||
mView.visibility = if (visible) View.VISIBLE else View.INVISIBLE
|
||||
if (previousVisibility != mView.visibility) updateView()
|
||||
}
|
||||
|
||||
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() {
|
||||
globalActionsDialog = globalActionsDialogProvider.get()
|
||||
if (showPMLiteButton) {
|
||||
powerMenuLite.visibility = View.VISIBLE
|
||||
powerMenuLite.setOnClickListener(onClickListener)
|
||||
} else {
|
||||
powerMenuLite.visibility = View.GONE
|
||||
}
|
||||
settingsButtonContainer.setOnClickListener(onClickListener)
|
||||
multiUserSetting.isListening = true
|
||||
|
||||
val securityFooter = securityFooterController.view
|
||||
securityFootersContainer?.addView(securityFooter)
|
||||
val separatorWidth = resources.getDimensionPixelSize(R.dimen.qs_footer_action_inset)
|
||||
securityFootersContainer?.addView(securityFootersSeparator, separatorWidth, 1)
|
||||
|
||||
val fgsFooter = fgsManagerFooterController.view
|
||||
securityFootersContainer?.addView(fgsFooter)
|
||||
|
||||
val visibilityListener =
|
||||
VisibilityChangedDispatcher.OnVisibilityChangedListener { visibility ->
|
||||
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)
|
||||
|
||||
configurationController.addCallback(configurationListener)
|
||||
|
||||
updateResources()
|
||||
updateView()
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
mView.updateEverything(multiUserSwitchController.isMultiUserEnabled)
|
||||
}
|
||||
|
||||
override fun onViewDetached() {
|
||||
globalActionsDialog?.destroy()
|
||||
globalActionsDialog = null
|
||||
setListening(false)
|
||||
multiUserSetting.isListening = false
|
||||
configurationController.removeCallback(configurationListener)
|
||||
}
|
||||
|
||||
fun setListening(listening: Boolean) {
|
||||
if (this.listening == listening) {
|
||||
return
|
||||
}
|
||||
this.listening = listening
|
||||
if (this.listening) {
|
||||
userInfoController.addCallback(onUserInfoChangedListener)
|
||||
updateView()
|
||||
} else {
|
||||
userInfoController.removeCallback(onUserInfoChangedListener)
|
||||
}
|
||||
|
||||
fgsManagerFooterController.setListening(listening)
|
||||
securityFooterController.setListening(listening)
|
||||
}
|
||||
|
||||
fun disable(state2: Int) {
|
||||
mView.disable(state2, multiUserSwitchController.isMultiUserEnabled)
|
||||
}
|
||||
|
||||
fun setExpansion(headerExpansionFraction: Float) {
|
||||
animators.setPosition(headerExpansionFraction)
|
||||
}
|
||||
|
||||
fun setKeyguardShowing(showing: Boolean) {
|
||||
setExpansion(lastExpansion)
|
||||
/** Controller for the footer actions. This manages the initialization of its dependencies. */
|
||||
@SysUISingleton
|
||||
class FooterActionsController
|
||||
@Inject
|
||||
constructor(
|
||||
private val fgsManagerController: FgsManagerController,
|
||||
) {
|
||||
fun init() {
|
||||
fgsManagerController.init()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* 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.graphics.PorterDuff
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.graphics.drawable.RippleDrawable
|
||||
import android.os.UserManager
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.annotation.Keep
|
||||
import com.android.settingslib.Utils
|
||||
import com.android.settingslib.drawable.UserIconDrawable
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.statusbar.phone.MultiUserSwitch
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
// TODO(b/242040009): Remove this file.
|
||||
class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
|
||||
private lateinit var settingsContainer: View
|
||||
private lateinit var multiUserSwitch: MultiUserSwitch
|
||||
private lateinit var multiUserAvatar: ImageView
|
||||
|
||||
private var qsDisabled = false
|
||||
private var expansionAmount = 0f
|
||||
|
||||
/**
|
||||
* Sets the alpha of the background of this view.
|
||||
*
|
||||
* Used from a [TouchAnimator] in the controller.
|
||||
*/
|
||||
var backgroundAlpha: Float = 1f
|
||||
@Keep
|
||||
set(value) {
|
||||
field = value
|
||||
background?.alpha = (value * 255).toInt()
|
||||
}
|
||||
@Keep get
|
||||
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
settingsContainer = findViewById(R.id.settings_button_container)
|
||||
multiUserSwitch = findViewById(R.id.multi_user_switch)
|
||||
multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar)
|
||||
|
||||
// RenderThread is doing more harm than good when touching the header (to expand quick
|
||||
// settings), so disable it for this view
|
||||
if (settingsContainer.background is RippleDrawable) {
|
||||
(settingsContainer.background as RippleDrawable).setForceSoftware(true)
|
||||
}
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
}
|
||||
|
||||
fun disable(
|
||||
state2: Int,
|
||||
multiUserEnabled: Boolean
|
||||
) {
|
||||
val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
|
||||
if (disabled == qsDisabled) return
|
||||
qsDisabled = disabled
|
||||
updateEverything(multiUserEnabled)
|
||||
}
|
||||
|
||||
fun updateEverything(
|
||||
multiUserEnabled: Boolean
|
||||
) {
|
||||
post {
|
||||
updateVisibilities(multiUserEnabled)
|
||||
updateClickabilities()
|
||||
isClickable = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateClickabilities() {
|
||||
multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
|
||||
settingsContainer.isClickable = settingsContainer.visibility == VISIBLE
|
||||
}
|
||||
|
||||
private fun updateVisibilities(
|
||||
multiUserEnabled: Boolean
|
||||
) {
|
||||
settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE
|
||||
multiUserSwitch.visibility = if (multiUserEnabled) VISIBLE else GONE
|
||||
val isDemo = UserManager.isDeviceInDemoMode(context)
|
||||
settingsContainer.visibility = if (isDemo) INVISIBLE else VISIBLE
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||
if (VERBOSE) Log.d(TAG, "FooterActionsView onInterceptTouchEvent ${ev?.string}")
|
||||
return super.onInterceptTouchEvent(ev)
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||
if (VERBOSE) Log.d(TAG, "FooterActionsView onTouchEvent ${event?.string}")
|
||||
return super.onTouchEvent(event)
|
||||
}
|
||||
}
|
||||
private const val TAG = "FooterActionsView"
|
||||
private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE)
|
||||
private val MotionEvent.string
|
||||
get() = "($id): ($x,$y)"
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import javax.inject.Inject
|
||||
|
||||
/** Controller for the footer actions. This manages the initialization of its dependencies. */
|
||||
@SysUISingleton
|
||||
class NewFooterActionsController
|
||||
@Inject
|
||||
// TODO(b/242040009): Rename this to FooterActionsController.
|
||||
constructor(
|
||||
private val fgsManagerController: FgsManagerController,
|
||||
) {
|
||||
fun init() {
|
||||
fgsManagerController.init()
|
||||
}
|
||||
}
|
||||
@@ -215,7 +215,7 @@ public class QSContainerImpl extends FrameLayout implements Dumpable {
|
||||
// Some views are always full width or have dependent padding
|
||||
continue;
|
||||
}
|
||||
if (!(view instanceof FooterActionsView)) {
|
||||
if (view.getId() != R.id.qs_footer_actions) {
|
||||
// Only padding for FooterActionsView, no margin. That way, the background goes
|
||||
// all the way to the edge.
|
||||
LayoutParams lp = (LayoutParams) view.getLayoutParams();
|
||||
|
||||
@@ -1,178 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOOTER_VIEW;
|
||||
import static com.android.systemui.util.PluralMessageFormaterKt.icuMessageFormat;
|
||||
|
||||
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.animation.Expandable;
|
||||
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;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Footer entry point for the foreground service manager
|
||||
*/
|
||||
// TODO(b/242040009): Remove this file.
|
||||
@QSScope
|
||||
public class QSFgsManagerFooter implements View.OnClickListener,
|
||||
FgsManagerController.OnDialogDismissedListener,
|
||||
FgsManagerController.OnNumberOfPackagesChangedListener,
|
||||
VisibilityChangedDispatcher {
|
||||
|
||||
private final View mRootView;
|
||||
private final TextView mFooterText;
|
||||
private final Context mContext;
|
||||
private final Executor mMainExecutor;
|
||||
private final Executor mExecutor;
|
||||
|
||||
private final FgsManagerController mFgsManagerController;
|
||||
|
||||
private boolean mIsInitialized = false;
|
||||
private int mNumPackages;
|
||||
|
||||
private final View mTextContainer;
|
||||
private final View mNumberContainer;
|
||||
private final TextView mNumberView;
|
||||
private final ImageView mDotView;
|
||||
private final ImageView mCollapsedDotView;
|
||||
|
||||
@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);
|
||||
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);
|
||||
mCollapsedDotView = mRootView.findViewById(R.id.fgs_collapsed_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;
|
||||
}
|
||||
|
||||
mFgsManagerController.init();
|
||||
|
||||
mRootView.setOnClickListener(this);
|
||||
|
||||
mIsInitialized = true;
|
||||
}
|
||||
|
||||
public void setListening(boolean listening) {
|
||||
if (listening) {
|
||||
mFgsManagerController.addOnDialogDismissedListener(this);
|
||||
mFgsManagerController.addOnNumberOfPackagesChangedListener(this);
|
||||
mNumPackages = mFgsManagerController.getNumRunningPackages();
|
||||
refreshState();
|
||||
} else {
|
||||
mFgsManagerController.removeOnDialogDismissedListener(this);
|
||||
mFgsManagerController.removeOnNumberOfPackagesChangedListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnVisibilityChangedListener(
|
||||
@Nullable OnVisibilityChangedListener onVisibilityChangedListener) {
|
||||
mVisibilityChangedListener = onVisibilityChangedListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
mFgsManagerController.showDialog(Expandable.fromView(view));
|
||||
}
|
||||
|
||||
public void refreshState() {
|
||||
mExecutor.execute(this::handleRefreshState);
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return mRootView;
|
||||
}
|
||||
|
||||
public void handleRefreshState() {
|
||||
mMainExecutor.execute(() -> {
|
||||
CharSequence text = icuMessageFormat(mContext.getResources(),
|
||||
R.string.fgs_manager_footer_label, mNumPackages);
|
||||
mFooterText.setText(text);
|
||||
mNumberView.setText(Integer.toString(mNumPackages));
|
||||
mNumberView.setContentDescription(text);
|
||||
if (mFgsManagerController.shouldUpdateFooterVisibility()) {
|
||||
mRootView.setVisibility(mNumPackages > 0
|
||||
&& mFgsManagerController.isAvailable().getValue() ? View.VISIBLE
|
||||
: View.GONE);
|
||||
int dotVis = mFgsManagerController.getShowFooterDot().getValue()
|
||||
&& mFgsManagerController.getNewChangesSinceDialogWasDismissed()
|
||||
? View.VISIBLE : View.GONE;
|
||||
mDotView.setVisibility(dotVis);
|
||||
mCollapsedDotView.setVisibility(dotVis);
|
||||
if (mVisibilityChangedListener != null) {
|
||||
mVisibilityChangedListener.onVisibilityChanged(mRootView.getVisibility());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDialogDismissed() {
|
||||
refreshState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNumberOfPackagesChanged(int numPackages) {
|
||||
mNumPackages = numPackages;
|
||||
refreshState();
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.FloatRange;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -48,7 +49,6 @@ import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.animation.ShadeInterpolation;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.media.controls.ui.MediaHost;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.qs.QS;
|
||||
@@ -114,7 +114,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
private final QSFragmentDisableFlagsLogger mQsFragmentDisableFlagsLogger;
|
||||
private final QSTileHost mHost;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
private final NewFooterActionsController mNewFooterActionsController;
|
||||
private final FooterActionsController mFooterActionsController;
|
||||
private final FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
|
||||
private final ListeningAndVisibilityLifecycleOwner mListeningAndVisibilityLifecycleOwner;
|
||||
private boolean mShowCollapsedOnKeyguard;
|
||||
@@ -132,9 +132,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
private QSPanelController mQSPanelController;
|
||||
private QuickQSPanelController mQuickQSPanelController;
|
||||
private QSCustomizerController mQSCustomizerController;
|
||||
@Nullable
|
||||
private FooterActionsController mQSFooterActionController;
|
||||
@Nullable
|
||||
private FooterActionsViewModel mQSFooterActionsViewModel;
|
||||
@Nullable
|
||||
private ScrollListener mScrollListener;
|
||||
@@ -185,7 +182,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
QSFragmentComponent.Factory qsComponentFactory,
|
||||
QSFragmentDisableFlagsLogger qsFragmentDisableFlagsLogger,
|
||||
FalsingManager falsingManager, DumpManager dumpManager, FeatureFlags featureFlags,
|
||||
NewFooterActionsController newFooterActionsController,
|
||||
FooterActionsController footerActionsController,
|
||||
FooterActionsViewModel.Factory footerActionsViewModelFactory) {
|
||||
mRemoteInputQuickSettingsDisabler = remoteInputQsDisabler;
|
||||
mQsMediaHost = qsMediaHost;
|
||||
@@ -199,7 +196,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
mStatusBarStateController = statusBarStateController;
|
||||
mDumpManager = dumpManager;
|
||||
mFeatureFlags = featureFlags;
|
||||
mNewFooterActionsController = newFooterActionsController;
|
||||
mFooterActionsController = footerActionsController;
|
||||
mFooterActionsViewModelFactory = footerActionsViewModelFactory;
|
||||
mListeningAndVisibilityLifecycleOwner = new ListeningAndVisibilityLifecycleOwner();
|
||||
}
|
||||
@@ -226,18 +223,12 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
mQSPanelController.init();
|
||||
mQuickQSPanelController.init();
|
||||
|
||||
if (mFeatureFlags.isEnabled(Flags.NEW_FOOTER_ACTIONS)) {
|
||||
mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
|
||||
this);
|
||||
FooterActionsView footerActionsView = view.findViewById(R.id.qs_footer_actions);
|
||||
FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
|
||||
mListeningAndVisibilityLifecycleOwner);
|
||||
|
||||
mNewFooterActionsController.init();
|
||||
} else {
|
||||
mQSFooterActionController = qsFragmentComponent.getQSFooterActionController();
|
||||
mQSFooterActionController.init();
|
||||
}
|
||||
mQSFooterActionsViewModel = mFooterActionsViewModelFactory.create(/* lifecycleOwner */
|
||||
this);
|
||||
LinearLayout footerActionsView = view.findViewById(R.id.qs_footer_actions);
|
||||
FooterActionsViewBinder.bind(footerActionsView, mQSFooterActionsViewModel,
|
||||
mListeningAndVisibilityLifecycleOwner);
|
||||
mFooterActionsController.init();
|
||||
|
||||
mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
|
||||
mQSPanelScrollView.addOnLayoutChangeListener(
|
||||
@@ -436,9 +427,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
mContainer.disable(state1, state2, animate);
|
||||
mHeader.disable(state1, state2, animate);
|
||||
mFooter.disable(state1, state2, animate);
|
||||
if (mQSFooterActionController != null) {
|
||||
mQSFooterActionController.disable(state2);
|
||||
}
|
||||
updateQsState();
|
||||
}
|
||||
|
||||
@@ -457,11 +445,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
boolean footerVisible = qsPanelVisible && (mQsExpanded || !keyguardShowing
|
||||
|| mHeaderAnimating || mShowCollapsedOnKeyguard);
|
||||
mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
if (mQSFooterActionController != null) {
|
||||
mQSFooterActionController.setVisible(footerVisible);
|
||||
} else {
|
||||
mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible);
|
||||
}
|
||||
mQSFooterActionsViewModel.onVisibilityChangeRequested(footerVisible);
|
||||
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|
||||
|| (mQsExpanded && !mStackScrollerOverscrolling));
|
||||
mQSPanelController.setVisibility(qsPanelVisible ? View.VISIBLE : View.INVISIBLE);
|
||||
@@ -534,9 +518,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
}
|
||||
|
||||
mFooter.setKeyguardShowing(keyguardShowing);
|
||||
if (mQSFooterActionController != null) {
|
||||
mQSFooterActionController.setKeyguardShowing(keyguardShowing);
|
||||
}
|
||||
updateQsState();
|
||||
}
|
||||
|
||||
@@ -552,9 +533,6 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
if (DEBUG) Log.d(TAG, "setListening " + listening);
|
||||
mListening = listening;
|
||||
mQSContainerImplController.setListening(listening && mQsVisible);
|
||||
if (mQSFooterActionController != null) {
|
||||
mQSFooterActionController.setListening(listening && mQsVisible);
|
||||
}
|
||||
mListeningAndVisibilityLifecycleOwner.updateState();
|
||||
updateQsPanelControllerListening();
|
||||
}
|
||||
@@ -665,12 +643,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
|
||||
float footerActionsExpansion =
|
||||
onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
|
||||
if (mQSFooterActionController != null) {
|
||||
mQSFooterActionController.setExpansion(footerActionsExpansion);
|
||||
} else {
|
||||
mQSFooterActionsViewModel.onQuickSettingsExpansionChanged(footerActionsExpansion,
|
||||
mInSplitShade);
|
||||
}
|
||||
mQSFooterActionsViewModel.onQuickSettingsExpansionChanged(footerActionsExpansion,
|
||||
mInSplitShade);
|
||||
mQSPanelController.setRevealExpansion(expansion);
|
||||
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
|
||||
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
|
||||
@@ -835,11 +809,7 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
boolean customizing = isCustomizing();
|
||||
mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
||||
mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
||||
if (mQSFooterActionController != null) {
|
||||
mQSFooterActionController.setVisible(!customizing);
|
||||
} else {
|
||||
mQSFooterActionsViewModel.onVisibilityChangeRequested(!customizing);
|
||||
}
|
||||
mQSFooterActionsViewModel.onVisibilityChangeRequested(!customizing);
|
||||
mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
|
||||
// Let the panel know the position changed and it needs to update where notifications
|
||||
// and whatnot are.
|
||||
@@ -927,6 +897,11 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
updateShowCollapsedOnKeyguard();
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public ListeningAndVisibilityLifecycleOwner getListeningAndVisibilityLifecycleOwner() {
|
||||
return mListeningAndVisibilityLifecycleOwner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(PrintWriter pw, String[] args) {
|
||||
IndentingPrintWriter indentingPw = new IndentingPrintWriter(pw, /* singleIndent= */ " ");
|
||||
@@ -994,7 +969,8 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
|
||||
* - STARTED when mListening == true && mQsVisible == false.
|
||||
* - RESUMED when mListening == true && mQsVisible == true.
|
||||
*/
|
||||
private class ListeningAndVisibilityLifecycleOwner implements LifecycleOwner {
|
||||
@VisibleForTesting
|
||||
class ListeningAndVisibilityLifecycleOwner implements LifecycleOwner {
|
||||
private final LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
|
||||
private boolean mDestroyed = false;
|
||||
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
package com.android.systemui.qs;
|
||||
|
||||
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW;
|
||||
|
||||
import android.app.admin.DevicePolicyEventLogger;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.internal.util.FrameworkStatsLog;
|
||||
import com.android.systemui.FontSizeUtils;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Expandable;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.common.shared.model.Icon;
|
||||
import com.android.systemui.dagger.qualifiers.Background;
|
||||
import com.android.systemui.dagger.qualifiers.Main;
|
||||
import com.android.systemui.qs.dagger.QSScope;
|
||||
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig;
|
||||
import com.android.systemui.security.data.model.SecurityModel;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Named;
|
||||
|
||||
/** ViewController for the footer actions. */
|
||||
// TODO(b/242040009): Remove this class.
|
||||
@QSScope
|
||||
public class QSSecurityFooter extends ViewController<View>
|
||||
implements OnClickListener, VisibilityChangedDispatcher {
|
||||
protected static final String TAG = "QSSecurityFooter";
|
||||
|
||||
private final TextView mFooterText;
|
||||
private final ImageView mPrimaryFooterIcon;
|
||||
private Context mContext;
|
||||
private final Callback mCallback = new Callback();
|
||||
private final SecurityController mSecurityController;
|
||||
private final Handler mMainHandler;
|
||||
private final BroadcastDispatcher mBroadcastDispatcher;
|
||||
private final QSSecurityFooterUtils mQSSecurityFooterUtils;
|
||||
|
||||
protected H mHandler;
|
||||
|
||||
private boolean mIsVisible;
|
||||
private boolean mIsClickable;
|
||||
@Nullable
|
||||
private CharSequence mFooterTextContent = null;
|
||||
private Icon mFooterIcon;
|
||||
|
||||
@Nullable
|
||||
private VisibilityChangedDispatcher.OnVisibilityChangedListener mVisibilityChangedListener;
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(
|
||||
DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG)) {
|
||||
showDeviceMonitoringDialog();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Inject
|
||||
QSSecurityFooter(@Named(QS_SECURITY_FOOTER_VIEW) View rootView,
|
||||
@Main Handler mainHandler, SecurityController securityController,
|
||||
@Background Looper bgLooper, BroadcastDispatcher broadcastDispatcher,
|
||||
QSSecurityFooterUtils qSSecurityFooterUtils) {
|
||||
super(rootView);
|
||||
mFooterText = mView.findViewById(R.id.footer_text);
|
||||
mPrimaryFooterIcon = mView.findViewById(R.id.primary_footer_icon);
|
||||
mFooterIcon = new Icon.Resource(
|
||||
R.drawable.ic_info_outline, /* contentDescription= */ null);
|
||||
mContext = rootView.getContext();
|
||||
mSecurityController = securityController;
|
||||
mMainHandler = mainHandler;
|
||||
mHandler = new H(bgLooper);
|
||||
mBroadcastDispatcher = broadcastDispatcher;
|
||||
mQSSecurityFooterUtils = qSSecurityFooterUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
// Use background handler, as it's the same thread that handleClick is called on.
|
||||
mBroadcastDispatcher.registerReceiverWithHandler(mReceiver,
|
||||
new IntentFilter(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG),
|
||||
mHandler, UserHandle.ALL);
|
||||
mView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewDetached() {
|
||||
mBroadcastDispatcher.unregisterReceiver(mReceiver);
|
||||
mView.setOnClickListener(null);
|
||||
}
|
||||
|
||||
public void setListening(boolean listening) {
|
||||
if (listening) {
|
||||
mSecurityController.addCallback(mCallback);
|
||||
refreshState();
|
||||
} else {
|
||||
mSecurityController.removeCallback(mCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
|
||||
int padding = r.getDimensionPixelSize(R.dimen.qs_footer_padding);
|
||||
mView.setPaddingRelative(padding, 0, padding, 0);
|
||||
mView.setBackground(mContext.getDrawable(R.drawable.qs_security_footer_background));
|
||||
}
|
||||
|
||||
public View getView() {
|
||||
return mView;
|
||||
}
|
||||
|
||||
public boolean hasFooter() {
|
||||
return mView.getVisibility() != View.GONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (!hasFooter()) return;
|
||||
mHandler.sendEmptyMessage(H.CLICK);
|
||||
}
|
||||
|
||||
private void handleClick() {
|
||||
showDeviceMonitoringDialog();
|
||||
DevicePolicyEventLogger
|
||||
.createEvent(FrameworkStatsLog.DEVICE_POLICY_EVENT__EVENT_ID__DO_USER_INFO_CLICKED)
|
||||
.write();
|
||||
}
|
||||
|
||||
// TODO(b/242040009): Remove this.
|
||||
public void showDeviceMonitoringDialog() {
|
||||
mQSSecurityFooterUtils.showDeviceMonitoringDialog(mContext, Expandable.fromView(mView));
|
||||
}
|
||||
|
||||
public void refreshState() {
|
||||
mHandler.sendEmptyMessage(H.REFRESH_STATE);
|
||||
}
|
||||
|
||||
private void handleRefreshState() {
|
||||
SecurityModel securityModel = SecurityModel.create(mSecurityController);
|
||||
SecurityButtonConfig buttonConfig = mQSSecurityFooterUtils.getButtonConfig(securityModel);
|
||||
|
||||
if (buttonConfig == null) {
|
||||
mIsVisible = false;
|
||||
} else {
|
||||
mIsVisible = true;
|
||||
mIsClickable = buttonConfig.isClickable();
|
||||
mFooterTextContent = buttonConfig.getText();
|
||||
mFooterIcon = buttonConfig.getIcon();
|
||||
}
|
||||
|
||||
// Update the UI.
|
||||
mMainHandler.post(mUpdatePrimaryIcon);
|
||||
mMainHandler.post(mUpdateDisplayState);
|
||||
}
|
||||
|
||||
private final Runnable mUpdatePrimaryIcon = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mFooterIcon instanceof Icon.Loaded) {
|
||||
mPrimaryFooterIcon.setImageDrawable(((Icon.Loaded) mFooterIcon).getDrawable());
|
||||
} else if (mFooterIcon instanceof Icon.Resource) {
|
||||
mPrimaryFooterIcon.setImageResource(((Icon.Resource) mFooterIcon).getRes());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mUpdateDisplayState = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mFooterTextContent != null) {
|
||||
mFooterText.setText(mFooterTextContent);
|
||||
}
|
||||
mView.setVisibility(mIsVisible ? View.VISIBLE : View.GONE);
|
||||
if (mVisibilityChangedListener != null) {
|
||||
mVisibilityChangedListener.onVisibilityChanged(mView.getVisibility());
|
||||
}
|
||||
|
||||
if (mIsVisible && mIsClickable) {
|
||||
mView.setClickable(true);
|
||||
mView.findViewById(R.id.footer_icon).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
mView.setClickable(false);
|
||||
mView.findViewById(R.id.footer_icon).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private class Callback implements SecurityController.SecurityControllerCallback {
|
||||
@Override
|
||||
public void onStateChanged() {
|
||||
refreshState();
|
||||
}
|
||||
}
|
||||
|
||||
private class H extends Handler {
|
||||
private static final int CLICK = 0;
|
||||
private static final int REFRESH_STATE = 1;
|
||||
|
||||
private H(Looper looper) {
|
||||
super(looper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
String name = null;
|
||||
try {
|
||||
if (msg.what == REFRESH_STATE) {
|
||||
name = "handleRefreshState";
|
||||
handleRefreshState();
|
||||
} else if (msg.what == CLICK) {
|
||||
name = "handleClick";
|
||||
handleClick();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
final String error = "Error in " + name;
|
||||
Log.w(TAG, error, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import com.android.systemui.battery.BatteryMeterView;
|
||||
import com.android.systemui.dagger.qualifiers.RootView;
|
||||
import com.android.systemui.plugins.qs.QS;
|
||||
import com.android.systemui.privacy.OngoingPrivacyChip;
|
||||
import com.android.systemui.qs.FooterActionsView;
|
||||
import com.android.systemui.qs.QSContainerImpl;
|
||||
import com.android.systemui.qs.QSFooter;
|
||||
import com.android.systemui.qs.QSFooterView;
|
||||
@@ -51,8 +50,6 @@ import dagger.Provides;
|
||||
*/
|
||||
@Module
|
||||
public interface QSFragmentModule {
|
||||
String QS_FGS_MANAGER_FOOTER_VIEW = "qs_fgs_manager_footer";
|
||||
String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
|
||||
String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
|
||||
String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media";
|
||||
|
||||
@@ -119,16 +116,6 @@ public interface QSFragmentModule {
|
||||
return view.findViewById(R.id.qs_footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a {@link FooterActionsView}.
|
||||
*
|
||||
* This will replace a ViewStub either in {@link QSFooterView} or in {@link QSContainerImpl}.
|
||||
*/
|
||||
@Provides
|
||||
static FooterActionsView providesQSFooterActionsView(@RootView View view) {
|
||||
return view.findViewById(R.id.qs_footer_actions);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@QSScope
|
||||
@@ -144,18 +131,6 @@ public interface QSFragmentModule {
|
||||
return view.findViewById(R.id.qs_customize);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@QSScope
|
||||
@Named(QS_SECURITY_FOOTER_VIEW)
|
||||
static View providesQSSecurityFooterView(
|
||||
@QSThemedContext LayoutInflater layoutInflater,
|
||||
FooterActionsView footerActionsView
|
||||
) {
|
||||
return layoutInflater.inflate(R.layout.quick_settings_security_footer, footerActionsView,
|
||||
false);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@Named(QS_USING_MEDIA_PLAYER)
|
||||
@@ -183,15 +158,4 @@ public interface QSFragmentModule {
|
||||
static StatusIconContainer providesStatusIconContainer(QuickStatusBarHeader qsHeader) {
|
||||
return qsHeader.findViewById(R.id.statusIcons);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@QSScope
|
||||
@Named(QS_FGS_MANAGER_FOOTER_VIEW)
|
||||
static View providesQSFgsManagerFooterView(
|
||||
@QSThemedContext LayoutInflater layoutInflater,
|
||||
FooterActionsView footerActionsView
|
||||
) {
|
||||
return layoutInflater.inflate(R.layout.fgs_footer, footerActionsView, false);
|
||||
}
|
||||
}
|
||||
@@ -35,35 +35,31 @@ import com.android.systemui.animation.Expandable
|
||||
import com.android.systemui.common.ui.binder.IconViewBinder
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import com.android.systemui.people.ui.view.PeopleViewBinder.bind
|
||||
import com.android.systemui.qs.FooterActionsView
|
||||
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsButtonViewModel
|
||||
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsForegroundServicesButtonViewModel
|
||||
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsSecurityButtonViewModel
|
||||
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel
|
||||
import kotlin.math.roundToInt
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/** A ViewBinder for [FooterActionsViewBinder]. */
|
||||
object FooterActionsViewBinder {
|
||||
/**
|
||||
* Create a [FooterActionsView] that can later be [bound][bind] to a [FooterActionsViewModel].
|
||||
*/
|
||||
/** Create a view that can later be [bound][bind] to a [FooterActionsViewModel]. */
|
||||
@JvmStatic
|
||||
fun create(context: Context): FooterActionsView {
|
||||
fun create(context: Context): LinearLayout {
|
||||
return LayoutInflater.from(context).inflate(R.layout.footer_actions, /* root= */ null)
|
||||
as FooterActionsView
|
||||
as LinearLayout
|
||||
}
|
||||
|
||||
/** Bind [view] to [viewModel]. */
|
||||
@JvmStatic
|
||||
fun bind(
|
||||
view: FooterActionsView,
|
||||
view: LinearLayout,
|
||||
viewModel: FooterActionsViewModel,
|
||||
qsVisibilityLifecycleOwner: LifecycleOwner,
|
||||
) {
|
||||
// Remove all children of the FooterActionsView that are used by the old implementation.
|
||||
// TODO(b/242040009): Clean up the XML once the old implementation is removed.
|
||||
view.removeAllViews()
|
||||
view.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
|
||||
|
||||
// Add the views used by this new implementation.
|
||||
val context = view.context
|
||||
@@ -117,7 +113,11 @@ object FooterActionsViewBinder {
|
||||
}
|
||||
|
||||
launch { viewModel.alpha.collect { view.alpha = it } }
|
||||
launch { viewModel.backgroundAlpha.collect { view.backgroundAlpha = it } }
|
||||
launch {
|
||||
viewModel.backgroundAlpha.collect {
|
||||
view.background?.alpha = (it * 255).roundToInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for model changes only when QS are visible.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.systemui.security.data.model
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.statusbar.policy.SecurityController
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -55,8 +56,8 @@ data class SecurityModel(
|
||||
* Important: This method should be called from a background thread as this will do a lot of
|
||||
* binder calls.
|
||||
*/
|
||||
// TODO(b/242040009): Remove this.
|
||||
@JvmStatic
|
||||
@VisibleForTesting
|
||||
fun create(securityController: SecurityController): SecurityModel {
|
||||
val deviceAdminInfo =
|
||||
if (securityController.isParentalControlsEnabled) {
|
||||
|
||||
@@ -1,171 +0,0 @@
|
||||
/*
|
||||
* 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.statusbar.phone;
|
||||
|
||||
import static com.android.systemui.DejankUtils.whitelistIpcs;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator;
|
||||
import com.android.systemui.animation.Expandable;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.qs.FooterActionsView;
|
||||
import com.android.systemui.qs.dagger.QSScope;
|
||||
import com.android.systemui.qs.user.UserSwitchDialogController;
|
||||
import com.android.systemui.statusbar.policy.BaseUserSwitcherAdapter;
|
||||
import com.android.systemui.statusbar.policy.UserSwitcherController;
|
||||
import com.android.systemui.user.UserSwitcherActivity;
|
||||
import com.android.systemui.util.ViewController;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/** View Controller for {@link MultiUserSwitch}. */
|
||||
// TODO(b/242040009): Remove this file.
|
||||
public class MultiUserSwitchController extends ViewController<MultiUserSwitch> {
|
||||
private final UserManager mUserManager;
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
private final FalsingManager mFalsingManager;
|
||||
private final UserSwitchDialogController mUserSwitchDialogController;
|
||||
private final ActivityStarter mActivityStarter;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
private BaseUserSwitcherAdapter mUserListener;
|
||||
|
||||
private final View.OnClickListener mOnClickListener = new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFeatureFlags.isEnabled(Flags.FULL_SCREEN_USER_SWITCHER)) {
|
||||
Intent intent = new Intent(v.getContext(), UserSwitcherActivity.class);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
mActivityStarter.startActivity(intent, true /* dismissShade */,
|
||||
ActivityLaunchAnimator.Controller.fromView(v, null),
|
||||
true /* showOverlockscreenwhenlocked */, UserHandle.SYSTEM);
|
||||
} else {
|
||||
mUserSwitchDialogController.showDialog(v.getContext(), Expandable.fromView(v));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@QSScope
|
||||
public static class Factory {
|
||||
private final UserManager mUserManager;
|
||||
private final UserSwitcherController mUserSwitcherController;
|
||||
private final FalsingManager mFalsingManager;
|
||||
private final UserSwitchDialogController mUserSwitchDialogController;
|
||||
private final ActivityStarter mActivityStarter;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
@Inject
|
||||
public Factory(UserManager userManager, UserSwitcherController userSwitcherController,
|
||||
FalsingManager falsingManager,
|
||||
UserSwitchDialogController userSwitchDialogController, FeatureFlags featureFlags,
|
||||
ActivityStarter activityStarter) {
|
||||
mUserManager = userManager;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mFalsingManager = falsingManager;
|
||||
mUserSwitchDialogController = userSwitchDialogController;
|
||||
mActivityStarter = activityStarter;
|
||||
mFeatureFlags = featureFlags;
|
||||
}
|
||||
|
||||
public MultiUserSwitchController create(FooterActionsView view) {
|
||||
return new MultiUserSwitchController(view.findViewById(R.id.multi_user_switch),
|
||||
mUserManager, mUserSwitcherController,
|
||||
mFalsingManager, mUserSwitchDialogController, mFeatureFlags,
|
||||
mActivityStarter);
|
||||
}
|
||||
}
|
||||
|
||||
private MultiUserSwitchController(MultiUserSwitch view, UserManager userManager,
|
||||
UserSwitcherController userSwitcherController,
|
||||
FalsingManager falsingManager, UserSwitchDialogController userSwitchDialogController,
|
||||
FeatureFlags featureFlags, ActivityStarter activityStarter) {
|
||||
super(view);
|
||||
mUserManager = userManager;
|
||||
mUserSwitcherController = userSwitcherController;
|
||||
mFalsingManager = falsingManager;
|
||||
mUserSwitchDialogController = userSwitchDialogController;
|
||||
mFeatureFlags = featureFlags;
|
||||
mActivityStarter = activityStarter;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInit() {
|
||||
registerListener();
|
||||
mView.refreshContentDescription(getCurrentUser());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewAttached() {
|
||||
mView.setOnClickListener(mOnClickListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onViewDetached() {
|
||||
mView.setOnClickListener(null);
|
||||
}
|
||||
|
||||
private void registerListener() {
|
||||
if (mUserManager.isUserSwitcherEnabled() && mUserListener == null) {
|
||||
|
||||
final UserSwitcherController controller = mUserSwitcherController;
|
||||
if (controller != null) {
|
||||
mUserListener = new BaseUserSwitcherAdapter(controller) {
|
||||
@Override
|
||||
public void notifyDataSetChanged() {
|
||||
mView.refreshContentDescription(getCurrentUser());
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
mView.refreshContentDescription(getCurrentUser());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getCurrentUser() {
|
||||
// TODO(b/138661450)
|
||||
if (whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled())) {
|
||||
return mUserSwitcherController.getCurrentUserName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns true if view should be made visible. */
|
||||
public boolean isMultiUserEnabled() {
|
||||
// TODO(b/138661450) Move IPC calls to background
|
||||
return whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
|
||||
getResources().getBoolean(R.bool.qs_show_user_switcher_for_single_user)));
|
||||
}
|
||||
}
|
||||
@@ -1,440 +0,0 @@
|
||||
package com.android.systemui.qs
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.UserManager
|
||||
import android.provider.Settings
|
||||
import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.ViewUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.R
|
||||
import com.android.systemui.animation.ActivityLaunchAnimator
|
||||
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.policy.DeviceProvisionedController
|
||||
import com.android.systemui.statusbar.policy.FakeConfigurationController
|
||||
import com.android.systemui.statusbar.policy.UserInfoController
|
||||
import com.android.systemui.util.mockito.capture
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.android.systemui.utils.leaks.LeakCheckedTest
|
||||
import com.google.common.truth.Expect
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import javax.inject.Provider
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.anyBoolean
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.Mockito.atLeastOnce
|
||||
import org.mockito.Mockito.clearInvocations
|
||||
import org.mockito.Mockito.never
|
||||
import org.mockito.Mockito.reset
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
class FooterActionsControllerTest : LeakCheckedTest() {
|
||||
|
||||
@get:Rule var expect: Expect = Expect.create()
|
||||
|
||||
@Mock private lateinit var userManager: UserManager
|
||||
@Mock private lateinit var userTracker: UserTracker
|
||||
@Mock private lateinit var activityStarter: ActivityStarter
|
||||
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
|
||||
@Mock private lateinit var userInfoController: UserInfoController
|
||||
@Mock private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
|
||||
@Mock private lateinit var multiUserSwitchController: MultiUserSwitchController
|
||||
@Mock private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
|
||||
@Mock private lateinit var globalActionsDialog: GlobalActionsDialogLite
|
||||
@Mock private lateinit var uiEventLogger: UiEventLogger
|
||||
@Mock private lateinit var securityFooterController: QSSecurityFooter
|
||||
@Mock private lateinit var fgsManagerController: QSFgsManagerFooter
|
||||
@Captor
|
||||
private lateinit var visibilityChangedCaptor:
|
||||
ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener>
|
||||
|
||||
private lateinit var controller: FooterActionsController
|
||||
|
||||
private val configurationController = FakeConfigurationController()
|
||||
private val metricsLogger: MetricsLogger = FakeMetricsLogger()
|
||||
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
|
||||
private lateinit var view: FooterActionsView
|
||||
private lateinit var testableLooper: TestableLooper
|
||||
private lateinit var fakeSettings: FakeSettings
|
||||
private lateinit var securityFooter: View
|
||||
private lateinit var fgsFooter: View
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// We want to make sure testable resources are always used
|
||||
context.ensureTestableResources()
|
||||
|
||||
MockitoAnnotations.initMocks(this)
|
||||
testableLooper = TestableLooper.get(this)
|
||||
fakeSettings = FakeSettings()
|
||||
|
||||
whenever(multiUserSwitchControllerFactory.create(any()))
|
||||
.thenReturn(multiUserSwitchController)
|
||||
whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog)
|
||||
|
||||
securityFooter = View(mContext)
|
||||
fgsFooter = View(mContext)
|
||||
|
||||
whenever(securityFooterController.view).thenReturn(securityFooter)
|
||||
whenever(fgsManagerController.view).thenReturn(fgsFooter)
|
||||
|
||||
view = inflateView()
|
||||
|
||||
controller = constructFooterActionsController(view)
|
||||
controller.init()
|
||||
ViewUtils.attachView(view)
|
||||
// View looper is the testable looper associated with the test
|
||||
testableLooper.processAllMessages()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
if (view.isAttachedToWindow) {
|
||||
ViewUtils.detachView(view)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInitializesControllers() {
|
||||
verify(multiUserSwitchController).init()
|
||||
verify(fgsManagerController).init()
|
||||
verify(securityFooterController).init()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLogPowerMenuClick() {
|
||||
controller.visible = 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() {
|
||||
val captor = ArgumentCaptor.forClass(Intent::class.java)
|
||||
whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(true)
|
||||
view.findViewById<View>(R.id.settings_button_container).performClick()
|
||||
|
||||
verify(activityStarter)
|
||||
.startActivity(capture(captor), anyBoolean(), any<ActivityLaunchAnimator.Controller>())
|
||||
|
||||
assertThat(captor.value.action).isEqualTo(Settings.ACTION_SETTINGS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSettings_UserNotSetup() {
|
||||
whenever(deviceProvisionedController.isCurrentUserSetup).thenReturn(false)
|
||||
view.findViewById<View>(R.id.settings_button_container).performClick()
|
||||
// Verify Settings wasn't launched.
|
||||
verify(activityStarter, never())
|
||||
.startActivity(any(), anyBoolean(), any<ActivityLaunchAnimator.Controller>())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultiUserSwitchUpdatedWhenExpansionStarts() {
|
||||
// When expansion starts, listening is set to true
|
||||
val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
|
||||
|
||||
assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
|
||||
|
||||
whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
|
||||
|
||||
controller.setListening(true)
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultiUserSwitchUpdatedWhenSettingChanged() {
|
||||
// Always listening to setting while View is attached
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
|
||||
assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
|
||||
|
||||
// The setting is only used as an indicator for whether the view should refresh. The actual
|
||||
// value of the setting is ignored; isMultiUserEnabled is the source of truth
|
||||
whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
|
||||
|
||||
// Changing the value of USER_SWITCHER_ENABLED should cause the view to update
|
||||
fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId)
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
assertThat(multiUserSwitch.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMultiUserSettingNotListenedAfterDetach() {
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
val multiUserSwitch = view.requireViewById<View>(R.id.multi_user_switch)
|
||||
assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
|
||||
|
||||
ViewUtils.detachView(view)
|
||||
|
||||
// The setting is only used as an indicator for whether the view should refresh. The actual
|
||||
// value of the setting is ignored; isMultiUserEnabled is the source of truth
|
||||
whenever(multiUserSwitchController.isMultiUserEnabled).thenReturn(true)
|
||||
|
||||
// Changing the value of USER_SWITCHER_ENABLED should cause the view to update
|
||||
fakeSettings.putIntForUser(Settings.Global.USER_SWITCHER_ENABLED, 1, userTracker.userId)
|
||||
testableLooper.processAllMessages()
|
||||
|
||||
assertThat(multiUserSwitch.visibility).isNotEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testCleanUpGAD() {
|
||||
reset(globalActionsDialogProvider)
|
||||
// We are creating a new controller, so detach the views from it
|
||||
(securityFooter.parent as ViewGroup).removeView(securityFooter)
|
||||
(fgsFooter.parent as ViewGroup).removeView(fgsFooter)
|
||||
|
||||
whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog)
|
||||
val view = inflateView()
|
||||
controller = constructFooterActionsController(view)
|
||||
controller.init()
|
||||
verify(globalActionsDialogProvider, never()).get()
|
||||
|
||||
// GAD is constructed during attachment
|
||||
ViewUtils.attachView(view)
|
||||
testableLooper.processAllMessages()
|
||||
verify(globalActionsDialogProvider).get()
|
||||
|
||||
ViewUtils.detachView(view)
|
||||
testableLooper.processAllMessages()
|
||||
verify(globalActionsDialog).destroy()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeparatorVisibility_noneVisible_gone() {
|
||||
verify(securityFooterController)
|
||||
.setOnVisibilityChangedListener(capture(visibilityChangedCaptor))
|
||||
val listener = visibilityChangedCaptor.value
|
||||
val separator = controller.securityFootersSeparator
|
||||
|
||||
setVisibilities(securityFooterVisible = false, fgsFooterVisible = false, listener)
|
||||
assertThat(separator.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeparatorVisibility_onlySecurityFooterVisible_gone() {
|
||||
verify(securityFooterController)
|
||||
.setOnVisibilityChangedListener(capture(visibilityChangedCaptor))
|
||||
val listener = visibilityChangedCaptor.value
|
||||
val separator = controller.securityFootersSeparator
|
||||
|
||||
setVisibilities(securityFooterVisible = true, fgsFooterVisible = false, listener)
|
||||
assertThat(separator.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeparatorVisibility_onlyFgsFooterVisible_gone() {
|
||||
verify(securityFooterController)
|
||||
.setOnVisibilityChangedListener(capture(visibilityChangedCaptor))
|
||||
val listener = visibilityChangedCaptor.value
|
||||
val separator = controller.securityFootersSeparator
|
||||
|
||||
setVisibilities(securityFooterVisible = false, fgsFooterVisible = true, listener)
|
||||
assertThat(separator.visibility).isEqualTo(View.GONE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSeparatorVisibility_bothVisible_visible() {
|
||||
verify(securityFooterController)
|
||||
.setOnVisibilityChangedListener(capture(visibilityChangedCaptor))
|
||||
val listener = visibilityChangedCaptor.value
|
||||
val separator = controller.securityFootersSeparator
|
||||
|
||||
setVisibilities(securityFooterVisible = true, fgsFooterVisible = true, listener)
|
||||
assertThat(separator.visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testFgsFooterCollapsed() {
|
||||
verify(securityFooterController)
|
||||
.setOnVisibilityChangedListener(capture(visibilityChangedCaptor))
|
||||
val listener = visibilityChangedCaptor.value
|
||||
|
||||
val booleanCaptor = ArgumentCaptor.forClass(Boolean::class.java)
|
||||
|
||||
clearInvocations(fgsManagerController)
|
||||
setVisibilities(securityFooterVisible = false, fgsFooterVisible = true, listener)
|
||||
verify(fgsManagerController, atLeastOnce()).setCollapsed(capture(booleanCaptor))
|
||||
assertThat(booleanCaptor.allValues.last()).isFalse()
|
||||
|
||||
clearInvocations(fgsManagerController)
|
||||
setVisibilities(securityFooterVisible = true, fgsFooterVisible = true, listener)
|
||||
verify(fgsManagerController, atLeastOnce()).setCollapsed(capture(booleanCaptor))
|
||||
assertThat(booleanCaptor.allValues.last()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setExpansion_inSplitShade_alphaFollowsExpansion() {
|
||||
enableSplitShade()
|
||||
|
||||
controller.setExpansion(0f)
|
||||
expect.that(view.alpha).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.25f)
|
||||
expect.that(view.alpha).isEqualTo(0.25f)
|
||||
|
||||
controller.setExpansion(0.5f)
|
||||
expect.that(view.alpha).isEqualTo(0.5f)
|
||||
|
||||
controller.setExpansion(0.75f)
|
||||
expect.that(view.alpha).isEqualTo(0.75f)
|
||||
|
||||
controller.setExpansion(1f)
|
||||
expect.that(view.alpha).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setExpansion_inSplitShade_backgroundAlphaFollowsExpansion_with_0_9_delay() {
|
||||
enableSplitShade()
|
||||
|
||||
controller.setExpansion(0f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.5f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.9f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.91f)
|
||||
expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.1f)
|
||||
|
||||
controller.setExpansion(0.95f)
|
||||
expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.5f)
|
||||
|
||||
controller.setExpansion(1f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setExpansion_inSingleShade_alphaFollowsExpansion_with_0_9_delay() {
|
||||
disableSplitShade()
|
||||
|
||||
controller.setExpansion(0f)
|
||||
expect.that(view.alpha).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.5f)
|
||||
expect.that(view.alpha).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.9f)
|
||||
expect.that(view.alpha).isEqualTo(0f)
|
||||
|
||||
controller.setExpansion(0.91f)
|
||||
expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.1f)
|
||||
|
||||
controller.setExpansion(0.95f)
|
||||
expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.5f)
|
||||
|
||||
controller.setExpansion(1f)
|
||||
expect.that(view.alpha).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun setExpansion_inSingleShade_backgroundAlphaAlways1() {
|
||||
disableSplitShade()
|
||||
|
||||
controller.setExpansion(0f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
|
||||
|
||||
controller.setExpansion(0.5f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
|
||||
|
||||
controller.setExpansion(1f)
|
||||
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
|
||||
}
|
||||
|
||||
private fun setVisibilities(
|
||||
securityFooterVisible: Boolean,
|
||||
fgsFooterVisible: Boolean,
|
||||
listener: VisibilityChangedDispatcher.OnVisibilityChangedListener
|
||||
) {
|
||||
securityFooter.visibility = if (securityFooterVisible) View.VISIBLE else View.GONE
|
||||
listener.onVisibilityChanged(securityFooter.visibility)
|
||||
fgsFooter.visibility = if (fgsFooterVisible) View.VISIBLE else View.GONE
|
||||
listener.onVisibilityChanged(fgsFooter.visibility)
|
||||
}
|
||||
|
||||
private fun inflateView(): FooterActionsView {
|
||||
return LayoutInflater.from(context).inflate(R.layout.footer_actions, null)
|
||||
as FooterActionsView
|
||||
}
|
||||
|
||||
private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController {
|
||||
return FooterActionsController(
|
||||
view,
|
||||
multiUserSwitchControllerFactory,
|
||||
activityStarter,
|
||||
userManager,
|
||||
userTracker,
|
||||
userInfoController,
|
||||
deviceProvisionedController,
|
||||
securityFooterController,
|
||||
fgsManagerController,
|
||||
falsingManager,
|
||||
metricsLogger,
|
||||
globalActionsDialogProvider,
|
||||
uiEventLogger,
|
||||
showPMLiteButton = true,
|
||||
fakeSettings,
|
||||
Handler(testableLooper.looper),
|
||||
configurationController)
|
||||
}
|
||||
|
||||
private fun enableSplitShade() {
|
||||
setSplitShadeEnabled(true)
|
||||
}
|
||||
|
||||
private fun disableSplitShade() {
|
||||
setSplitShadeEnabled(false)
|
||||
}
|
||||
|
||||
private fun setSplitShadeEnabled(enabled: Boolean) {
|
||||
overrideResource(R.bool.config_use_split_notification_shade, enabled)
|
||||
configurationController.notifyConfigurationChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private const val FLOAT_TOLERANCE = 0.01f
|
||||
|
||||
private val View.backgroundAlphaFraction: Float?
|
||||
get() {
|
||||
return if (background != null) {
|
||||
background.alpha / 255f
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -32,6 +33,7 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.app.Fragment;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
@@ -42,6 +44,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.keyguard.BouncerPanelExpansionCalculator;
|
||||
@@ -50,12 +53,12 @@ import com.android.systemui.SysuiBaseFragmentTest;
|
||||
import com.android.systemui.animation.ShadeInterpolation;
|
||||
import com.android.systemui.dump.DumpManager;
|
||||
import com.android.systemui.flags.FakeFeatureFlags;
|
||||
import com.android.systemui.flags.Flags;
|
||||
import com.android.systemui.media.controls.ui.MediaHost;
|
||||
import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.qs.customize.QSCustomizerController;
|
||||
import com.android.systemui.qs.dagger.QSFragmentComponent;
|
||||
import com.android.systemui.qs.external.TileServiceRequestController;
|
||||
import com.android.systemui.qs.footer.ui.binder.FooterActionsViewBinder;
|
||||
import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel;
|
||||
import com.android.systemui.statusbar.CommandQueue;
|
||||
import com.android.systemui.statusbar.StatusBarState;
|
||||
@@ -99,6 +102,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
@Mock private QSAnimator mQSAnimator;
|
||||
@Mock private SysuiStatusBarStateController mStatusBarStateController;
|
||||
@Mock private QSSquishinessController mSquishinessController;
|
||||
@Mock private FooterActionsViewModel mFooterActionsViewModel;
|
||||
@Mock private FooterActionsViewModel.Factory mFooterActionsViewModelFactory;
|
||||
private View mQsFragmentView;
|
||||
|
||||
public QSFragmentTest() {
|
||||
@@ -245,7 +250,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
|
||||
squishinessFraction);
|
||||
|
||||
verify(mQSFooterActionController).setExpansion(panelExpansionFraction);
|
||||
verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
|
||||
panelExpansionFraction, /* isInSplitShade= */ true);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -262,7 +268,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
|
||||
squishinessFraction);
|
||||
|
||||
verify(mQSFooterActionController).setExpansion(expansion);
|
||||
verify(mFooterActionsViewModel).onQuickSettingsExpansionChanged(
|
||||
expansion, /* isInSplitShade= */ false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -379,6 +386,13 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
assertThat(mQsFragmentView.getTranslationY()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private Lifecycle.State getListeningAndVisibilityLifecycleState() {
|
||||
return getFragment()
|
||||
.getListeningAndVisibilityLifecycleOwner()
|
||||
.getLifecycle()
|
||||
.getCurrentState();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setListeningFalse_notVisible() {
|
||||
QSFragment fragment = resumeAndGetFragment();
|
||||
@@ -387,7 +401,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
|
||||
fragment.setListening(false);
|
||||
verify(mQSContainerImplController).setListening(false);
|
||||
verify(mQSFooterActionController).setListening(false);
|
||||
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
|
||||
verify(mQSPanelController).setListening(eq(false), anyBoolean());
|
||||
}
|
||||
|
||||
@@ -399,7 +413,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
|
||||
fragment.setListening(true);
|
||||
verify(mQSContainerImplController).setListening(false);
|
||||
verify(mQSFooterActionController).setListening(false);
|
||||
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.STARTED);
|
||||
verify(mQSPanelController).setListening(eq(false), anyBoolean());
|
||||
}
|
||||
|
||||
@@ -411,7 +425,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
|
||||
fragment.setListening(false);
|
||||
verify(mQSContainerImplController).setListening(false);
|
||||
verify(mQSFooterActionController).setListening(false);
|
||||
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.CREATED);
|
||||
verify(mQSPanelController).setListening(eq(false), anyBoolean());
|
||||
}
|
||||
|
||||
@@ -423,7 +437,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
|
||||
fragment.setListening(true);
|
||||
verify(mQSContainerImplController).setListening(true);
|
||||
verify(mQSFooterActionController).setListening(true);
|
||||
assertThat(getListeningAndVisibilityLifecycleState()).isEqualTo(Lifecycle.State.RESUMED);
|
||||
verify(mQSPanelController).setListening(eq(true), anyBoolean());
|
||||
}
|
||||
|
||||
@@ -480,7 +494,6 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
setUpOther();
|
||||
|
||||
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
|
||||
featureFlags.set(Flags.NEW_FOOTER_ACTIONS, false);
|
||||
return new QSFragment(
|
||||
new RemoteInputQuickSettingsDisabler(
|
||||
context, commandQueue, mock(ConfigurationController.class)),
|
||||
@@ -495,8 +508,8 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
mFalsingManager,
|
||||
mock(DumpManager.class),
|
||||
featureFlags,
|
||||
mock(NewFooterActionsController.class),
|
||||
mock(FooterActionsViewModel.Factory.class));
|
||||
mock(FooterActionsController.class),
|
||||
mFooterActionsViewModelFactory);
|
||||
}
|
||||
|
||||
private void setUpOther() {
|
||||
@@ -505,6 +518,7 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
when(mQSContainerImplController.getView()).thenReturn(mContainer);
|
||||
when(mQSPanelController.getTileLayout()).thenReturn(mQQsTileLayout);
|
||||
when(mQuickQSPanelController.getTileLayout()).thenReturn(mQsTileLayout);
|
||||
when(mFooterActionsViewModelFactory.create(any())).thenReturn(mFooterActionsViewModel);
|
||||
}
|
||||
|
||||
private void setUpMedia() {
|
||||
@@ -519,15 +533,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
|
||||
.thenReturn(mQSPanelScrollView);
|
||||
when(mQsFragmentView.findViewById(R.id.header)).thenReturn(mHeader);
|
||||
when(mQsFragmentView.findViewById(android.R.id.edit)).thenReturn(new View(mContext));
|
||||
when(mQsFragmentView.findViewById(R.id.qs_footer_actions)).thenAnswer(
|
||||
invocation -> FooterActionsViewBinder.create(mContext));
|
||||
}
|
||||
|
||||
private void setUpInflater() {
|
||||
LayoutInflater realInflater = LayoutInflater.from(mContext);
|
||||
|
||||
when(mLayoutInflater.cloneInContext(any(Context.class))).thenReturn(mLayoutInflater);
|
||||
when(mLayoutInflater.inflate(anyInt(), any(ViewGroup.class), anyBoolean()))
|
||||
.thenReturn(mQsFragmentView);
|
||||
when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class), anyBoolean()))
|
||||
.thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0),
|
||||
(ViewGroup) invocation.getArgument(1),
|
||||
(boolean) invocation.getArgument(2)));
|
||||
when(mLayoutInflater.inflate(anyInt(), nullable(ViewGroup.class)))
|
||||
.thenAnswer((invocation) -> inflate(realInflater, (int) invocation.getArgument(0),
|
||||
(ViewGroup) invocation.getArgument(1)));
|
||||
mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater);
|
||||
}
|
||||
|
||||
private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root) {
|
||||
return inflate(realInflater, layoutRes, root, root != null);
|
||||
}
|
||||
|
||||
private View inflate(LayoutInflater realInflater, int layoutRes, @Nullable ViewGroup root,
|
||||
boolean attachToRoot) {
|
||||
if (layoutRes == R.layout.footer_actions
|
||||
|| layoutRes == R.layout.footer_actions_text_button
|
||||
|| layoutRes == R.layout.footer_actions_number_button
|
||||
|| layoutRes == R.layout.footer_actions_icon_button) {
|
||||
return realInflater.inflate(layoutRes, root, attachToRoot);
|
||||
}
|
||||
|
||||
return mQsFragmentView;
|
||||
}
|
||||
|
||||
private void setupQsComponent() {
|
||||
when(mQsComponentFactory.create(any(QSFragment.class))).thenReturn(mQsFragmentComponent);
|
||||
when(mQsFragmentComponent.getQSPanelController()).thenReturn(mQSPanelController);
|
||||
|
||||
@@ -17,23 +17,24 @@ package com.android.systemui.qs;
|
||||
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
|
||||
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
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.annotation.IdRes;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.admin.DevicePolicyManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.VectorDrawable;
|
||||
@@ -42,27 +43,27 @@ import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.LayoutInflaterBuilder;
|
||||
import android.testing.TestableImageView;
|
||||
import android.testing.TestableLooper;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.testing.ViewUtils;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.animation.DialogLaunchAnimator;
|
||||
import com.android.systemui.animation.Expandable;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.common.shared.model.Icon;
|
||||
import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig;
|
||||
import com.android.systemui.security.data.model.SecurityModel;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.statusbar.policy.SecurityController;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -71,8 +72,6 @@ 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
|
||||
@@ -96,11 +95,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
new ComponentName("TestDPC", "Test");
|
||||
private static final int DEFAULT_ICON_ID = R.drawable.ic_info_outline;
|
||||
|
||||
private ViewGroup mRootView;
|
||||
private ViewGroup mSecurityFooterView;
|
||||
private TextView mFooterText;
|
||||
private TestableImageView mPrimaryFooterIcon;
|
||||
private QSSecurityFooter mFooter;
|
||||
private QSSecurityFooterUtils mFooterUtils;
|
||||
@Mock
|
||||
private SecurityController mSecurityController;
|
||||
@@ -122,58 +116,53 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
Looper looper = mTestableLooper.getLooper();
|
||||
Handler mainHandler = new Handler(looper);
|
||||
when(mUserTracker.getUserInfo()).thenReturn(mock(UserInfo.class));
|
||||
mSecurityFooterView = (ViewGroup) new LayoutInflaterBuilder(mContext)
|
||||
.replace("ImageView", TestableImageView.class)
|
||||
.build().inflate(R.layout.quick_settings_security_footer, null, false);
|
||||
mFooterUtils = new QSSecurityFooterUtils(getContext(),
|
||||
getContext().getSystemService(DevicePolicyManager.class), mUserTracker,
|
||||
mainHandler, mActivityStarter, mSecurityController, looper, mDialogLaunchAnimator);
|
||||
mFooter = new QSSecurityFooter(mSecurityFooterView, mainHandler, mSecurityController,
|
||||
looper, mBroadcastDispatcher, mFooterUtils);
|
||||
mFooterText = mSecurityFooterView.findViewById(R.id.footer_text);
|
||||
mPrimaryFooterIcon = mSecurityFooterView.findViewById(R.id.primary_footer_icon);
|
||||
|
||||
when(mSecurityController.getDeviceOwnerComponentOnAnyUser())
|
||||
.thenReturn(DEVICE_OWNER_COMPONENT);
|
||||
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
|
||||
.thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
|
||||
|
||||
// mSecurityFooterView must have a ViewGroup parent so that
|
||||
// DialogLaunchAnimator.Controller.fromView() does not return null.
|
||||
mRootView = new FrameLayout(mContext);
|
||||
mRootView.addView(mSecurityFooterView);
|
||||
ViewUtils.attachView(mRootView);
|
||||
|
||||
mFooter.init();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
ViewUtils.detachView(mRootView);
|
||||
@Nullable
|
||||
private SecurityButtonConfig getButtonConfig() {
|
||||
SecurityModel securityModel = SecurityModel.create(mSecurityController);
|
||||
return mFooterUtils.getButtonConfig(securityModel);
|
||||
}
|
||||
|
||||
private void assertIsDefaultIcon(Icon icon) {
|
||||
assertIsIconResource(icon, DEFAULT_ICON_ID);
|
||||
}
|
||||
|
||||
private void assertIsIconResource(Icon icon, @IdRes int res) {
|
||||
assertThat(icon).isInstanceOf(Icon.Resource.class);
|
||||
assertEquals(res, ((Icon.Resource) icon).getRes());
|
||||
}
|
||||
|
||||
private void assertIsIconDrawable(Icon icon, Drawable drawable) {
|
||||
assertThat(icon).isInstanceOf(Icon.Loaded.class);
|
||||
assertEquals(drawable, ((Icon.Loaded) icon).getDrawable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnmanaged() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(false);
|
||||
when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(false);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(View.GONE, mSecurityFooterView.getVisibility());
|
||||
assertNull(getButtonConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManagedNoOwnerName() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
when(mSecurityController.getDeviceOwnerOrganizationName()).thenReturn(null);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management),
|
||||
mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mSecurityFooterView.getVisibility());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
buttonConfig.getText());
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,15 +170,13 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
when(mSecurityController.getDeviceOwnerOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management,
|
||||
MANAGING_ORGANIZATION),
|
||||
mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mSecurityFooterView.getVisibility());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
MANAGING_ORGANIZATION),
|
||||
buttonConfig.getText());
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -200,15 +187,13 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
|
||||
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
|
||||
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_financed_disclosure_named_management,
|
||||
MANAGING_ORGANIZATION), mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mSecurityFooterView.getVisibility());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
R.string.quick_settings_financed_disclosure_named_management,
|
||||
MANAGING_ORGANIZATION),
|
||||
buttonConfig.getText());
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -220,21 +205,16 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mUserTracker.getUserInfo()).thenReturn(mockUserInfo);
|
||||
Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.DEVICE_DEMO_MODE, 1);
|
||||
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(View.GONE, mSecurityFooterView.getVisibility());
|
||||
assertNull(getButtonConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUntappableView_profileOwnerOfOrgOwnedDevice() {
|
||||
when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true);
|
||||
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertFalse(mSecurityFooterView.isClickable());
|
||||
assertEquals(View.GONE, mSecurityFooterView.findViewById(R.id.footer_icon).getVisibility());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertFalse(buttonConfig.isClickable());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -244,12 +224,9 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(true);
|
||||
when(mSecurityController.hasWorkProfile()).thenReturn(true);
|
||||
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertTrue(mSecurityFooterView.isClickable());
|
||||
assertEquals(View.VISIBLE,
|
||||
mSecurityFooterView.findViewById(R.id.footer_icon).getVisibility());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertTrue(buttonConfig.isClickable());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -258,35 +235,31 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(false);
|
||||
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertFalse(mSecurityFooterView.isClickable());
|
||||
assertEquals(View.GONE, mSecurityFooterView.findViewById(R.id.footer_icon).getVisibility());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertFalse(buttonConfig.isClickable());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNetworkLoggingEnabled_deviceOwner() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
|
||||
mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
buttonConfig.getText());
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
|
||||
// Same situation, but with organization name set
|
||||
when(mSecurityController.getDeviceOwnerOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_named_management_monitoring,
|
||||
MANAGING_ORGANIZATION),
|
||||
mFooterText.getText());
|
||||
R.string.quick_settings_disclosure_named_management_monitoring,
|
||||
MANAGING_ORGANIZATION),
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -294,12 +267,12 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.hasWorkProfile()).thenReturn(true);
|
||||
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_managed_profile_network_activity),
|
||||
mFooterText.getText());
|
||||
R.string.quick_settings_disclosure_managed_profile_network_activity),
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -307,21 +280,19 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.hasWorkProfile()).thenReturn(true);
|
||||
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(false);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals("", mFooterText.getText());
|
||||
assertNull(getButtonConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testManagedCACertsInstalled() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -329,25 +300,23 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_named_vpn,
|
||||
VPN_PACKAGE),
|
||||
mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
|
||||
VPN_PACKAGE),
|
||||
buttonConfig.getText());
|
||||
assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic);
|
||||
|
||||
// Same situation, but with organization name set
|
||||
when(mSecurityController.getDeviceOwnerOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_named_management_named_vpn,
|
||||
MANAGING_ORGANIZATION, VPN_PACKAGE),
|
||||
mFooterText.getText());
|
||||
R.string.quick_settings_disclosure_named_management_named_vpn,
|
||||
MANAGING_ORGANIZATION, VPN_PACKAGE),
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -356,23 +325,21 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
|
||||
when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_vpns),
|
||||
mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
|
||||
buttonConfig.getText());
|
||||
assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic);
|
||||
|
||||
// Same situation, but with organization name set
|
||||
when(mSecurityController.getDeviceOwnerOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_management_vpns,
|
||||
MANAGING_ORGANIZATION),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -381,13 +348,12 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isNetworkLoggingEnabled()).thenReturn(true);
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getPrimaryVpnName()).thenReturn("VPN Test App");
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_management_monitoring),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -395,24 +361,23 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(false);
|
||||
when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_managed_profile_monitoring),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
|
||||
// Same situation, but with organization name set
|
||||
when(mSecurityController.getWorkProfileOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_named_managed_profile_monitoring,
|
||||
MANAGING_ORGANIZATION),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -420,22 +385,20 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(false);
|
||||
when(mSecurityController.hasCACertInWorkProfile()).thenReturn(true);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(false);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals("", mFooterText.getText());
|
||||
assertNull(getButtonConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCACertsInstalled() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(false);
|
||||
when(mSecurityController.hasCACertInCurrentUser()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_monitoring),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -443,12 +406,12 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
|
||||
when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_vpns),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -456,14 +419,14 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_managed_profile_named_vpn,
|
||||
VPN_PACKAGE_2),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -471,22 +434,19 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getWorkProfileVpnName()).thenReturn(VPN_PACKAGE_2);
|
||||
when(mSecurityController.isWorkProfileOn()).thenReturn(false);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals("", mFooterText.getText());
|
||||
assertNull(getButtonConfig());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProfileOwnerOfOrganizationOwnedDeviceNoName() {
|
||||
when(mSecurityController.isProfileOwnerOfOrganizationOwnedDevice()).thenReturn(true);
|
||||
|
||||
mFooter.refreshState();
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_management),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -495,35 +455,33 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.getWorkProfileOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
|
||||
mFooter.refreshState();
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_named_management,
|
||||
MANAGING_ORGANIZATION),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVpnEnabled() {
|
||||
when(mSecurityController.isVpnEnabled()).thenReturn(true);
|
||||
when(mSecurityController.getPrimaryVpnName()).thenReturn(VPN_PACKAGE);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(R.drawable.stat_sys_vpn_ic, mPrimaryFooterIcon.getLastImageResource());
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsIconResource(buttonConfig.getIcon(), R.drawable.stat_sys_vpn_ic);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_named_vpn,
|
||||
VPN_PACKAGE),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
|
||||
when(mSecurityController.hasWorkProfile()).thenReturn(true);
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(
|
||||
R.string.quick_settings_disclosure_personal_profile_named_vpn,
|
||||
VPN_PACKAGE),
|
||||
mFooterText.getText());
|
||||
buttonConfig.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -686,19 +644,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
view.findViewById(R.id.vpn_subtitle).getVisibility());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoClickWhenGone() {
|
||||
mFooter.refreshState();
|
||||
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
|
||||
assertFalse(mFooter.hasFooter());
|
||||
mFooter.onClick(mFooter.getView());
|
||||
|
||||
// Proxy for dialog being created
|
||||
verify(mDialogLaunchAnimator, never()).showFromView(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParentalControls() {
|
||||
// Make sure the security footer is visible, so that the images are updated.
|
||||
@@ -707,29 +652,26 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
|
||||
// We use the default icon when there is no admin icon.
|
||||
when(mSecurityController.getIcon(any())).thenReturn(null);
|
||||
mFooter.refreshState();
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
SecurityButtonConfig buttonConfig = getButtonConfig();
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
|
||||
mFooterText.getText());
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
buttonConfig.getText());
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
|
||||
Drawable testDrawable = new VectorDrawable();
|
||||
when(mSecurityController.getIcon(any())).thenReturn(testDrawable);
|
||||
assertNotNull(mSecurityController.getIcon(null));
|
||||
|
||||
mFooter.refreshState();
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertEquals(mContext.getString(R.string.quick_settings_disclosure_parental_controls),
|
||||
mFooterText.getText());
|
||||
assertEquals(View.VISIBLE, mPrimaryFooterIcon.getVisibility());
|
||||
assertEquals(testDrawable, mPrimaryFooterIcon.getDrawable());
|
||||
buttonConfig.getText());
|
||||
assertIsIconDrawable(buttonConfig.getIcon(), testDrawable);
|
||||
|
||||
// Ensure the primary icon is back to default after parental controls are gone
|
||||
when(mSecurityController.isParentalControlsEnabled()).thenReturn(false);
|
||||
mFooter.refreshState();
|
||||
TestableLooper.get(this).processAllMessages();
|
||||
assertEquals(DEFAULT_ICON_ID, mPrimaryFooterIcon.getLastImageResource());
|
||||
buttonConfig = getButtonConfig();
|
||||
assertNotNull(buttonConfig);
|
||||
assertIsDefaultIcon(buttonConfig.getIcon());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -742,16 +684,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDialogUsesDialogLauncher() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
mFooter.onClick(mSecurityFooterView);
|
||||
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
verify(mDialogLaunchAnimator).show(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDialogViewForFinancedDevice() {
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
@@ -782,7 +714,10 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
|
||||
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
|
||||
|
||||
mFooter.showDeviceMonitoringDialog();
|
||||
Expandable expandable = mock(Expandable.class);
|
||||
when(expandable.dialogLaunchController(any())).thenReturn(
|
||||
mock(DialogLaunchAnimator.Controller.class));
|
||||
mFooterUtils.showDeviceMonitoringDialog(getContext(), expandable);
|
||||
ArgumentCaptor<AlertDialog> dialogCaptor = ArgumentCaptor.forClass(AlertDialog.class);
|
||||
|
||||
mTestableLooper.processAllMessages();
|
||||
@@ -797,47 +732,6 @@ public class QSSecurityFooterTest extends SysuiTestCase {
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVisibilityListener() {
|
||||
final AtomicInteger lastVisibility = new AtomicInteger(-1);
|
||||
VisibilityChangedDispatcher.OnVisibilityChangedListener listener = 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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBroadcastShowsDialog() {
|
||||
// Setup dialog content
|
||||
when(mSecurityController.isDeviceManaged()).thenReturn(true);
|
||||
when(mSecurityController.getDeviceOwnerOrganizationName())
|
||||
.thenReturn(MANAGING_ORGANIZATION);
|
||||
when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
|
||||
.thenReturn(DEVICE_OWNER_TYPE_FINANCED);
|
||||
|
||||
ArgumentCaptor<BroadcastReceiver> captor = ArgumentCaptor.forClass(BroadcastReceiver.class);
|
||||
verify(mBroadcastDispatcher).registerReceiverWithHandler(captor.capture(), any(), any(),
|
||||
any());
|
||||
|
||||
// Pretend view is not attached anymore.
|
||||
mRootView.removeView(mSecurityFooterView);
|
||||
captor.getValue().onReceive(mContext,
|
||||
new Intent(DevicePolicyManager.ACTION_SHOW_DEVICE_MONITORING_DIALOG));
|
||||
mTestableLooper.processAllMessages();
|
||||
|
||||
assertTrue(mFooterUtils.getDialog().isShowing());
|
||||
mFooterUtils.getDialog().dismiss();
|
||||
}
|
||||
|
||||
private CharSequence addLink(CharSequence description) {
|
||||
final SpannableStringBuilder message = new SpannableStringBuilder();
|
||||
message.append(description);
|
||||
|
||||
@@ -385,4 +385,86 @@ class FooterActionsViewModelTest : SysuiTestCase() {
|
||||
underTest.onVisibilityChangeRequested(visible = true)
|
||||
assertThat(underTest.isVisible.value).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun alpha_inSplitShade_followsExpansion() {
|
||||
val underTest = utils.footerActionsViewModel()
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.25f, isInSplitShade = true)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0.25f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = true)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0.5f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.75f, isInSplitShade = true)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0.75f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = true)
|
||||
assertThat(underTest.alpha.value).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun backgroundAlpha_inSplitShade_followsExpansion_with_0_99_delay() {
|
||||
val underTest = utils.footerActionsViewModel()
|
||||
val floatTolerance = 0.01f
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = true)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = true)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.9f, isInSplitShade = true)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.991f, isInSplitShade = true)
|
||||
assertThat(underTest.backgroundAlpha.value).isWithin(floatTolerance).of(0.1f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.995f, isInSplitShade = true)
|
||||
assertThat(underTest.backgroundAlpha.value).isWithin(floatTolerance).of(0.5f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = true)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun alpha_inSingleShade_followsExpansion_with_0_9_delay() {
|
||||
val underTest = utils.footerActionsViewModel()
|
||||
val floatTolerance = 0.01f
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = false)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.9f, isInSplitShade = false)
|
||||
assertThat(underTest.alpha.value).isEqualTo(0f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.91f, isInSplitShade = false)
|
||||
assertThat(underTest.alpha.value).isWithin(floatTolerance).of(0.1f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.95f, isInSplitShade = false)
|
||||
assertThat(underTest.alpha.value).isWithin(floatTolerance).of(0.5f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = false)
|
||||
assertThat(underTest.alpha.value).isEqualTo(1f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun backgroundAlpha_inSingleShade_always1() {
|
||||
val underTest = utils.footerActionsViewModel()
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0f, isInSplitShade = false)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(1f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(0.5f, isInSplitShade = false)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(1f)
|
||||
|
||||
underTest.onQuickSettingsExpansionChanged(1f, isInSplitShade = false)
|
||||
assertThat(underTest.backgroundAlpha.value).isEqualTo(1f)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,5 @@ class FakeFgsManagerController(
|
||||
dialogDismissedListeners.remove(listener)
|
||||
}
|
||||
|
||||
override fun shouldUpdateFooterVisibility(): Boolean = false
|
||||
|
||||
override fun visibleButtonsCount(): Int = 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user