Support new pipeline demo mode in the old view presenter

Adds support for the new mobile pipeline's demo mode to the old front
end. Note that this is kind of hacky because we actually don't have to
do anything to support demo mode, since it's implemented entirely at the
level of the repository. However, due to the fact that the old
implementation of demo mode added a special view, `DemoStatusIcons`,
that overlays the regular icons, we have to suport the new pipeline in
_that_ icon container.

Test: manual
Bug: 249790009
Change-Id: Ib02a1eb6f6abaec113a15045f9bd0b151d2a773f
This commit is contained in:
Evan Laird
2022-12-01 17:25:23 -05:00
parent a2c6879a83
commit cc43d6a878
13 changed files with 77 additions and 22 deletions

View File

@@ -40,6 +40,8 @@ import com.android.systemui.statusbar.StatusIconDisplayable;
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView;
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel;
import java.util.ArrayList;
import java.util.List;
@@ -50,20 +52,25 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
private final LinearLayout mStatusIcons;
private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
private final ArrayList<ModernStatusBarMobileView> mModernMobileViews = new ArrayList<>();
private final int mIconSize;
private StatusBarWifiView mWifiView;
private boolean mDemoMode;
private int mColor;
private final MobileIconsViewModel mMobileIconsViewModel;
public DemoStatusIcons(
LinearLayout statusIcons,
MobileIconsViewModel mobileIconsViewModel,
int iconSize
) {
super(statusIcons.getContext());
mStatusIcons = statusIcons;
mIconSize = iconSize;
mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
mMobileIconsViewModel = mobileIconsViewModel;
if (statusIcons instanceof StatusIconContainer) {
setShouldRestrictIcons(((StatusIconContainer) statusIcons).isRestrictingIcons());
@@ -71,7 +78,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
setShouldRestrictIcons(false);
}
setLayoutParams(mStatusIcons.getLayoutParams());
setPadding(mStatusIcons.getPaddingLeft(),mStatusIcons.getPaddingTop(),
setPadding(mStatusIcons.getPaddingLeft(), mStatusIcons.getPaddingTop(),
mStatusIcons.getPaddingRight(), mStatusIcons.getPaddingBottom());
setOrientation(mStatusIcons.getOrientation());
setGravity(Gravity.CENTER_VERTICAL); // no LL.getGravity()
@@ -115,6 +122,8 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
public void onDemoModeFinished() {
mDemoMode = false;
mStatusIcons.setVisibility(View.VISIBLE);
mModernMobileViews.clear();
mMobileViews.clear();
setVisibility(View.GONE);
}
@@ -268,6 +277,24 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
addView(view, getChildCount(), createLayoutParams());
}
/**
* Add a {@link ModernStatusBarMobileView}
* @param mobileContext possibly mcc/mnc overridden mobile context
* @param subId the subscriptionId for this mobile view
*/
public void addModernMobileView(Context mobileContext, int subId) {
Log.d(TAG, "addModernMobileView (subId=" + subId + ")");
ModernStatusBarMobileView view = ModernStatusBarMobileView.constructAndBind(
mobileContext,
"mobile",
mMobileIconsViewModel.viewModelForSub(subId)
);
// mobile always goes at the end
mModernMobileViews.add(view);
addView(view, getChildCount(), createLayoutParams());
}
/**
* Apply an update to a mobile icon view for the given {@link MobileIconState}. For
* compatibility with {@link MobileContextProvider}, we have to recreate the view every time we
@@ -292,12 +319,19 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
if (view.getSlot().equals("wifi")) {
removeView(mWifiView);
mWifiView = null;
} else {
} else if (view instanceof StatusBarMobileView) {
StatusBarMobileView mobileView = matchingMobileView(view);
if (mobileView != null) {
removeView(mobileView);
mMobileViews.remove(mobileView);
}
} else if (view instanceof ModernStatusBarMobileView) {
ModernStatusBarMobileView mobileView = matchingModernMobileView(
(ModernStatusBarMobileView) view);
if (mobileView != null) {
removeView(mobileView);
mModernMobileViews.remove(mobileView);
}
}
}
@@ -316,6 +350,16 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
return null;
}
private ModernStatusBarMobileView matchingModernMobileView(ModernStatusBarMobileView other) {
for (ModernStatusBarMobileView v : mModernMobileViews) {
if (v.getSubId() == other.getSubId()) {
return v;
}
}
return null;
}
private LayoutParams createLayoutParams() {
return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize);
}

View File

@@ -536,8 +536,7 @@ public interface StatusBarIconController {
mGroup.addView(view, index, onCreateLayoutParams());
if (mIsInDemoMode) {
// TODO (b/249790009): demo mode should be handled at the data layer in the
// new pipeline
mDemoStatusIcons.addModernMobileView(mContext, subId);
}
return view;
@@ -565,11 +564,13 @@ public interface StatusBarIconController {
private ModernStatusBarMobileView onCreateModernStatusBarMobileView(
String slot, int subId) {
Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
return ModernStatusBarMobileView
.constructAndBind(
mContext,
mobileContext,
slot,
mMobileIconsViewModel.viewModelForSub(subId));
mMobileIconsViewModel.viewModelForSub(subId)
);
}
protected LinearLayout.LayoutParams onCreateLayoutParams() {
@@ -704,7 +705,7 @@ public interface StatusBarIconController {
}
protected DemoStatusIcons createDemoStatusIcons() {
return new DemoStatusIcons((LinearLayout) mGroup, mIconSize);
return new DemoStatusIcons((LinearLayout) mGroup, mMobileIconsViewModel, mIconSize);
}
}
}

View File

@@ -276,6 +276,11 @@ public class StatusBarIconControllerImpl implements Tunable,
String slotName = mContext.getString(com.android.internal.R.string.status_bar_mobile);
Slot mobileSlot = mStatusBarIconList.getSlot(slotName);
// Because of the way we cache the icon holders, we need to remove everything any time
// we get a new set of subscriptions. This might change in the future, but is required
// to support demo mode for now
removeAllIconsForSlot(slotName);
Collections.reverse(subIds);
for (Integer subId : subIds) {

View File

@@ -36,6 +36,8 @@ import kotlinx.coroutines.flow.StateFlow
* eventually becomes a single icon in the status bar.
*/
interface MobileConnectionRepository {
/** The subscriptionId that this connection represents */
val subId: Int
/**
* A flow that aggregates all necessary callbacks from [TelephonyCallback] into a single
* listener + model.

View File

@@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.StateFlow
*/
interface MobileConnectionsRepository {
/** Observable list of current mobile subscriptions */
val subscriptionsFlow: Flow<List<SubscriptionInfo>>
val subscriptionsFlow: StateFlow<List<SubscriptionInfo>>
/** Observable for the subscriptionId of the current mobile data connection */
val activeMobileDataSubscriptionId: StateFlow<Int>

View File

@@ -268,7 +268,7 @@ private fun SignalIcon.MobileIconGroup?.toResolvedNetworkType(): ResolvedNetwork
else -> DefaultNetworkType(toNetworkType())
}
class DemoMobileConnectionRepository(val subId: Int) : MobileConnectionRepository {
class DemoMobileConnectionRepository(override val subId: Int) : MobileConnectionRepository {
override val subscriptionModelFlow = MutableStateFlow(MobileSubscriptionModel())
override val dataEnabled = MutableStateFlow(true)

View File

@@ -58,7 +58,7 @@ import kotlinx.coroutines.flow.stateIn
@OptIn(ExperimentalCoroutinesApi::class)
class MobileConnectionRepositoryImpl(
private val context: Context,
private val subId: Int,
override val subId: Int,
private val telephonyManager: TelephonyManager,
private val globalSettings: GlobalSettings,
defaultDataSubId: StateFlow<Int>,

View File

@@ -32,6 +32,8 @@ class ModernStatusBarMobileView(
attrs: AttributeSet?,
) : BaseStatusBarFrameLayout(context, attrs) {
var subId: Int = -1
private lateinit var slot: String
override fun getSlot() = slot
@@ -76,6 +78,7 @@ class ModernStatusBarMobileView(
as ModernStatusBarMobileView)
.also {
it.slot = slot
it.subId = viewModel.subscriptionId
MobileIconBinder.bind(it, viewModel)
}
}

View File

@@ -23,7 +23,7 @@ import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMob
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import javax.inject.Inject
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/**
* View model for describing the system's current mobile cellular connections. The result is a list
@@ -33,7 +33,7 @@ import kotlinx.coroutines.flow.Flow
class MobileIconsViewModel
@Inject
constructor(
val subscriptionIdsFlow: Flow<List<Int>>,
val subscriptionIdsFlow: StateFlow<List<Int>>,
private val interactor: MobileIconsInteractor,
private val logger: ConnectivityPipelineLogger,
) {
@@ -51,7 +51,7 @@ constructor(
private val interactor: MobileIconsInteractor,
private val logger: ConnectivityPipelineLogger,
) {
fun create(subscriptionIdsFlow: Flow<List<Int>>): MobileIconsViewModel {
fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
return MobileIconsViewModel(
subscriptionIdsFlow,
interactor,

View File

@@ -19,7 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel
import kotlinx.coroutines.flow.MutableStateFlow
class FakeMobileConnectionRepository : MobileConnectionRepository {
class FakeMobileConnectionRepository(override val subId: Int) : MobileConnectionRepository {
private val _subscriptionsModelFlow = MutableStateFlow(MobileSubscriptionModel())
override val subscriptionModelFlow = _subscriptionsModelFlow

View File

@@ -20,12 +20,11 @@ import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import com.android.settingslib.mobile.MobileMappings.Config
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
class FakeMobileConnectionsRepository : MobileConnectionsRepository {
private val _subscriptionsFlow = MutableStateFlow<List<SubscriptionInfo>>(listOf())
override val subscriptionsFlow: Flow<List<SubscriptionInfo>> = _subscriptionsFlow
override val subscriptionsFlow = _subscriptionsFlow
private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
@@ -41,7 +40,8 @@ class FakeMobileConnectionsRepository : MobileConnectionsRepository {
private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
return subIdRepos[subId] ?: FakeMobileConnectionRepository().also { subIdRepos[subId] = it }
return subIdRepos[subId]
?: FakeMobileConnectionRepository(subId).also { subIdRepos[subId] = it }
}
private val _globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)

View File

@@ -49,7 +49,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
private lateinit var underTest: MobileIconInteractor
private val mobileMappingsProxy = FakeMobileMappingsProxy()
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy)
private val connectionRepository = FakeMobileConnectionRepository()
private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID)
private val scope = CoroutineScope(IMMEDIATE)

View File

@@ -263,12 +263,12 @@ class MobileIconsInteractorTest : SysuiTestCase() {
private const val SUB_1_ID = 1
private val SUB_1 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
private val CONNECTION_1 = FakeMobileConnectionRepository()
private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID)
private const val SUB_2_ID = 2
private val SUB_2 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
private val CONNECTION_2 = FakeMobileConnectionRepository()
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID)
private const val SUB_3_ID = 3
private val SUB_3_OPP =
@@ -276,7 +276,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
whenever(it.subscriptionId).thenReturn(SUB_3_ID)
whenever(it.isOpportunistic).thenReturn(true)
}
private val CONNECTION_3 = FakeMobileConnectionRepository()
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID)
private const val SUB_4_ID = 4
private val SUB_4_OPP =
@@ -284,6 +284,6 @@ class MobileIconsInteractorTest : SysuiTestCase() {
whenever(it.subscriptionId).thenReturn(SUB_4_ID)
whenever(it.isOpportunistic).thenReturn(true)
}
private val CONNECTION_4 = FakeMobileConnectionRepository()
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID)
}
}