• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.Intent
20 import android.os.Handler
21 import android.os.Looper
22 import android.service.quicksettings.Tile
23 import androidx.annotation.DrawableRes
24 import androidx.annotation.VisibleForTesting
25 import androidx.lifecycle.Lifecycle
26 import androidx.lifecycle.coroutineScope
27 import androidx.lifecycle.repeatOnLifecycle
28 import com.android.app.tracing.coroutines.launchTraced as launch
29 import com.android.internal.logging.MetricsLogger
30 import com.android.systemui.animation.Expandable
31 import com.android.systemui.dagger.qualifiers.Background
32 import com.android.systemui.dagger.qualifiers.Main
33 import com.android.systemui.modes.shared.ModesUi
34 import com.android.systemui.plugins.ActivityStarter
35 import com.android.systemui.plugins.FalsingManager
36 import com.android.systemui.plugins.qs.QSTile
37 import com.android.systemui.plugins.qs.TileDetailsViewModel
38 import com.android.systemui.plugins.statusbar.StatusBarStateController
39 import com.android.systemui.qs.QSHost
40 import com.android.systemui.qs.QsEventLogger
41 import com.android.systemui.qs.asQSTileIcon
42 import com.android.systemui.qs.flags.QsInCompose
43 import com.android.systemui.qs.logging.QSLogger
44 import com.android.systemui.qs.tileimpl.QSTileImpl
45 import com.android.systemui.qs.tiles.base.shared.model.QSTileConfigProvider
46 import com.android.systemui.qs.tiles.base.shared.model.QSTileState
47 import com.android.systemui.qs.tiles.dialog.ModesDetailsViewModel
48 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileDataInteractor
49 import com.android.systemui.qs.tiles.impl.modes.domain.interactor.ModesTileUserActionInteractor
50 import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
51 import com.android.systemui.qs.tiles.impl.modes.ui.mapper.ModesTileMapper
52 import com.android.systemui.res.R
53 import com.android.systemui.statusbar.policy.ui.dialog.viewmodel.ModesDialogViewModel
54 import javax.inject.Inject
55 import kotlinx.coroutines.runBlocking
56 
57 class ModesTile
58 @Inject
59 constructor(
60     host: QSHost,
61     uiEventLogger: QsEventLogger,
62     @Background backgroundLooper: Looper,
63     @Main mainHandler: Handler,
64     falsingManager: FalsingManager,
65     metricsLogger: MetricsLogger,
66     statusBarStateController: StatusBarStateController,
67     activityStarter: ActivityStarter,
68     qsLogger: QSLogger,
69     qsTileConfigProvider: QSTileConfigProvider,
70     private val dataInteractor: ModesTileDataInteractor,
71     private val tileMapper: ModesTileMapper,
72     private val userActionInteractor: ModesTileUserActionInteractor,
73     private val modesDialogViewModel: ModesDialogViewModel,
74 ) :
75     QSTileImpl<QSTile.State>(
76         host,
77         uiEventLogger,
78         backgroundLooper,
79         mainHandler,
80         falsingManager,
81         metricsLogger,
82         statusBarStateController,
83         activityStarter,
84         qsLogger,
85     ) {
86 
87     private lateinit var tileState: QSTileState
88     private val config = qsTileConfigProvider.getConfig(TILE_SPEC)
89 
90     init {
<lambda>null91         lifecycle.coroutineScope.launch {
92             lifecycle.repeatOnLifecycle(
93                 // TODO: b/403434908 - Workaround for "not listening to tile updates". Can be reset
94                 //   to RESUMED if either b/403434908 is fixed or QsInCompose is inlined.
95                 if (QsInCompose.isEnabled) Lifecycle.State.RESUMED else Lifecycle.State.CREATED
96             ) {
97                 dataInteractor.tileData().collect { refreshState(it) }
98             }
99         }
100     }
101 
isAvailablenull102     override fun isAvailable(): Boolean = ModesUi.isEnabled
103 
104     override fun getTileLabel(): CharSequence = tileState.label
105 
106     override fun newTileState(): QSTile.State {
107         return QSTile.State().apply {
108             label = mContext.getString(R.string.quick_settings_modes_label)
109             icon = ResourceIcon.get(ICON_RES_ID)
110             state = Tile.STATE_INACTIVE
111         }
112     }
113 
<lambda>null114     override fun handleClick(expandable: Expandable?) = runBlocking {
115         userActionInteractor.handleClick(expandable)
116     }
117 
<lambda>null118     override fun handleSecondaryClick(expandable: Expandable?) = runBlocking {
119         val model = dataInteractor.getCurrentTileModel()
120         userActionInteractor.handleToggleClick(model)
121     }
122 
getDetailsViewModelnull123     override fun getDetailsViewModel(): TileDetailsViewModel {
124         return ModesDetailsViewModel(
125             onSettingsClick = { userActionInteractor.handleLongClick(null) },
126             viewModel = modesDialogViewModel,
127         )
128     }
129 
getLongClickIntentnull130     override fun getLongClickIntent(): Intent = userActionInteractor.longClickIntent
131 
132     @VisibleForTesting
133     public override fun handleUpdateState(state: QSTile.State?, arg: Any?) {
134         // This runBlocking() will block @Background. Due to caches, it's expected to be fast.
135         val model =
136             if (arg is ModesTileModel) arg else runBlocking { dataInteractor.getCurrentTileModel() }
137 
138         tileState = tileMapper.map(config, model)
139         state?.apply {
140             this.state = tileState.activationState.legacyState
141             icon = tileState.icon?.asQSTileIcon() ?: maybeLoadResourceIcon(ICON_RES_ID)
142             label = tileLabel
143             secondaryLabel = tileState.secondaryLabel
144             contentDescription = tileState.contentDescription
145             expandedAccessibilityClassName = tileState.expandedAccessibilityClassName
146             handlesSecondaryClick = true
147         }
148     }
149 
150     companion object {
151         const val TILE_SPEC = "dnd"
152         @DrawableRes val ICON_RES_ID = com.android.internal.R.drawable.ic_zen_priority_modes
153     }
154 }
155