1 /* <lambda>null2 * Copyright (C) 2022 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.statusbar.pipeline.mobile.ui.binder 18 19 import android.content.res.ColorStateList 20 import android.view.View 21 import android.view.View.GONE 22 import android.view.View.INVISIBLE 23 import android.view.View.VISIBLE 24 import android.view.ViewGroup 25 import android.widget.ImageView 26 import android.widget.Space 27 import androidx.core.view.isVisible 28 import androidx.lifecycle.Lifecycle 29 import androidx.lifecycle.repeatOnLifecycle 30 import com.android.settingslib.graph.SignalDrawable 31 import com.android.systemui.R 32 import com.android.systemui.common.ui.binder.ContentDescriptionViewBinder 33 import com.android.systemui.common.ui.binder.IconViewBinder 34 import com.android.systemui.lifecycle.repeatWhenAttached 35 import com.android.systemui.statusbar.StatusBarIconView 36 import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT 37 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN 38 import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON 39 import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger 40 import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel 41 import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding 42 import kotlinx.coroutines.awaitCancellation 43 import kotlinx.coroutines.flow.MutableStateFlow 44 import kotlinx.coroutines.flow.distinctUntilChanged 45 import kotlinx.coroutines.launch 46 47 object MobileIconBinder { 48 /** Binds the view to the view-model, continuing to update the former based on the latter */ 49 @JvmStatic 50 fun bind( 51 view: ViewGroup, 52 viewModel: LocationBasedMobileViewModel, 53 logger: MobileViewLogger, 54 ): ModernStatusBarViewBinding { 55 val mobileGroupView = view.requireViewById<ViewGroup>(R.id.mobile_group) 56 val activityContainer = view.requireViewById<View>(R.id.inout_container) 57 val activityIn = view.requireViewById<ImageView>(R.id.mobile_in) 58 val activityOut = view.requireViewById<ImageView>(R.id.mobile_out) 59 val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type) 60 val iconView = view.requireViewById<ImageView>(R.id.mobile_signal) 61 val mobileDrawable = SignalDrawable(view.context).also { iconView.setImageDrawable(it) } 62 val roamingView = view.requireViewById<ImageView>(R.id.mobile_roaming) 63 val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space) 64 val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot) 65 66 view.isVisible = true 67 iconView.isVisible = true 68 69 // TODO(b/238425913): We should log this visibility state. 70 @StatusBarIconView.VisibleState 71 val visibilityState: MutableStateFlow<Int> = MutableStateFlow(STATE_HIDDEN) 72 73 val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor) 74 val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor) 75 76 var isCollecting: Boolean = false 77 78 view.repeatWhenAttached { 79 repeatOnLifecycle(Lifecycle.State.STARTED) { 80 logger.logCollectionStarted(view, viewModel) 81 isCollecting = true 82 83 launch { 84 visibilityState.collect { state -> 85 when (state) { 86 STATE_ICON -> { 87 mobileGroupView.visibility = VISIBLE 88 dotView.visibility = GONE 89 } 90 STATE_DOT -> { 91 mobileGroupView.visibility = INVISIBLE 92 dotView.visibility = VISIBLE 93 } 94 STATE_HIDDEN -> { 95 mobileGroupView.visibility = INVISIBLE 96 dotView.visibility = INVISIBLE 97 } 98 } 99 } 100 } 101 102 launch { viewModel.isVisible.collect { isVisible -> view.isVisible = isVisible } } 103 104 // Set the icon for the triangle 105 launch { 106 viewModel.icon.distinctUntilChanged().collect { icon -> 107 viewModel.verboseLogger?.logBinderReceivedSignalIcon( 108 view, 109 viewModel.subscriptionId, 110 icon, 111 ) 112 mobileDrawable.level = 113 SignalDrawable.getState( 114 icon.level, 115 icon.numberOfLevels, 116 icon.showExclamationMark, 117 ) 118 } 119 } 120 121 launch { 122 viewModel.contentDescription.distinctUntilChanged().collect { 123 ContentDescriptionViewBinder.bind(it, view) 124 } 125 } 126 127 // Set the network type icon 128 launch { 129 viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId -> 130 viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon( 131 view, 132 viewModel.subscriptionId, 133 dataTypeId, 134 ) 135 dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) } 136 networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE 137 } 138 } 139 140 // Set the roaming indicator 141 launch { 142 viewModel.roaming.distinctUntilChanged().collect { isRoaming -> 143 roamingView.isVisible = isRoaming 144 roamingSpace.isVisible = isRoaming 145 } 146 } 147 148 // Set the activity indicators 149 launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } } 150 151 launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } } 152 153 launch { 154 viewModel.activityContainerVisible.collect { activityContainer.isVisible = it } 155 } 156 157 // Set the tint 158 launch { 159 iconTint.collect { tint -> 160 val tintList = ColorStateList.valueOf(tint) 161 iconView.imageTintList = tintList 162 networkTypeView.imageTintList = tintList 163 roamingView.imageTintList = tintList 164 activityIn.imageTintList = tintList 165 activityOut.imageTintList = tintList 166 dotView.setDecorColor(tint) 167 } 168 } 169 170 launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } } 171 172 try { 173 awaitCancellation() 174 } finally { 175 isCollecting = false 176 logger.logCollectionStopped(view, viewModel) 177 } 178 } 179 } 180 181 return object : ModernStatusBarViewBinding { 182 override fun getShouldIconBeVisible(): Boolean { 183 return viewModel.isVisible.value 184 } 185 186 override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) { 187 visibilityState.value = state 188 } 189 190 override fun onIconTintChanged(newTint: Int) { 191 if (viewModel.useDebugColoring) { 192 return 193 } 194 iconTint.value = newTint 195 } 196 197 override fun onDecorTintChanged(newTint: Int) { 198 if (viewModel.useDebugColoring) { 199 return 200 } 201 decorTint.value = newTint 202 } 203 204 override fun isCollecting(): Boolean { 205 return isCollecting 206 } 207 } 208 } 209 } 210