1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.qs 18 19 import android.content.Intent 20 import android.os.UserManager 21 import android.provider.Settings 22 import android.view.View 23 import android.widget.Toast 24 import androidx.annotation.VisibleForTesting 25 import com.android.internal.jank.InteractionJankMonitor 26 import com.android.internal.logging.MetricsLogger 27 import com.android.internal.logging.UiEventLogger 28 import com.android.internal.logging.nano.MetricsProto 29 import com.android.keyguard.KeyguardUpdateMonitor 30 import com.android.systemui.R 31 import com.android.systemui.animation.ActivityLaunchAnimator 32 import com.android.systemui.globalactions.GlobalActionsDialogLite 33 import com.android.systemui.plugins.ActivityStarter 34 import com.android.systemui.plugins.FalsingManager 35 import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED 36 import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED 37 import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED 38 import com.android.systemui.statusbar.phone.MultiUserSwitchController 39 import com.android.systemui.statusbar.phone.SettingsButton 40 import com.android.systemui.statusbar.policy.DeviceProvisionedController 41 import com.android.systemui.statusbar.policy.UserInfoController 42 import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener 43 import com.android.systemui.tuner.TunerService 44 import com.android.systemui.util.ViewController 45 import javax.inject.Inject 46 import javax.inject.Named 47 48 /** 49 * Manages [FooterActionsView] behaviour, both when it's placed in QS or QQS (split shade). 50 * Main difference between QS and QQS behaviour is condition when buttons should be visible, 51 * determined by [buttonsVisibleState] 52 */ 53 class FooterActionsController @Inject constructor( 54 view: FooterActionsView, 55 private val qsPanelController: QSPanelController, 56 private val activityStarter: ActivityStarter, 57 private val userManager: UserManager, 58 private val userInfoController: UserInfoController, 59 private val multiUserSwitchController: MultiUserSwitchController, 60 private val deviceProvisionedController: DeviceProvisionedController, 61 private val falsingManager: FalsingManager, 62 private val metricsLogger: MetricsLogger, 63 private val tunerService: TunerService, 64 private val globalActionsDialog: GlobalActionsDialogLite, 65 private val uiEventLogger: UiEventLogger, 66 @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean, 67 private val buttonsVisibleState: ExpansionState 68 ) : ViewController<FooterActionsView>(view) { 69 70 enum class ExpansionState { COLLAPSED, EXPANDED } 71 72 private var listening: Boolean = false 73 74 var expanded = false 75 76 private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button) 77 private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container) 78 private val editButton: View = view.findViewById(android.R.id.edit) 79 private val powerMenuLite: View = view.findViewById(R.id.pm_lite) 80 81 private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ -> 82 val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) 83 mView.onUserInfoChanged(picture, isGuestUser) 84 } 85 86 private val onClickListener = View.OnClickListener { v -> 87 // Don't do anything until views are unhidden. Don't do anything if the tap looks 88 // suspicious. 89 if (!buttonsVisible() || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { 90 return@OnClickListener 91 } 92 if (v === settingsButton) { 93 if (!deviceProvisionedController.isCurrentUserSetup) { 94 // If user isn't setup just unlock the device and dump them back at SUW. 95 activityStarter.postQSRunnableDismissingKeyguard {} 96 return@OnClickListener 97 } 98 metricsLogger.action( 99 if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH 100 else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH) 101 if (settingsButton.isTunerClick) { 102 activityStarter.postQSRunnableDismissingKeyguard { 103 if (isTunerEnabled()) { 104 tunerService.showResetRequest { 105 // Relaunch settings so that the tuner disappears. 106 startSettingsActivity() 107 } 108 } else { 109 Toast.makeText(context, R.string.tuner_toast, Toast.LENGTH_LONG).show() 110 tunerService.isTunerEnabled = true 111 } 112 startSettingsActivity() 113 } 114 } else { 115 startSettingsActivity() 116 } 117 } else if (v === powerMenuLite) { 118 uiEventLogger.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS) 119 globalActionsDialog.showOrHideDialog(false, true, v) 120 } 121 } 122 123 private fun buttonsVisible(): Boolean { 124 return when (buttonsVisibleState) { 125 EXPANDED -> expanded 126 COLLAPSED -> !expanded 127 } 128 } 129 130 override fun onInit() { 131 multiUserSwitchController.init() 132 } 133 134 fun hideFooter() { 135 mView.visibility = View.GONE 136 } 137 138 fun showFooter() { 139 mView.visibility = View.VISIBLE 140 updateView() 141 } 142 143 private fun startSettingsActivity() { 144 val animationController = settingsButtonContainer?.let { 145 ActivityLaunchAnimator.Controller.fromView( 146 it, 147 InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_SETTINGS_BUTTON) 148 } 149 activityStarter.startActivity(Intent(Settings.ACTION_SETTINGS), 150 true /* dismissShade */, animationController) 151 } 152 153 @VisibleForTesting 154 public override fun onViewAttached() { 155 if (showPMLiteButton) { 156 powerMenuLite.visibility = View.VISIBLE 157 powerMenuLite.setOnClickListener(onClickListener) 158 } else { 159 powerMenuLite.visibility = View.GONE 160 } 161 settingsButton.setOnClickListener(onClickListener) 162 editButton.setOnClickListener(View.OnClickListener { view: View? -> 163 if (falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) { 164 return@OnClickListener 165 } 166 activityStarter.postQSRunnableDismissingKeyguard { qsPanelController.showEdit(view) } 167 }) 168 169 updateView() 170 } 171 172 private fun updateView() { 173 mView.updateEverything(isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled) 174 } 175 176 override fun onViewDetached() { 177 setListening(false) 178 } 179 180 fun setListening(listening: Boolean) { 181 if (this.listening == listening) { 182 return 183 } 184 this.listening = listening 185 if (this.listening) { 186 userInfoController.addCallback(onUserInfoChangedListener) 187 updateView() 188 } else { 189 userInfoController.removeCallback(onUserInfoChangedListener) 190 } 191 } 192 193 fun disable(state2: Int) { 194 mView.disable(state2, isTunerEnabled(), multiUserSwitchController.isMultiUserEnabled) 195 } 196 197 fun setExpansion(headerExpansionFraction: Float) { 198 mView.setExpansion(headerExpansionFraction) 199 } 200 201 fun updateAnimator(width: Int, numTiles: Int) { 202 mView.updateAnimator(width, numTiles) 203 } 204 205 fun setKeyguardShowing() { 206 mView.setKeyguardShowing() 207 } 208 209 fun refreshVisibility(shouldBeVisible: Boolean) { 210 if (shouldBeVisible) { 211 showFooter() 212 } else { 213 hideFooter() 214 } 215 } 216 217 private fun isTunerEnabled() = tunerService.isTunerEnabled 218 }