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 }