Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +18 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupR import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy Loading @@ -53,6 +54,9 @@ import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import kotlinx.coroutines.flow.Flow import java.util.function.Supplier import javax.inject.Named @Module abstract class StatusBarPipelineModule { Loading Loading @@ -113,6 +117,17 @@ abstract class StatusBarPipelineModule { } } @Provides @SysUISingleton @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON) fun provideFirstMobileSubShowingNetworkTypeIconProvider( mobileIconsViewModel: MobileIconsViewModel, ): Supplier<Flow<Boolean>> { return Supplier<Flow<Boolean>> { mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon } } @Provides @SysUISingleton @WifiInputLog Loading Loading @@ -168,5 +183,8 @@ abstract class StatusBarPipelineModule { fun provideVerboseMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer { return factory.create("VerboseMobileViewLog", 100) } const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON = "FirstMobileSubShowingNetworkTypeIcon" } } packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +40 −11 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch Loading Loading @@ -63,13 +66,46 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), listOf()) private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> = subscriptionIdsFlow .map { if (it.isEmpty()) { null } else { // Mobile icons get reversed by [StatusBarIconController], so the last element // in this list will show up visually first. commonViewModelForSub(it.last()) } } .stateIn(scope, SharingStarted.WhileSubscribed(), null) /** * A flow that emits `true` if the mobile sub that's displayed first visually is showing its * network type icon and `false` otherwise. */ val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> = firstMobileSubViewModel .flatMapLatest { firstMobileSubViewModel -> firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false) } .stateIn(scope, SharingStarted.WhileSubscribed(), false) init { scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } } } fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel { val common = mobileIconSubIdCache[subId] val common = commonViewModelForSub(subId) return LocationBasedMobileViewModel.viewModelForLocation( common, statusBarPipelineFlags, verboseLogger, location, ) } private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon { return mobileIconSubIdCache[subId] ?: MobileIconViewModel( subId, interactor.createMobileConnectionInteractorForSubId(subId), Loading @@ -78,13 +114,6 @@ constructor( scope, ) .also { mobileIconSubIdCache[subId] = it } return LocationBasedMobileViewModel.viewModelForLocation( common, statusBarPipelineFlags, verboseLogger, location, ) } private fun removeInvalidModelsFromCache(subIds: List<Int>) { Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt +7 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWi import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch Loading Loading @@ -64,6 +63,7 @@ object WifiViewBinder { val activityOutView = view.requireViewById<ImageView>(R.id.wifi_out) val activityContainerView = view.requireViewById<View>(R.id.inout_container) val airplaneSpacer = view.requireViewById<View>(R.id.wifi_airplane_spacer) val signalSpacer = view.requireViewById<View>(R.id.wifi_signal_spacer) view.isVisible = true iconView.isVisible = true Loading Loading @@ -133,6 +133,12 @@ object WifiViewBinder { } } launch { viewModel.isSignalSpacerVisible.distinctUntilChanged().collect { visible -> signalSpacer.isVisible = visible } } try { awaitCancellation() } finally { Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt +12 −8 Original line number Diff line number Diff line Loading @@ -30,8 +30,8 @@ import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel Loading @@ -39,7 +39,9 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon import java.util.function.Supplier import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted Loading @@ -53,24 +55,24 @@ import kotlinx.coroutines.flow.stateIn /** * Models the UI state for the status bar wifi icon. * * This class exposes three view models, one per status bar location: [home], [keyguard], and [qs]. * In order to get the UI state for the wifi icon, you must use one of those view models (whichever * is correct for your location). * * Internally, this class maintains the current state of the wifi icon and notifies those three view * models of any changes. * This is a singleton so that we don't have duplicate logs and should *not* be used directly to * control views. Instead, use an instance of [LocationBasedWifiViewModel]. See * [LocationBasedWifiViewModel.viewModelForLocation]. */ @SysUISingleton class WifiViewModel @Inject constructor( airplaneModeViewModel: AirplaneModeViewModel, // TODO(b/238425913): The wifi icon shouldn't need to consume mobile information. A // container-level view model should do the work instead. @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON) shouldShowSignalSpacerProvider: Supplier<Flow<Boolean>>, connectivityConstants: ConnectivityConstants, private val context: Context, @WifiTableLog wifiTableLogBuffer: TableLogBuffer, interactor: WifiInteractor, @Application private val scope: CoroutineScope, statusBarPipelineFlags: StatusBarPipelineFlags, wifiConstants: WifiConstants, ) : WifiViewModelCommon { /** Returns the icon to use based on the given network. */ Loading Loading @@ -183,6 +185,8 @@ constructor( override val isAirplaneSpacerVisible: Flow<Boolean> = airplaneModeViewModel.isAirplaneModeIconVisible override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get() companion object { @StringRes @VisibleForTesting Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt +3 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,7 @@ interface WifiViewModelCommon { /** True if the airplane spacer view should be visible. */ val isAirplaneSpacerVisible: Flow<Boolean> /** True if the spacer between the wifi icon and the RAT icon should be visible. */ val isSignalSpacerVisible: Flow<Boolean> } Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +18 −0 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupR import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy Loading @@ -53,6 +54,9 @@ import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap import kotlinx.coroutines.flow.Flow import java.util.function.Supplier import javax.inject.Named @Module abstract class StatusBarPipelineModule { Loading Loading @@ -113,6 +117,17 @@ abstract class StatusBarPipelineModule { } } @Provides @SysUISingleton @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON) fun provideFirstMobileSubShowingNetworkTypeIconProvider( mobileIconsViewModel: MobileIconsViewModel, ): Supplier<Flow<Boolean>> { return Supplier<Flow<Boolean>> { mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon } } @Provides @SysUISingleton @WifiInputLog Loading Loading @@ -168,5 +183,8 @@ abstract class StatusBarPipelineModule { fun provideVerboseMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer { return factory.create("VerboseMobileViewLog", 100) } const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON = "FirstMobileSubShowingNetworkTypeIcon" } }
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt +40 −11 Original line number Diff line number Diff line Loading @@ -32,6 +32,9 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch Loading Loading @@ -63,13 +66,46 @@ constructor( } .stateIn(scope, SharingStarted.WhileSubscribed(), listOf()) private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> = subscriptionIdsFlow .map { if (it.isEmpty()) { null } else { // Mobile icons get reversed by [StatusBarIconController], so the last element // in this list will show up visually first. commonViewModelForSub(it.last()) } } .stateIn(scope, SharingStarted.WhileSubscribed(), null) /** * A flow that emits `true` if the mobile sub that's displayed first visually is showing its * network type icon and `false` otherwise. */ val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> = firstMobileSubViewModel .flatMapLatest { firstMobileSubViewModel -> firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false) } .stateIn(scope, SharingStarted.WhileSubscribed(), false) init { scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } } } fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel { val common = mobileIconSubIdCache[subId] val common = commonViewModelForSub(subId) return LocationBasedMobileViewModel.viewModelForLocation( common, statusBarPipelineFlags, verboseLogger, location, ) } private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon { return mobileIconSubIdCache[subId] ?: MobileIconViewModel( subId, interactor.createMobileConnectionInteractorForSubId(subId), Loading @@ -78,13 +114,6 @@ constructor( scope, ) .also { mobileIconSubIdCache[subId] = it } return LocationBasedMobileViewModel.viewModelForLocation( common, statusBarPipelineFlags, verboseLogger, location, ) } private fun removeInvalidModelsFromCache(subIds: List<Int>) { Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt +7 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWi import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch Loading Loading @@ -64,6 +63,7 @@ object WifiViewBinder { val activityOutView = view.requireViewById<ImageView>(R.id.wifi_out) val activityContainerView = view.requireViewById<View>(R.id.inout_container) val airplaneSpacer = view.requireViewById<View>(R.id.wifi_airplane_spacer) val signalSpacer = view.requireViewById<View>(R.id.wifi_signal_spacer) view.isVisible = true iconView.isVisible = true Loading Loading @@ -133,6 +133,12 @@ object WifiViewBinder { } } launch { viewModel.isSignalSpacerVisible.distinctUntilChanged().collect { visible -> signalSpacer.isVisible = visible } } try { awaitCancellation() } finally { Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt +12 −8 Original line number Diff line number Diff line Loading @@ -30,8 +30,8 @@ import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel Loading @@ -39,7 +39,9 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon import java.util.function.Supplier import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted Loading @@ -53,24 +55,24 @@ import kotlinx.coroutines.flow.stateIn /** * Models the UI state for the status bar wifi icon. * * This class exposes three view models, one per status bar location: [home], [keyguard], and [qs]. * In order to get the UI state for the wifi icon, you must use one of those view models (whichever * is correct for your location). * * Internally, this class maintains the current state of the wifi icon and notifies those three view * models of any changes. * This is a singleton so that we don't have duplicate logs and should *not* be used directly to * control views. Instead, use an instance of [LocationBasedWifiViewModel]. See * [LocationBasedWifiViewModel.viewModelForLocation]. */ @SysUISingleton class WifiViewModel @Inject constructor( airplaneModeViewModel: AirplaneModeViewModel, // TODO(b/238425913): The wifi icon shouldn't need to consume mobile information. A // container-level view model should do the work instead. @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON) shouldShowSignalSpacerProvider: Supplier<Flow<Boolean>>, connectivityConstants: ConnectivityConstants, private val context: Context, @WifiTableLog wifiTableLogBuffer: TableLogBuffer, interactor: WifiInteractor, @Application private val scope: CoroutineScope, statusBarPipelineFlags: StatusBarPipelineFlags, wifiConstants: WifiConstants, ) : WifiViewModelCommon { /** Returns the icon to use based on the given network. */ Loading Loading @@ -183,6 +185,8 @@ constructor( override val isAirplaneSpacerVisible: Flow<Boolean> = airplaneModeViewModel.isAirplaneModeIconVisible override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get() companion object { @StringRes @VisibleForTesting Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt +3 −0 Original line number Diff line number Diff line Loading @@ -39,4 +39,7 @@ interface WifiViewModelCommon { /** True if the airplane spacer view should be visible. */ val isAirplaneSpacerVisible: Flow<Boolean> /** True if the spacer between the wifi icon and the RAT icon should be visible. */ val isSignalSpacerVisible: Flow<Boolean> }