Create Configuration overrides for MCC/MNC values
SystemUI currently only shows the RAT type indicator for the active network, which may change. At the same time, the Resources system does not track the active mobile network, which means that `context.getResources().getConfiguration()` may return a `Configuration` object with an incorrect MCC/MNC for the current active subscription. This CL creates a `MobileContextProvider` class, which will lazily generate `Context`s with a correctly overridden MCC/MNC, so that resource overlays properly load individual carrier iconography when the active data subscription changes. We also will load each individual mobile icon with the proper context, in case there is ever a need to show the RAT type indicator for multiple subscriptions at once. Lastly, this CL adds a new "mccmnc" arg string to demo mode so that we can easily validate RAT type indicator overlays. Test: atest MobileContextProviderTest Test: use the new `mccmnc` string in sysui demo mode to manually verify Fixes: 240555502 Change-Id: If1f7d31a46f19f335b9545ca33638786a423ba0d
This commit is contained in:
@@ -78,6 +78,9 @@ public class MobileStatusTracker {
|
||||
* Config the MobileStatusTracker to start or stop monitoring platform signals.
|
||||
*/
|
||||
public void setListening(boolean listening) {
|
||||
if (mListening == listening) {
|
||||
return;
|
||||
}
|
||||
mListening = listening;
|
||||
if (listening) {
|
||||
mPhone.registerTelephonyCallback(mReceiverHandler::post, mTelephonyCallback);
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.android.settingslib.graph.SignalDrawable;
|
||||
import com.android.systemui.DualToneHandler;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController;
|
||||
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -64,6 +65,15 @@ public class StatusBarMobileView extends FrameLayout implements DarkReceiver,
|
||||
|
||||
/**
|
||||
* Designated constructor
|
||||
*
|
||||
* This view is special, in that it is the only view in SystemUI that allows for a configuration
|
||||
* override on a MCC/MNC-basis. This means that for every mobile view inflated, we have to
|
||||
* construct a context with that override, since the resource system doesn't have a way to
|
||||
* handle this for us.
|
||||
*
|
||||
* @param context A context with resources configured by MCC/MNC
|
||||
* @param slot The string key defining which slot this icon refers to. Always "mobile" for the
|
||||
* mobile icon
|
||||
*/
|
||||
public static StatusBarMobileView fromContext(
|
||||
Context context,
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.connectivity.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.view.ContextThemeWrapper
|
||||
import com.android.systemui.Dumpable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.demomode.DemoMode
|
||||
import com.android.systemui.demomode.DemoMode.COMMAND_NETWORK
|
||||
import com.android.systemui.demomode.DemoModeController
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.statusbar.connectivity.NetworkController
|
||||
import com.android.systemui.statusbar.connectivity.SignalCallback
|
||||
import java.io.PrintWriter
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Every subscriptionId can have its own CarrierConfig associated with it, so we have to create our
|
||||
* own [Configuration] and track resources based on the full set of available mcc-mnc combinations.
|
||||
*
|
||||
* (for future reference: b/240555502 is the initiating bug for this)
|
||||
*/
|
||||
@SysUISingleton
|
||||
class MobileContextProvider
|
||||
@Inject
|
||||
constructor(
|
||||
networkController: NetworkController,
|
||||
dumpManager: DumpManager,
|
||||
private val demoModeController: DemoModeController,
|
||||
) : Dumpable, DemoMode {
|
||||
private val subscriptions = mutableMapOf<Int, SubscriptionInfo>()
|
||||
private val signalCallback =
|
||||
object : SignalCallback {
|
||||
override fun setSubs(subs: List<SubscriptionInfo>) {
|
||||
subscriptions.clear()
|
||||
subs.forEach { info -> subscriptions[info.subscriptionId] = info }
|
||||
}
|
||||
}
|
||||
|
||||
// These should always be null when not in demo mode
|
||||
private var demoMcc: Int? = null
|
||||
private var demoMnc: Int? = null
|
||||
|
||||
init {
|
||||
networkController.addCallback(signalCallback)
|
||||
dumpManager.registerDumpable(this)
|
||||
demoModeController.addCallback(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a context with the MCC/MNC [Configuration] values corresponding to this
|
||||
* subscriptionId
|
||||
*/
|
||||
fun getMobileContextForSub(subId: Int, context: Context): Context {
|
||||
if (demoModeController.isInDemoMode) {
|
||||
return createMobileContextForDemoMode(context)
|
||||
}
|
||||
|
||||
// Fail back to the given context if no sub exists
|
||||
val info = subscriptions[subId] ?: return context
|
||||
|
||||
return createCarrierConfigContext(context, info.mcc, info.mnc)
|
||||
}
|
||||
|
||||
/** For Demo mode (for now), just apply the same MCC/MNC override for all subIds */
|
||||
private fun createMobileContextForDemoMode(context: Context): Context {
|
||||
return createCarrierConfigContext(context, demoMcc ?: 0, demoMnc ?: 0)
|
||||
}
|
||||
|
||||
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||
pw.println(
|
||||
"Subscriptions below will be inflated with a configuration context with " +
|
||||
"MCC/MNC overrides"
|
||||
)
|
||||
subscriptions.forEach { (subId, info) ->
|
||||
pw.println(" Subscription with subId($subId) with MCC/MNC(${info.mcc}/${info.mnc})")
|
||||
}
|
||||
pw.println(" MCC override: ${demoMcc ?: "(none)"}")
|
||||
pw.println(" MNC override: ${demoMnc ?: "(none)"}")
|
||||
}
|
||||
|
||||
override fun demoCommands(): List<String> {
|
||||
return listOf(COMMAND_NETWORK)
|
||||
}
|
||||
|
||||
override fun onDemoModeFinished() {
|
||||
demoMcc = null
|
||||
demoMnc = null
|
||||
}
|
||||
|
||||
override fun dispatchDemoCommand(command: String, args: Bundle) {
|
||||
val mccmnc = args.getString("mccmnc") ?: return
|
||||
// Only length 5/6 strings are valid
|
||||
if (!(mccmnc.length == 5 || mccmnc.length == 6)) {
|
||||
return
|
||||
}
|
||||
|
||||
// MCC is always the first 3 digits, and mnc is the last 2 or 3
|
||||
demoMcc = mccmnc.subSequence(0, 3).toString().toInt()
|
||||
demoMnc = mccmnc.subSequence(3, mccmnc.length).toString().toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
/**
|
||||
* Creates a context based on this [SubscriptionInfo]'s MCC/MNC values, allowing the overlay
|
||||
* system to properly load different carrier's iconography
|
||||
*/
|
||||
private fun createCarrierConfigContext(context: Context, mcc: Int, mnc: Int): Context {
|
||||
// Copy the existing configuration
|
||||
val c = Configuration(context.resources.configuration)
|
||||
c.mcc = mcc
|
||||
c.mnc = mnc
|
||||
|
||||
return ContextThemeWrapper(context, context.theme).also { ctx ->
|
||||
ctx.applyOverrideConfiguration(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package com.android.systemui.statusbar.phone;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.os.Bundle;
|
||||
@@ -29,13 +31,13 @@ import android.widget.LinearLayout;
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.demomode.DemoMode;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
|
||||
import com.android.systemui.statusbar.StatusBarIconView;
|
||||
import com.android.systemui.statusbar.StatusBarMobileView;
|
||||
import com.android.systemui.statusbar.StatusBarWifiView;
|
||||
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;
|
||||
|
||||
@@ -49,7 +51,6 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
|
||||
private final LinearLayout mStatusIcons;
|
||||
private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>();
|
||||
private final int mIconSize;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
|
||||
private StatusBarWifiView mWifiView;
|
||||
private boolean mDemoMode;
|
||||
@@ -57,14 +58,12 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
|
||||
|
||||
public DemoStatusIcons(
|
||||
LinearLayout statusIcons,
|
||||
int iconSize,
|
||||
FeatureFlags featureFlags
|
||||
int iconSize
|
||||
) {
|
||||
super(statusIcons.getContext());
|
||||
mStatusIcons = statusIcons;
|
||||
mIconSize = iconSize;
|
||||
mColor = DarkIconDispatcher.DEFAULT_ICON_TINT;
|
||||
mFeatureFlags = featureFlags;
|
||||
|
||||
if (statusIcons instanceof StatusIconContainer) {
|
||||
setShouldRestrictIcons(((StatusIconContainer) statusIcons).isRestrictingIcons());
|
||||
@@ -253,9 +252,13 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
|
||||
}
|
||||
}
|
||||
|
||||
public void addMobileView(MobileIconState state) {
|
||||
/**
|
||||
* Add a new mobile icon view
|
||||
*/
|
||||
public void addMobileView(MobileIconState state, Context mobileContext) {
|
||||
Log.d(TAG, "addMobileView: ");
|
||||
StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, state.slot);
|
||||
StatusBarMobileView view = StatusBarMobileView
|
||||
.fromContext(mobileContext, state.slot);
|
||||
|
||||
view.applyMobileState(state);
|
||||
view.setStaticDrawableColor(mColor);
|
||||
@@ -265,19 +268,24 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da
|
||||
addView(view, getChildCount(), createLayoutParams());
|
||||
}
|
||||
|
||||
public void updateMobileState(MobileIconState state) {
|
||||
Log.d(TAG, "updateMobileState: ");
|
||||
// If the view for this subId exists already, use it
|
||||
/**
|
||||
* 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
|
||||
* update it, since the context (and thus the {@link Configuration}) may have changed
|
||||
*/
|
||||
public void updateMobileState(MobileIconState state, Context mobileContext) {
|
||||
Log.d(TAG, "updateMobileState: " + state);
|
||||
|
||||
// The mobile config provided by MobileContextProvider could have changed; always recreate
|
||||
for (int i = 0; i < mMobileViews.size(); i++) {
|
||||
StatusBarMobileView view = mMobileViews.get(i);
|
||||
if (view.getState().subId == state.subId) {
|
||||
view.applyMobileState(state);
|
||||
return;
|
||||
removeView(view);
|
||||
}
|
||||
}
|
||||
|
||||
// Else we have to add it
|
||||
addMobileView(state);
|
||||
// Add the replacement or new icon
|
||||
addMobileView(state, mobileContext);
|
||||
}
|
||||
|
||||
public void onRemoveIcon(StatusIconDisplayable view) {
|
||||
|
||||
@@ -36,7 +36,6 @@ import com.android.internal.statusbar.StatusBarIcon;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.dagger.SysUISingleton;
|
||||
import com.android.systemui.demomode.DemoModeCommandReceiver;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
|
||||
import com.android.systemui.statusbar.BaseStatusBarWifiView;
|
||||
@@ -44,6 +43,7 @@ import com.android.systemui.statusbar.StatusBarIconView;
|
||||
import com.android.systemui.statusbar.StatusBarMobileView;
|
||||
import com.android.systemui.statusbar.StatusBarWifiView;
|
||||
import com.android.systemui.statusbar.StatusIconDisplayable;
|
||||
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
|
||||
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
|
||||
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
|
||||
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState;
|
||||
@@ -70,6 +70,7 @@ public interface StatusBarIconController {
|
||||
void addIconGroup(IconManager iconManager);
|
||||
/** */
|
||||
void removeIconGroup(IconManager iconManager);
|
||||
|
||||
/** Refresh the state of an IconManager by recreating the views */
|
||||
void refreshIconGroup(IconManager iconManager);
|
||||
/** */
|
||||
@@ -82,21 +83,25 @@ public interface StatusBarIconController {
|
||||
void setSignalIcon(String slot, WifiIconState state);
|
||||
/** */
|
||||
void setMobileIcons(String slot, List<MobileIconState> states);
|
||||
|
||||
/**
|
||||
* Display the no calling & SMS icons.
|
||||
*/
|
||||
void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states);
|
||||
|
||||
/**
|
||||
* Display the no calling & SMS icons.
|
||||
*/
|
||||
void setNoCallingIcons(String slot, List<CallIndicatorIconState> states);
|
||||
|
||||
public void setIconVisibility(String slot, boolean b);
|
||||
|
||||
/**
|
||||
* Sets the live region mode for the icon
|
||||
* @see android.view.View#setAccessibilityLiveRegion(int)
|
||||
* @param slot Icon slot to set region for
|
||||
*
|
||||
* @param slot Icon slot to set region for
|
||||
* @param accessibilityLiveRegion live region mode for the icon
|
||||
* @see android.view.View#setAccessibilityLiveRegion(int)
|
||||
*/
|
||||
void setIconAccessibilityLiveRegion(String slot, int accessibilityLiveRegion);
|
||||
|
||||
@@ -115,8 +120,8 @@ public interface StatusBarIconController {
|
||||
static ArraySet<String> getIconHideList(Context context, String hideListStr) {
|
||||
ArraySet<String> ret = new ArraySet<>();
|
||||
String[] hideList = hideListStr == null
|
||||
? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
|
||||
: hideListStr.split(",");
|
||||
? context.getResources().getStringArray(R.array.config_statusBarIconsToExclude)
|
||||
: hideListStr.split(",");
|
||||
for (String slot : hideList) {
|
||||
if (!TextUtils.isEmpty(slot)) {
|
||||
ret.add(slot);
|
||||
@@ -134,11 +139,14 @@ public interface StatusBarIconController {
|
||||
|
||||
public DarkIconManager(
|
||||
LinearLayout linearLayout,
|
||||
FeatureFlags featureFlags,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> wifiViewModelProvider,
|
||||
MobileContextProvider mobileContextProvider,
|
||||
DarkIconDispatcher darkIconDispatcher) {
|
||||
super(linearLayout, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
|
||||
super(linearLayout,
|
||||
statusBarPipelineFlags,
|
||||
wifiViewModelProvider,
|
||||
mobileContextProvider);
|
||||
mIconHPadding = mContext.getResources().getDimensionPixelSize(
|
||||
R.dimen.status_bar_icon_padding);
|
||||
mDarkIconDispatcher = darkIconDispatcher;
|
||||
@@ -195,41 +203,49 @@ public interface StatusBarIconController {
|
||||
|
||||
@SysUISingleton
|
||||
public static class Factory {
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
|
||||
private final Provider<WifiViewModel> mWifiViewModelProvider;
|
||||
private final MobileContextProvider mMobileContextProvider;
|
||||
private final DarkIconDispatcher mDarkIconDispatcher;
|
||||
|
||||
@Inject
|
||||
public Factory(
|
||||
FeatureFlags featureFlags,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> wifiViewModelProvider,
|
||||
MobileContextProvider mobileContextProvider,
|
||||
DarkIconDispatcher darkIconDispatcher) {
|
||||
mFeatureFlags = featureFlags;
|
||||
mStatusBarPipelineFlags = statusBarPipelineFlags;
|
||||
mWifiViewModelProvider = wifiViewModelProvider;
|
||||
mMobileContextProvider = mobileContextProvider;
|
||||
mDarkIconDispatcher = darkIconDispatcher;
|
||||
}
|
||||
|
||||
public DarkIconManager create(LinearLayout group) {
|
||||
return new DarkIconManager(
|
||||
group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider,
|
||||
group,
|
||||
mStatusBarPipelineFlags,
|
||||
mWifiViewModelProvider,
|
||||
mMobileContextProvider,
|
||||
mDarkIconDispatcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** */
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class TintedIconManager extends IconManager {
|
||||
private int mColor;
|
||||
|
||||
public TintedIconManager(
|
||||
ViewGroup group,
|
||||
FeatureFlags featureFlags,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> wifiViewModelProvider) {
|
||||
super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider);
|
||||
Provider<WifiViewModel> wifiViewModelProvider,
|
||||
MobileContextProvider mobileContextProvider) {
|
||||
super(group,
|
||||
statusBarPipelineFlags,
|
||||
wifiViewModelProvider,
|
||||
mobileContextProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -261,23 +277,26 @@ public interface StatusBarIconController {
|
||||
|
||||
@SysUISingleton
|
||||
public static class Factory {
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
|
||||
private final Provider<WifiViewModel> mWifiViewModelProvider;
|
||||
private final MobileContextProvider mMobileContextProvider;
|
||||
|
||||
@Inject
|
||||
public Factory(
|
||||
FeatureFlags featureFlags,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> wifiViewModelProvider) {
|
||||
mFeatureFlags = featureFlags;
|
||||
Provider<WifiViewModel> wifiViewModelProvider,
|
||||
MobileContextProvider mobileContextProvider) {
|
||||
mStatusBarPipelineFlags = statusBarPipelineFlags;
|
||||
mWifiViewModelProvider = wifiViewModelProvider;
|
||||
mMobileContextProvider = mobileContextProvider;
|
||||
}
|
||||
|
||||
public TintedIconManager create(ViewGroup group) {
|
||||
return new TintedIconManager(
|
||||
group, mFeatureFlags, mStatusBarPipelineFlags, mWifiViewModelProvider);
|
||||
group,
|
||||
mStatusBarPipelineFlags,
|
||||
mWifiViewModelProvider,
|
||||
mMobileContextProvider);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -287,9 +306,9 @@ public interface StatusBarIconController {
|
||||
*/
|
||||
class IconManager implements DemoModeCommandReceiver {
|
||||
protected final ViewGroup mGroup;
|
||||
private final FeatureFlags mFeatureFlags;
|
||||
private final StatusBarPipelineFlags mStatusBarPipelineFlags;
|
||||
private final Provider<WifiViewModel> mWifiViewModelProvider;
|
||||
private final MobileContextProvider mMobileContextProvider;
|
||||
protected final Context mContext;
|
||||
protected final int mIconSize;
|
||||
// Whether or not these icons show up in dumpsys
|
||||
@@ -305,13 +324,13 @@ public interface StatusBarIconController {
|
||||
|
||||
public IconManager(
|
||||
ViewGroup group,
|
||||
FeatureFlags featureFlags,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> wifiViewModelProvider) {
|
||||
Provider<WifiViewModel> wifiViewModelProvider,
|
||||
MobileContextProvider mobileContextProvider) {
|
||||
mGroup = group;
|
||||
mFeatureFlags = featureFlags;
|
||||
mStatusBarPipelineFlags = statusBarPipelineFlags;
|
||||
mWifiViewModelProvider = wifiViewModelProvider;
|
||||
mMobileContextProvider = mobileContextProvider;
|
||||
mContext = group.getContext();
|
||||
mIconSize = mContext.getResources().getDimensionPixelSize(
|
||||
com.android.internal.R.dimen.status_bar_icon_size);
|
||||
@@ -403,12 +422,15 @@ public interface StatusBarIconController {
|
||||
|
||||
@VisibleForTesting
|
||||
protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) {
|
||||
StatusBarMobileView view = onCreateStatusBarMobileView(slot);
|
||||
// Use the `subId` field as a key to query for the correct context
|
||||
StatusBarMobileView view = onCreateStatusBarMobileView(state.subId, slot);
|
||||
view.applyMobileState(state);
|
||||
mGroup.addView(view, index, onCreateLayoutParams());
|
||||
|
||||
if (mIsInDemoMode) {
|
||||
mDemoStatusIcons.addMobileView(state);
|
||||
Context mobileContext = mMobileContextProvider
|
||||
.getMobileContextForSub(state.subId, mContext);
|
||||
mDemoStatusIcons.addMobileView(state, mobileContext);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
@@ -427,8 +449,10 @@ public interface StatusBarIconController {
|
||||
mContext, slot, mWifiViewModelProvider.get());
|
||||
}
|
||||
|
||||
private StatusBarMobileView onCreateStatusBarMobileView(String slot) {
|
||||
StatusBarMobileView view = StatusBarMobileView.fromContext(mContext, slot);
|
||||
private StatusBarMobileView onCreateStatusBarMobileView(int subId, String slot) {
|
||||
Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext);
|
||||
StatusBarMobileView view = StatusBarMobileView
|
||||
.fromContext(mobileContext, slot);
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -516,7 +540,9 @@ public interface StatusBarIconController {
|
||||
}
|
||||
|
||||
if (mIsInDemoMode) {
|
||||
mDemoStatusIcons.updateMobileState(state);
|
||||
Context mobileContext = mMobileContextProvider
|
||||
.getMobileContextForSub(state.subId, mContext);
|
||||
mDemoStatusIcons.updateMobileState(state, mobileContext);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -553,7 +579,7 @@ public interface StatusBarIconController {
|
||||
}
|
||||
|
||||
protected DemoStatusIcons createDemoStatusIcons() {
|
||||
return new DemoStatusIcons((LinearLayout) mGroup, mIconSize, mFeatureFlags);
|
||||
return new DemoStatusIcons((LinearLayout) mGroup, mIconSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.connectivity.ui
|
||||
|
||||
import android.telephony.SubscriptionInfo
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.demomode.DemoModeController
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.statusbar.connectivity.NetworkController
|
||||
import com.android.systemui.statusbar.connectivity.SignalCallback
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.withArgCaptor
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.verify
|
||||
import org.mockito.Mockito.`when` as whenever
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@SmallTest
|
||||
class MobileContextProviderTest : SysuiTestCase() {
|
||||
@Mock private lateinit var networkController: NetworkController
|
||||
@Mock private lateinit var dumpManager: DumpManager
|
||||
@Mock private lateinit var demoModeController: DemoModeController
|
||||
|
||||
private lateinit var provider: MobileContextProvider
|
||||
private lateinit var signalCallback: SignalCallback
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
provider =
|
||||
MobileContextProvider(
|
||||
networkController,
|
||||
dumpManager,
|
||||
demoModeController,
|
||||
)
|
||||
|
||||
signalCallback = withArgCaptor { verify(networkController).addCallback(capture()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_oneSubscription_contextHasMccMnc() {
|
||||
// GIVEN there is one SubscriptionInfo
|
||||
signalCallback.setSubs(listOf(SUB_1))
|
||||
|
||||
// WHEN we ask for a mobile context
|
||||
val ctx = provider.getMobileContextForSub(SUB_1_ID, context)
|
||||
|
||||
// THEN the configuration of that context reflect this subscription's MCC/MNC override
|
||||
val config = ctx.resources.configuration
|
||||
assertThat(config.mcc).isEqualTo(SUB_1_MCC)
|
||||
assertThat(config.mnc).isEqualTo(SUB_1_MNC)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_twoSubscriptions_eachContextReflectsMccMnc() {
|
||||
// GIVEN there are two SubscriptionInfos
|
||||
signalCallback.setSubs(listOf(SUB_1, SUB_2))
|
||||
|
||||
// WHEN we ask for a mobile context for each sub
|
||||
val ctx1 = provider.getMobileContextForSub(SUB_1_ID, context)
|
||||
val ctx2 = provider.getMobileContextForSub(SUB_2_ID, context)
|
||||
|
||||
// THEN the configuration of each context reflect this subscription's MCC/MNC override
|
||||
val config1 = ctx1.resources.configuration
|
||||
assertThat(config1.mcc).isEqualTo(SUB_1_MCC)
|
||||
assertThat(config1.mnc).isEqualTo(SUB_1_MNC)
|
||||
|
||||
val config2 = ctx2.resources.configuration
|
||||
assertThat(config2.mcc).isEqualTo(SUB_2_MCC)
|
||||
assertThat(config2.mnc).isEqualTo(SUB_2_MNC)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_requestingContextForNonexistentSubscription_returnsGivenContext() {
|
||||
// GIVEN no SubscriptionInfos
|
||||
signalCallback.setSubs(listOf())
|
||||
|
||||
// WHEN we ask for a mobile context for an unknown subscription
|
||||
val ctx = provider.getMobileContextForSub(SUB_1_ID, context)
|
||||
|
||||
// THEN we get the original context back
|
||||
assertThat(ctx).isEqualTo(context)
|
||||
}
|
||||
|
||||
private val SUB_1_ID = 1
|
||||
private val SUB_1_MCC = 123
|
||||
private val SUB_1_MNC = 456
|
||||
private val SUB_1 =
|
||||
mock<SubscriptionInfo>().also {
|
||||
whenever(it.subscriptionId).thenReturn(SUB_1_ID)
|
||||
whenever(it.mcc).thenReturn(SUB_1_MCC)
|
||||
whenever(it.mnc).thenReturn(SUB_1_MNC)
|
||||
}
|
||||
|
||||
private val SUB_2_ID = 2
|
||||
private val SUB_2_MCC = 666
|
||||
private val SUB_2_MNC = 777
|
||||
private val SUB_2 =
|
||||
mock<SubscriptionInfo>().also {
|
||||
whenever(it.subscriptionId).thenReturn(SUB_2_ID)
|
||||
whenever(it.mcc).thenReturn(SUB_2_MCC)
|
||||
whenever(it.mnc).thenReturn(SUB_2_MNC)
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,10 @@ import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
@@ -30,12 +33,12 @@ import android.widget.LinearLayout;
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.statusbar.StatusBarIcon;
|
||||
import com.android.systemui.flags.FeatureFlags;
|
||||
import com.android.systemui.plugins.DarkIconDispatcher;
|
||||
import com.android.systemui.statusbar.StatusBarIconView;
|
||||
import com.android.systemui.statusbar.StatusBarMobileView;
|
||||
import com.android.systemui.statusbar.StatusBarWifiView;
|
||||
import com.android.systemui.statusbar.StatusIconDisplayable;
|
||||
import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager;
|
||||
import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState;
|
||||
@@ -55,15 +58,19 @@ import javax.inject.Provider;
|
||||
@SmallTest
|
||||
public class StatusBarIconControllerTest extends LeakCheckedTest {
|
||||
|
||||
private MobileContextProvider mMobileContextProvider = mock(MobileContextProvider.class);
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
|
||||
// For testing, ignore context overrides
|
||||
when(mMobileContextProvider.getMobileContextForSub(anyInt(), any())).thenReturn(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetCalledOnAdd_IconManager() {
|
||||
LinearLayout layout = new LinearLayout(mContext);
|
||||
TestIconManager manager = new TestIconManager(layout);
|
||||
TestIconManager manager = new TestIconManager(layout, mMobileContextProvider);
|
||||
testCallOnAdd_forManager(manager);
|
||||
}
|
||||
|
||||
@@ -72,9 +79,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
|
||||
LinearLayout layout = new LinearLayout(mContext);
|
||||
TestDarkIconManager manager = new TestDarkIconManager(
|
||||
layout,
|
||||
mock(FeatureFlags.class),
|
||||
mock(StatusBarPipelineFlags.class),
|
||||
() -> mock(WifiViewModel.class),
|
||||
mMobileContextProvider,
|
||||
mock(DarkIconDispatcher.class));
|
||||
testCallOnAdd_forManager(manager);
|
||||
}
|
||||
@@ -114,11 +121,14 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
|
||||
|
||||
TestDarkIconManager(
|
||||
LinearLayout group,
|
||||
FeatureFlags featureFlags,
|
||||
StatusBarPipelineFlags statusBarPipelineFlags,
|
||||
Provider<WifiViewModel> wifiViewModelProvider,
|
||||
MobileContextProvider contextProvider,
|
||||
DarkIconDispatcher darkIconDispatcher) {
|
||||
super(group, featureFlags, statusBarPipelineFlags, wifiViewModelProvider,
|
||||
super(group,
|
||||
statusBarPipelineFlags,
|
||||
wifiViewModelProvider,
|
||||
contextProvider,
|
||||
darkIconDispatcher);
|
||||
}
|
||||
|
||||
@@ -153,11 +163,11 @@ public class StatusBarIconControllerTest extends LeakCheckedTest {
|
||||
}
|
||||
|
||||
private static class TestIconManager extends IconManager implements TestableIconManager {
|
||||
TestIconManager(ViewGroup group) {
|
||||
TestIconManager(ViewGroup group, MobileContextProvider contextProvider) {
|
||||
super(group,
|
||||
mock(FeatureFlags.class),
|
||||
mock(StatusBarPipelineFlags.class),
|
||||
() -> mock(WifiViewModel.class));
|
||||
() -> mock(WifiViewModel.class),
|
||||
contextProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user