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.wifi.ui.binder 18 19 import android.content.res.ColorStateList 20 import android.view.View 21 import android.view.ViewGroup 22 import android.widget.ImageView 23 import androidx.core.view.isVisible 24 import androidx.lifecycle.Lifecycle 25 import androidx.lifecycle.repeatOnLifecycle 26 import com.android.systemui.R 27 import com.android.systemui.common.ui.binder.IconViewBinder 28 import com.android.systemui.lifecycle.repeatWhenAttached 29 import com.android.systemui.statusbar.StatusBarIconView 30 import com.android.systemui.statusbar.StatusBarIconView.STATE_DOT 31 import com.android.systemui.statusbar.StatusBarIconView.STATE_HIDDEN 32 import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON 33 import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding 34 import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon 35 import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel 36 import kotlinx.coroutines.InternalCoroutinesApi 37 import kotlinx.coroutines.awaitCancellation 38 import kotlinx.coroutines.flow.MutableStateFlow 39 import kotlinx.coroutines.flow.collect 40 import kotlinx.coroutines.flow.distinctUntilChanged 41 import kotlinx.coroutines.launch 42 43 /** 44 * Binds a wifi icon in the status bar to its view-model. 45 * 46 * To use this properly, users should maintain a one-to-one relationship between the [View] and the 47 * view-binding, binding each view only once. It is okay and expected for the same instance of the 48 * view-model to be reused for multiple view/view-binder bindings. 49 */ 50 @OptIn(InternalCoroutinesApi::class) 51 @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") 52 object WifiViewBinder { 53 54 /** Binds the view to the view-model, continuing to update the former based on the latter. */ 55 @JvmStatic 56 fun bind( 57 view: ViewGroup, 58 viewModel: LocationBasedWifiViewModel, 59 ): ModernStatusBarViewBinding { 60 val groupView = view.requireViewById<ViewGroup>(R.id.wifi_group) 61 val iconView = view.requireViewById<ImageView>(R.id.wifi_signal) 62 val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot) 63 val activityInView = view.requireViewById<ImageView>(R.id.wifi_in) 64 val activityOutView = view.requireViewById<ImageView>(R.id.wifi_out) 65 val activityContainerView = view.requireViewById<View>(R.id.inout_container) 66 val airplaneSpacer = view.requireViewById<View>(R.id.wifi_airplane_spacer) 67 68 view.isVisible = true 69 iconView.isVisible = true 70 71 // TODO(b/238425913): We should log this visibility state. 72 @StatusBarIconView.VisibleState 73 val visibilityState: MutableStateFlow<Int> = MutableStateFlow(STATE_HIDDEN) 74 75 val iconTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor) 76 val decorTint: MutableStateFlow<Int> = MutableStateFlow(viewModel.defaultColor) 77 78 var isCollecting: Boolean = false 79 80 view.repeatWhenAttached { 81 repeatOnLifecycle(Lifecycle.State.STARTED) { 82 isCollecting = true 83 84 launch { 85 visibilityState.collect { visibilityState -> 86 groupView.isVisible = visibilityState == STATE_ICON 87 dotView.isVisible = visibilityState == STATE_DOT 88 } 89 } 90 91 launch { 92 viewModel.wifiIcon.collect { wifiIcon -> 93 view.isVisible = wifiIcon is WifiIcon.Visible 94 if (wifiIcon is WifiIcon.Visible) { 95 IconViewBinder.bind(wifiIcon.icon, iconView) 96 } 97 } 98 } 99 100 launch { 101 iconTint.collect { tint -> 102 val tintList = ColorStateList.valueOf(tint) 103 iconView.imageTintList = tintList 104 activityInView.imageTintList = tintList 105 activityOutView.imageTintList = tintList 106 dotView.setDecorColor(tint) 107 } 108 } 109 110 launch { decorTint.collect { tint -> dotView.setDecorColor(tint) } } 111 112 launch { 113 viewModel.isActivityInViewVisible.distinctUntilChanged().collect { visible -> 114 activityInView.isVisible = visible 115 } 116 } 117 118 launch { 119 viewModel.isActivityOutViewVisible.distinctUntilChanged().collect { visible -> 120 activityOutView.isVisible = visible 121 } 122 } 123 124 launch { 125 viewModel.isActivityContainerVisible.distinctUntilChanged().collect { visible -> 126 activityContainerView.isVisible = visible 127 } 128 } 129 130 launch { 131 viewModel.isAirplaneSpacerVisible.distinctUntilChanged().collect { visible -> 132 airplaneSpacer.isVisible = visible 133 } 134 } 135 136 try { 137 awaitCancellation() 138 } finally { 139 isCollecting = false 140 } 141 } 142 } 143 144 return object : ModernStatusBarViewBinding { 145 override fun getShouldIconBeVisible(): Boolean { 146 return viewModel.wifiIcon.value is WifiIcon.Visible 147 } 148 149 override fun onVisibilityStateChanged(@StatusBarIconView.VisibleState state: Int) { 150 visibilityState.value = state 151 } 152 153 override fun onIconTintChanged(newTint: Int) { 154 if (viewModel.useDebugColoring) { 155 return 156 } 157 iconTint.value = newTint 158 } 159 160 override fun onDecorTintChanged(newTint: Int) { 161 if (viewModel.useDebugColoring) { 162 return 163 } 164 decorTint.value = newTint 165 } 166 167 override fun isCollecting(): Boolean { 168 return isCollecting 169 } 170 } 171 } 172 } 173