• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.tiles
18 
19 import android.content.ComponentName
20 import android.content.Intent
21 import android.os.Handler
22 import android.os.Looper
23 import android.service.quicksettings.Tile
24 import android.widget.Button
25 import com.android.internal.jank.InteractionJankMonitor
26 import com.android.internal.logging.MetricsLogger
27 import com.android.systemui.animation.Expandable
28 import com.android.systemui.controls.ControlsServiceInfo
29 import com.android.systemui.controls.dagger.ControlsComponent
30 import com.android.systemui.controls.dagger.ControlsComponent.Visibility.AVAILABLE
31 import com.android.systemui.controls.management.ControlsListingController
32 import com.android.systemui.controls.ui.ControlsUiController
33 import com.android.systemui.controls.ui.SelectedItem
34 import com.android.systemui.dagger.qualifiers.Background
35 import com.android.systemui.dagger.qualifiers.Main
36 import com.android.systemui.plugins.ActivityStarter
37 import com.android.systemui.plugins.FalsingManager
38 import com.android.systemui.plugins.qs.QSTile
39 import com.android.systemui.plugins.statusbar.StatusBarStateController
40 import com.android.systemui.qs.QSHost
41 import com.android.systemui.qs.QsEventLogger
42 import com.android.systemui.qs.logging.QSLogger
43 import com.android.systemui.qs.tileimpl.QSTileImpl
44 import com.android.systemui.res.R
45 import java.util.concurrent.atomic.AtomicBoolean
46 import javax.inject.Inject
47 
48 class DeviceControlsTile
49 @Inject
50 constructor(
51     host: QSHost,
52     uiEventLogger: QsEventLogger,
53     @Background backgroundLooper: Looper,
54     @Main mainHandler: Handler,
55     falsingManager: FalsingManager,
56     metricsLogger: MetricsLogger,
57     statusBarStateController: StatusBarStateController,
58     activityStarter: ActivityStarter,
59     qsLogger: QSLogger,
60     private val controlsComponent: ControlsComponent,
61 ) :
62     QSTileImpl<QSTile.State>(
63         host,
64         uiEventLogger,
65         backgroundLooper,
66         mainHandler,
67         falsingManager,
68         metricsLogger,
69         statusBarStateController,
70         activityStarter,
71         qsLogger,
72     ) {
73 
74     private var hasControlsApps = AtomicBoolean(false)
75 
76     private var icon: QSTile.Icon? = null
77 
78     private val listingCallback =
79         object : ControlsListingController.ControlsListingCallback {
onServicesUpdatednull80             override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
81                 if (
82                     hasControlsApps.compareAndSet(serviceInfos.isEmpty(), serviceInfos.isNotEmpty())
83                 ) {
84                     refreshState()
85                 }
86             }
87         }
88 
89     init {
<lambda>null90         controlsComponent.getControlsListingController().ifPresent {
91             it.observe(this, listingCallback)
92         }
93     }
94 
isAvailablenull95     override fun isAvailable(): Boolean {
96         return controlsComponent.getControlsController().isPresent
97     }
98 
newTileStatenull99     override fun newTileState(): QSTile.State {
100         return QSTile.State().also {
101             it.state = Tile.STATE_UNAVAILABLE // Start unavailable matching `hasControlsApps`
102             it.handlesLongClick = false
103         }
104     }
105 
handleClicknull106     override fun handleClick(expandable: Expandable?) {
107         if (state.state == Tile.STATE_UNAVAILABLE) {
108             return
109         }
110 
111         val intent =
112             Intent().apply {
113                 component =
114                     ComponentName(
115                         mContext,
116                         controlsComponent.getControlsUiController().get().resolveActivity(),
117                     )
118                 addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
119                 putExtra(ControlsUiController.EXTRA_ANIMATE, true)
120             }
121         val animationController =
122             expandable?.activityTransitionController(
123                 InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE
124             )
125 
126         mUiHandler.post {
127             val showOverLockscreenWhenLocked = state.state == Tile.STATE_ACTIVE
128             mActivityStarter.startActivity(
129                 intent,
130                 true /* dismissShade */,
131                 animationController,
132                 showOverLockscreenWhenLocked,
133             )
134         }
135     }
136 
handleUpdateStatenull137     override fun handleUpdateState(state: QSTile.State, arg: Any?) {
138         state.label = tileLabel
139         state.contentDescription = state.label
140         if (icon == null) {
141             icon = maybeLoadResourceIcon(controlsComponent.getTileImageId())
142         }
143         state.icon = icon
144         if (controlsComponent.isEnabled() && hasControlsApps.get()) {
145             if (controlsComponent.getVisibility() == AVAILABLE) {
146                 val selection =
147                     controlsComponent.getControlsController().get().getPreferredSelection()
148                 state.state =
149                     if (
150                         selection is SelectedItem.StructureItem &&
151                             selection.structure.controls.isEmpty()
152                     ) {
153                         Tile.STATE_INACTIVE
154                     } else {
155                         Tile.STATE_ACTIVE
156                     }
157                 val label = selection.name
158                 state.secondaryLabel = if (label == tileLabel) null else label
159             } else {
160                 state.state = Tile.STATE_INACTIVE
161                 state.secondaryLabel = mContext.getText(R.string.controls_tile_locked)
162             }
163             state.stateDescription = state.secondaryLabel
164         } else {
165             state.state = Tile.STATE_UNAVAILABLE
166         }
167         state.expandedAccessibilityClassName = Button::class.java.name
168     }
169 
getMetricsCategorynull170     override fun getMetricsCategory(): Int {
171         return 0
172     }
173 
getLongClickIntentnull174     override fun getLongClickIntent(): Intent? {
175         return null
176     }
177 
handleLongClicknull178     override fun handleLongClick(expandable: Expandable?) {}
179 
getTileLabelnull180     override fun getTileLabel(): CharSequence {
181         return mContext.getText(controlsComponent.getTileTitleId())
182     }
183 
184     companion object {
185         const val TILE_SPEC = "controls"
186     }
187 }
188