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 package com.android.systemui.qs
17 
18 import android.app.StatusBarManager
19 import android.content.Context
20 import android.content.res.Configuration
21 import android.graphics.PorterDuff
22 import android.graphics.drawable.Drawable
23 import android.graphics.drawable.RippleDrawable
24 import android.os.UserManager
25 import android.util.AttributeSet
26 import android.view.View
27 import android.widget.ImageView
28 import android.widget.LinearLayout
29 import com.android.settingslib.Utils
30 import com.android.settingslib.drawable.UserIconDrawable
31 import com.android.systemui.R
32 import com.android.systemui.statusbar.phone.MultiUserSwitch
33 import com.android.systemui.statusbar.phone.SettingsButton
34 
35 /**
36  * Quick Settings bottom buttons placed in footer (aka utility bar) - always visible in expanded QS,
37  * in split shade mode visible also in collapsed state. May contain up to 5 buttons: settings,
38  * edit tiles, power off and conditionally: user switch and tuner
39  */
40 class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
41     private lateinit var settingsContainer: View
42     private lateinit var settingsButton: SettingsButton
43     private lateinit var multiUserSwitch: MultiUserSwitch
44     private lateinit var multiUserAvatar: ImageView
45     private lateinit var tunerIcon: View
46     private lateinit var editTilesButton: View
47 
48     private var settingsCogAnimator: TouchAnimator? = null
49 
50     private var qsDisabled = false
51     private var expansionAmount = 0f
52 
53     override fun onFinishInflate() {
54         super.onFinishInflate()
55         editTilesButton = requireViewById(android.R.id.edit)
56         settingsButton = findViewById(R.id.settings_button)
57         settingsContainer = findViewById(R.id.settings_button_container)
58         multiUserSwitch = findViewById(R.id.multi_user_switch)
59         multiUserAvatar = multiUserSwitch.findViewById(R.id.multi_user_avatar)
60         tunerIcon = requireViewById(R.id.tuner_icon)
61 
62         // RenderThread is doing more harm than good when touching the header (to expand quick
63         // settings), so disable it for this view
64         if (settingsButton.background is RippleDrawable) {
65             (settingsButton.background as RippleDrawable).setForceSoftware(true)
66         }
67         updateResources()
68         importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
69     }
70 
71     fun updateAnimator(width: Int, numTiles: Int) {
72         val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
73                 mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
74         val remaining = (width - numTiles * size) / (numTiles - 1)
75         val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
76         val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
77         settingsCogAnimator = TouchAnimator.Builder()
78                 .addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
79                 .addFloat(settingsButton, "rotation", -120f, 0f)
80                 .build()
81         setExpansion(expansionAmount)
82     }
83 
84     override fun onConfigurationChanged(newConfig: Configuration) {
85         super.onConfigurationChanged(newConfig)
86         updateResources()
87     }
88 
89     override fun onRtlPropertiesChanged(layoutDirection: Int) {
90         super.onRtlPropertiesChanged(layoutDirection)
91         updateResources()
92     }
93 
94     private fun updateResources() {
95         val tunerIconTranslation = mContext.resources
96                 .getDimensionPixelOffset(R.dimen.qs_footer_tuner_icon_translation).toFloat()
97         tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
98     }
99 
100     fun setKeyguardShowing() {
101         setExpansion(expansionAmount)
102     }
103 
104     fun setExpansion(headerExpansionFraction: Float) {
105         expansionAmount = headerExpansionFraction
106         if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
107     }
108 
109     fun disable(
110         state2: Int,
111         isTunerEnabled: Boolean,
112         multiUserEnabled: Boolean
113     ) {
114         val disabled = state2 and StatusBarManager.DISABLE2_QUICK_SETTINGS != 0
115         if (disabled == qsDisabled) return
116         qsDisabled = disabled
117         updateEverything(isTunerEnabled, multiUserEnabled)
118     }
119 
120     fun updateEverything(
121         isTunerEnabled: Boolean,
122         multiUserEnabled: Boolean
123     ) {
124         post {
125             updateVisibilities(isTunerEnabled, multiUserEnabled)
126             updateClickabilities()
127             isClickable = false
128         }
129     }
130 
131     private fun updateClickabilities() {
132         multiUserSwitch.isClickable = multiUserSwitch.visibility == VISIBLE
133         editTilesButton.isClickable = editTilesButton.visibility == VISIBLE
134         settingsButton.isClickable = settingsButton.visibility == VISIBLE
135     }
136 
137     private fun updateVisibilities(
138         isTunerEnabled: Boolean,
139         multiUserEnabled: Boolean
140     ) {
141         settingsContainer.visibility = if (qsDisabled) GONE else VISIBLE
142         tunerIcon.visibility = if (isTunerEnabled) VISIBLE else INVISIBLE
143         multiUserSwitch.visibility = if (multiUserEnabled) VISIBLE else GONE
144         val isDemo = UserManager.isDeviceInDemoMode(context)
145         settingsButton.visibility = if (isDemo) INVISIBLE else VISIBLE
146     }
147 
148     fun onUserInfoChanged(picture: Drawable?, isGuestUser: Boolean) {
149         var pictureToSet = picture
150         if (picture != null && isGuestUser && picture !is UserIconDrawable) {
151             pictureToSet = picture.constantState.newDrawable(resources).mutate()
152             pictureToSet.setColorFilter(
153                     Utils.getColorAttrDefaultColor(mContext, android.R.attr.colorForeground),
154                     PorterDuff.Mode.SRC_IN)
155         }
156         multiUserAvatar.setImageDrawable(pictureToSet)
157     }
158 }