Merge "[Sim UI enhancement] MobileNetworkSettings UI enhancement" into main
This commit is contained in:
committed by
Android (Google) Code Review
commit
35b3ad939c
@@ -47,6 +47,23 @@
|
|||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
settings:controller="com.android.settings.network.telephony.SmsDefaultSubscriptionController"/>
|
settings:controller="com.android.settings.network.telephony.SmsDefaultSubscriptionController"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="mobile_network_spn"
|
||||||
|
android:title="@string/mobile_network_spn_title"
|
||||||
|
android:summary="@string/summary_placeholder"
|
||||||
|
android:selectable="false"
|
||||||
|
settings:controller="com.android.settings.network.telephony.MobileNetworkSpnPreferenceController"
|
||||||
|
settings:allowDividerAbove="true" />
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="phone_number"
|
||||||
|
android:title="@string/status_number"
|
||||||
|
android:summary="@string/summary_placeholder"
|
||||||
|
android:selectable="false"
|
||||||
|
settings:controller="com.android.settings.network.telephony.MobileNetworkPhoneNumberPreferenceController"
|
||||||
|
settings:allowDividerBelow="true"
|
||||||
|
settings:enableCopying="true"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
android:key="cdma_lte_data_service_key"
|
android:key="cdma_lte_data_service_key"
|
||||||
android:title="@string/cdma_lte_data_service"
|
android:title="@string/cdma_lte_data_service"
|
||||||
@@ -162,6 +179,24 @@
|
|||||||
settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController"
|
settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController"
|
||||||
settings:enableCopying="true"/>
|
settings:enableCopying="true"/>
|
||||||
|
|
||||||
|
<!-- IMEI -->
|
||||||
|
<Preference
|
||||||
|
android:key="network_mode_imei_info"
|
||||||
|
android:title="@string/status_imei"
|
||||||
|
android:summary="@string/summary_placeholder"
|
||||||
|
settings:keywords="@string/keywords_imei_info"
|
||||||
|
settings:enableCopying="true"
|
||||||
|
settings:controller="com.android.settings.network.telephony.MobileNetworkImeiPreferenceController"/>
|
||||||
|
<!-- EID -->
|
||||||
|
<com.android.settingslib.CustomDialogPreferenceCompat
|
||||||
|
android:key="network_mode_eid_info"
|
||||||
|
android:title="@string/status_eid"
|
||||||
|
android:summary="@string/device_info_protected_single_press"
|
||||||
|
android:positiveButtonText="@string/dlg_ok"
|
||||||
|
android:dialogLayout="@layout/dialog_eid_status"
|
||||||
|
settings:enableCopying="true"
|
||||||
|
settings:controller="com.android.settings.network.telephony.MobileNetworkEidPreferenceController"/>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="calling_category"
|
android:key="calling_category"
|
||||||
android:title="@string/call_category"
|
android:title="@string/call_category"
|
||||||
|
@@ -42,6 +42,7 @@ import androidx.preference.TwoStatePreference;
|
|||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.datausage.DataUsageUtils;
|
import com.android.settings.datausage.DataUsageUtils;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.network.MobileDataContentObserver;
|
import com.android.settings.network.MobileDataContentObserver;
|
||||||
import com.android.settings.network.ProxySubscriptionManager;
|
import com.android.settings.network.ProxySubscriptionManager;
|
||||||
import com.android.settings.network.SubscriptionsChangeListener;
|
import com.android.settings.network.SubscriptionsChangeListener;
|
||||||
@@ -194,7 +195,8 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvailabilityStatus(int subId) {
|
public int getAvailabilityStatus(int subId) {
|
||||||
if (!SubscriptionManager.isValidSubscriptionId(subId)
|
if (Flags.isDualSimOnboardingEnabled()
|
||||||
|
|| !SubscriptionManager.isValidSubscriptionId(subId)
|
||||||
|| SubscriptionManager.getDefaultDataSubscriptionId() == subId
|
|| SubscriptionManager.getDefaultDataSubscriptionId() == subId
|
||||||
|| (!hasMobileData())) {
|
|| (!hasMobileData())) {
|
||||||
return CONDITIONALLY_UNAVAILABLE;
|
return CONDITIONALLY_UNAVAILABLE;
|
||||||
|
@@ -32,6 +32,7 @@ import androidx.preference.PreferenceScreen;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.network.DefaultSubscriptionReceiver;
|
import com.android.settings.network.DefaultSubscriptionReceiver;
|
||||||
import com.android.settings.network.MobileNetworkRepository;
|
import com.android.settings.network.MobileNetworkRepository;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
@@ -88,6 +89,9 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvailabilityStatus(int subId) {
|
public int getAvailabilityStatus(int subId) {
|
||||||
|
if (Flags.isDualSimOnboardingEnabled()) {
|
||||||
|
return CONDITIONALLY_UNAVAILABLE;
|
||||||
|
}
|
||||||
return AVAILABLE;
|
return AVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -34,6 +34,7 @@ import androidx.preference.PreferenceScreen;
|
|||||||
import androidx.preference.TwoStatePreference;
|
import androidx.preference.TwoStatePreference;
|
||||||
|
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.network.MobileNetworkRepository;
|
import com.android.settings.network.MobileNetworkRepository;
|
||||||
import com.android.settings.wifi.WifiPickerTrackerHelper;
|
import com.android.settings.wifi.WifiPickerTrackerHelper;
|
||||||
import com.android.settingslib.core.lifecycle.Lifecycle;
|
import com.android.settingslib.core.lifecycle.Lifecycle;
|
||||||
@@ -83,6 +84,9 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAvailabilityStatus(int subId) {
|
public int getAvailabilityStatus(int subId) {
|
||||||
|
if (Flags.isDualSimOnboardingEnabled()) {
|
||||||
|
return CONDITIONALLY_UNAVAILABLE;
|
||||||
|
}
|
||||||
return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||||
? AVAILABLE
|
? AVAILABLE
|
||||||
: AVAILABLE_UNSEARCHABLE;
|
: AVAILABLE_UNSEARCHABLE;
|
||||||
|
@@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.telephony.euicc.EuiccManager
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.WindowManager
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.deviceinfo.PhoneNumberUtil
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.android.settingslib.CustomDialogPreferenceCompat
|
||||||
|
import com.android.settingslib.Utils
|
||||||
|
import com.android.settingslib.qrcode.QrCodeGenerator
|
||||||
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
|
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference controller for "EID"
|
||||||
|
*/
|
||||||
|
open class MobileNetworkEidPreferenceController(context: Context, key: String) :
|
||||||
|
TelephonyBasePreferenceController(context, key) {
|
||||||
|
|
||||||
|
private lateinit var lazyViewModel: Lazy<SubscriptionInfoListViewModel>
|
||||||
|
private lateinit var preference: CustomDialogPreferenceCompat
|
||||||
|
private lateinit var fragment: Fragment
|
||||||
|
private var coroutineScope: CoroutineScope? = null
|
||||||
|
private var title = String()
|
||||||
|
private var eid = String()
|
||||||
|
|
||||||
|
fun init(fragment: Fragment, subId: Int) {
|
||||||
|
this.fragment = fragment
|
||||||
|
lazyViewModel = fragment.viewModels()
|
||||||
|
mSubId = subId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(subId: Int): Int = when {
|
||||||
|
!Flags.isDualSimOnboardingEnabled() -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
SubscriptionManager.isValidSubscriptionId(subId)
|
||||||
|
&& eid.isNotEmpty()
|
||||||
|
&& mContext.userManager.isAdminUser -> AVAILABLE
|
||||||
|
|
||||||
|
else -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun displayPreference(screen: PreferenceScreen) {
|
||||||
|
super.displayPreference(screen)
|
||||||
|
preference = screen.findPreference(preferenceKey)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||||
|
preference.isVisible = false
|
||||||
|
|
||||||
|
val viewModel by lazyViewModel
|
||||||
|
coroutineScope = viewLifecycleOwner.lifecycleScope
|
||||||
|
viewModel.subscriptionInfoListFlow
|
||||||
|
.map { subscriptionInfoList ->
|
||||||
|
subscriptionInfoList
|
||||||
|
.firstOrNull { subInfo ->
|
||||||
|
subInfo.subscriptionId == mSubId && subInfo.isEmbedded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.collectLatestWithLifecycle(viewLifecycleOwner) { subscriptionInfo ->
|
||||||
|
subscriptionInfo?.let {
|
||||||
|
coroutineScope?.launch {
|
||||||
|
refreshData(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
suspend fun refreshData(subscriptionInfo: SubscriptionInfo) {
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
eid = getEid(subscriptionInfo)
|
||||||
|
if (eid.isEmpty()) {
|
||||||
|
Log.d(TAG, "EID is empty.")
|
||||||
|
}
|
||||||
|
title = getTitle()
|
||||||
|
}
|
||||||
|
refreshUi()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun refreshUi() {
|
||||||
|
preference.title = title
|
||||||
|
preference.dialogTitle = title
|
||||||
|
preference.summary = eid
|
||||||
|
preference.isVisible = eid.isNotEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
|
||||||
|
if (preference.key != preferenceKey) return false
|
||||||
|
this.preference.setOnShowListener {
|
||||||
|
coroutineScope?.launch { updateDialog() }
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTitle(): String {
|
||||||
|
return mContext.getString(R.string.status_eid)
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun updateDialog() {
|
||||||
|
val dialog = preference.dialog ?: return
|
||||||
|
dialog.window?.setFlags(
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
|
WindowManager.LayoutParams.FLAG_SECURE
|
||||||
|
)
|
||||||
|
dialog.setCanceledOnTouchOutside(false)
|
||||||
|
val textView = dialog.requireViewById<TextView>(R.id.esim_id_value)
|
||||||
|
textView.text = PhoneNumberUtil.expandByTts(eid)
|
||||||
|
|
||||||
|
val qrCodeView = dialog.requireViewById<ImageView>(R.id.esim_id_qrcode)
|
||||||
|
|
||||||
|
qrCodeView.setImageBitmap(getEidQrCode(eid))
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getTelephonyManager(context: Context): TelephonyManager? {
|
||||||
|
return context.getSystemService(TelephonyManager::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun getEuiccManager(context: Context): EuiccManager? {
|
||||||
|
return context.getSystemService(EuiccManager::class.java)
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun getEid(subscriptionInfo: SubscriptionInfo): String {
|
||||||
|
val euiccMgr = getEuiccManager(mContext)
|
||||||
|
val telMgr = getTelephonyManager(mContext)
|
||||||
|
if(euiccMgr==null || telMgr==null) return String()
|
||||||
|
|
||||||
|
var eid = getEidPerSlot(telMgr, euiccMgr, subscriptionInfo)
|
||||||
|
return eid.ifEmpty {
|
||||||
|
getDefaultEid(euiccMgr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getEidPerSlot(
|
||||||
|
telMgr: TelephonyManager,
|
||||||
|
euiccMgr: EuiccManager,
|
||||||
|
subscriptionInfo: SubscriptionInfo
|
||||||
|
): String {
|
||||||
|
val uiccCardInfoList = telMgr.uiccCardsInfo
|
||||||
|
val cardId = subscriptionInfo.cardId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find EID from first slot which contains an eSIM and with card ID within
|
||||||
|
* the eSIM card ID provided by SubscriptionManager.
|
||||||
|
*/
|
||||||
|
return uiccCardInfoList.firstOrNull { cardInfo -> cardInfo.isEuicc && cardInfo.cardId == cardId }
|
||||||
|
?.let { cardInfo ->
|
||||||
|
var eid = cardInfo.getEid()
|
||||||
|
if (TextUtils.isEmpty(eid)) {
|
||||||
|
eid = euiccMgr.createForCardId(cardInfo.cardId).getEid()
|
||||||
|
}
|
||||||
|
eid
|
||||||
|
} ?: String()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getDefaultEid(euiccMgr: EuiccManager?): String {
|
||||||
|
return if (euiccMgr == null || !euiccMgr.isEnabled) {
|
||||||
|
String()
|
||||||
|
} else euiccMgr.getEid() ?: String()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MobileNetworkEidPreferenceController"
|
||||||
|
private const val QR_CODE_SIZE = 600
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the QR code for EID
|
||||||
|
* @param eid is the EID string
|
||||||
|
* @return a Bitmap of QR code
|
||||||
|
*/
|
||||||
|
private suspend fun getEidQrCode(eid: String): Bitmap? = withContext(Dispatchers.Default) {
|
||||||
|
try {
|
||||||
|
Log.d(TAG, "updateDialog. getEidQrCode $eid")
|
||||||
|
QrCodeGenerator.encodeQrCode(contents = eid, size = QR_CODE_SIZE)
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
Log.w(TAG, "Error when creating QR code width $QR_CODE_SIZE", exception)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,145 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.UserManager
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.deviceinfo.imei.ImeiInfoDialogFragment
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.android.settingslib.Utils
|
||||||
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
|
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference controller for "IMEI"
|
||||||
|
*/
|
||||||
|
class MobileNetworkImeiPreferenceController(context: Context, key: String) :
|
||||||
|
TelephonyBasePreferenceController(context, key) {
|
||||||
|
|
||||||
|
private lateinit var lazyViewModel: Lazy<SubscriptionInfoListViewModel>
|
||||||
|
private lateinit var preference: Preference
|
||||||
|
private lateinit var fragment: Fragment
|
||||||
|
private lateinit var mTelephonyManager: TelephonyManager
|
||||||
|
private var simSlot = -1
|
||||||
|
private var imei = String()
|
||||||
|
private var title = String()
|
||||||
|
|
||||||
|
fun init(fragment: Fragment, subId: Int) {
|
||||||
|
this.fragment = fragment
|
||||||
|
lazyViewModel = fragment.viewModels()
|
||||||
|
mSubId = subId
|
||||||
|
mTelephonyManager = mContext.getSystemService(TelephonyManager::class.java)
|
||||||
|
?.createForSubscriptionId(mSubId)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(subId: Int): Int = when {
|
||||||
|
!Flags.isDualSimOnboardingEnabled() -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
SubscriptionManager.isValidSubscriptionId(subId)
|
||||||
|
&& SubscriptionUtil.isSimHardwareVisible(mContext)
|
||||||
|
&& mContext.userManager.isAdminUser
|
||||||
|
&& !Utils.isWifiOnly(mContext) -> AVAILABLE
|
||||||
|
else -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun displayPreference(screen: PreferenceScreen) {
|
||||||
|
super.displayPreference(screen)
|
||||||
|
preference = screen.findPreference(preferenceKey)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||||
|
val viewModel by lazyViewModel
|
||||||
|
val coroutineScope = viewLifecycleOwner.lifecycleScope
|
||||||
|
|
||||||
|
viewModel.subscriptionInfoListFlow
|
||||||
|
.collectLatestWithLifecycle(viewLifecycleOwner) { subscriptionInfoList ->
|
||||||
|
subscriptionInfoList
|
||||||
|
.firstOrNull { subInfo -> subInfo.subscriptionId == mSubId }
|
||||||
|
?.let {
|
||||||
|
coroutineScope.launch {
|
||||||
|
refreshData(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
suspend fun refreshData(subscription:SubscriptionInfo){
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
title = getTitle()
|
||||||
|
imei = getImei()
|
||||||
|
simSlot = subscription.simSlotIndex
|
||||||
|
}
|
||||||
|
refreshUi()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshUi(){
|
||||||
|
preference.title = title
|
||||||
|
preference.summary = imei
|
||||||
|
preference.isVisible = true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun handlePreferenceTreeClick(preference: Preference): Boolean {
|
||||||
|
if (preference.key != preferenceKey) return false
|
||||||
|
|
||||||
|
Log.d(TAG, "handlePreferenceTreeClick:")
|
||||||
|
ImeiInfoDialogFragment.show(fragment, simSlot, preference.title.toString())
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
private fun getImei(): String {
|
||||||
|
val phoneType = getPhoneType()
|
||||||
|
return if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) mTelephonyManager.meid?: String()
|
||||||
|
else mTelephonyManager.imei?: String()
|
||||||
|
}
|
||||||
|
private fun getTitleForGsmPhone(): String {
|
||||||
|
return mContext.getString(R.string.status_imei)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTitleForCdmaPhone(): String {
|
||||||
|
return mContext.getString(R.string.status_meid_number)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTitle(): String {
|
||||||
|
val phoneType = getPhoneType()
|
||||||
|
return if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) getTitleForCdmaPhone()
|
||||||
|
else getTitleForGsmPhone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPhoneType(): Int {
|
||||||
|
return mTelephonyManager.currentPhoneType
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "MobileNetworkImeiPreferenceController"
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,112 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference controller for "Phone number"
|
||||||
|
*/
|
||||||
|
class MobileNetworkPhoneNumberPreferenceController(context: Context, key: String) :
|
||||||
|
TelephonyBasePreferenceController(context, key) {
|
||||||
|
|
||||||
|
private lateinit var lazyViewModel: Lazy<SubscriptionInfoListViewModel>
|
||||||
|
private lateinit var preference: Preference
|
||||||
|
|
||||||
|
private var phoneNumber = String()
|
||||||
|
|
||||||
|
fun init(fragment: Fragment, subId: Int) {
|
||||||
|
lazyViewModel = fragment.viewModels()
|
||||||
|
mSubId = subId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(subId: Int): Int = when {
|
||||||
|
!Flags.isDualSimOnboardingEnabled() -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
SubscriptionManager.isValidSubscriptionId(subId)
|
||||||
|
&& SubscriptionUtil.isSimHardwareVisible(mContext) -> AVAILABLE
|
||||||
|
else -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun displayPreference(screen: PreferenceScreen) {
|
||||||
|
super.displayPreference(screen)
|
||||||
|
preference = screen.findPreference(preferenceKey)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||||
|
val viewModel by lazyViewModel
|
||||||
|
val coroutineScope = viewLifecycleOwner.lifecycleScope
|
||||||
|
|
||||||
|
viewModel.subscriptionInfoListFlow
|
||||||
|
.map { subscriptionInfoList ->
|
||||||
|
subscriptionInfoList
|
||||||
|
.firstOrNull { subInfo -> subInfo.subscriptionId == mSubId }
|
||||||
|
}
|
||||||
|
.flowOn(Dispatchers.Default)
|
||||||
|
.collectLatestWithLifecycle(viewLifecycleOwner) {
|
||||||
|
it?.let {
|
||||||
|
coroutineScope.launch {
|
||||||
|
refreshData(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
suspend fun refreshData(subscriptionInfo: SubscriptionInfo){
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
phoneNumber = getFormattedPhoneNumber(subscriptionInfo)
|
||||||
|
}
|
||||||
|
refreshUi()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshUi(){
|
||||||
|
preference.summary = phoneNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFormattedPhoneNumber(subscriptionInfo: SubscriptionInfo?): String {
|
||||||
|
val phoneNumber = SubscriptionUtil.getBidiFormattedPhoneNumber(
|
||||||
|
mContext,
|
||||||
|
subscriptionInfo
|
||||||
|
)
|
||||||
|
return phoneNumber
|
||||||
|
?.let { return it.ifEmpty { getStringUnknown() } }
|
||||||
|
?: getStringUnknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getStringUnknown(): String {
|
||||||
|
return mContext.getString(R.string.device_info_default)
|
||||||
|
}
|
||||||
|
}
|
@@ -85,6 +85,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
|
|||||||
private static final String KEY_SMS_PREF = "sms_preference";
|
private static final String KEY_SMS_PREF = "sms_preference";
|
||||||
private static final String KEY_MOBILE_DATA_PREF = "mobile_data_enable";
|
private static final String KEY_MOBILE_DATA_PREF = "mobile_data_enable";
|
||||||
private static final String KEY_CONVERT_TO_ESIM_PREF = "convert_to_esim";
|
private static final String KEY_CONVERT_TO_ESIM_PREF = "convert_to_esim";
|
||||||
|
private static final String KEY_EID_KEY = "network_mode_eid_info";
|
||||||
|
|
||||||
//String keys for preference lookup
|
//String keys for preference lookup
|
||||||
private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key";
|
private static final String BUTTON_CDMA_SYSTEM_SELECT_KEY = "cdma_system_select_key";
|
||||||
@@ -171,6 +172,10 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
|
|||||||
String.valueOf(mSubId));
|
String.valueOf(mSubId));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
MobileNetworkEidPreferenceController eid = new MobileNetworkEidPreferenceController(context,
|
||||||
|
KEY_EID_KEY);
|
||||||
|
eid.init(this, mSubId);
|
||||||
|
|
||||||
return Arrays.asList(
|
return Arrays.asList(
|
||||||
new DataUsageSummaryPreferenceController(context, mSubId),
|
new DataUsageSummaryPreferenceController(context, mSubId),
|
||||||
new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),
|
new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(),
|
||||||
@@ -182,7 +187,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
|
|||||||
new MobileDataPreferenceController(context, KEY_MOBILE_DATA_PREF,
|
new MobileDataPreferenceController(context, KEY_MOBILE_DATA_PREF,
|
||||||
getSettingsLifecycle(), this, mSubId),
|
getSettingsLifecycle(), this, mSubId),
|
||||||
new ConvertToEsimPreferenceController(context, KEY_CONVERT_TO_ESIM_PREF,
|
new ConvertToEsimPreferenceController(context, KEY_CONVERT_TO_ESIM_PREF,
|
||||||
getSettingsLifecycle(), this, mSubId));
|
getSettingsLifecycle(), this, mSubId), eid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -239,6 +244,10 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme
|
|||||||
use(DisableSimFooterPreferenceController.class).init(mSubId);
|
use(DisableSimFooterPreferenceController.class).init(mSubId);
|
||||||
use(NrDisabledInDsdsFooterPreferenceController.class).init(mSubId);
|
use(NrDisabledInDsdsFooterPreferenceController.class).init(mSubId);
|
||||||
|
|
||||||
|
use(MobileNetworkSpnPreferenceController.class).init(this, mSubId);
|
||||||
|
use(MobileNetworkPhoneNumberPreferenceController.class).init(this, mSubId);
|
||||||
|
use(MobileNetworkImeiPreferenceController.class).init(this, mSubId);
|
||||||
|
|
||||||
final MobileDataPreferenceController mobileDataPreferenceController =
|
final MobileDataPreferenceController mobileDataPreferenceController =
|
||||||
use(MobileDataPreferenceController.class);
|
use(MobileDataPreferenceController.class);
|
||||||
if (mobileDataPreferenceController != null) {
|
if (mobileDataPreferenceController != null) {
|
||||||
|
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceScreen
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference controller for "Mobile network" and showing the SPN.
|
||||||
|
*/
|
||||||
|
class MobileNetworkSpnPreferenceController(context: Context, key: String) :
|
||||||
|
TelephonyBasePreferenceController(context, key) {
|
||||||
|
|
||||||
|
private lateinit var lazyViewModel: Lazy<SubscriptionInfoListViewModel>
|
||||||
|
private lateinit var preference: Preference
|
||||||
|
|
||||||
|
private var spn = String()
|
||||||
|
|
||||||
|
fun init(fragment: Fragment, subId: Int) {
|
||||||
|
lazyViewModel = fragment.viewModels()
|
||||||
|
mSubId = subId
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getAvailabilityStatus(subId: Int): Int = when {
|
||||||
|
!Flags.isDualSimOnboardingEnabled() -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
SubscriptionManager.isValidSubscriptionId(subId)-> AVAILABLE
|
||||||
|
else -> CONDITIONALLY_UNAVAILABLE
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun displayPreference(screen: PreferenceScreen) {
|
||||||
|
super.displayPreference(screen)
|
||||||
|
preference = screen.findPreference(preferenceKey)!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
|
||||||
|
val viewModel by lazyViewModel
|
||||||
|
|
||||||
|
viewModel.subscriptionInfoListFlow
|
||||||
|
.collectLatestWithLifecycle(viewLifecycleOwner) { subscriptionInfoList ->
|
||||||
|
refreshData(subscriptionInfoList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
fun refreshData(subscriptionInfoList: List<SubscriptionInfo>){
|
||||||
|
spn = subscriptionInfoList
|
||||||
|
.firstOrNull { subInfo -> subInfo.subscriptionId == mSubId }
|
||||||
|
?.let { info -> info.carrierName.toString() }
|
||||||
|
?: String()
|
||||||
|
|
||||||
|
refreshUi()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refreshUi(){
|
||||||
|
preference.summary = spn
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,160 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.platform.test.flag.junit.SetFlagsRule
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.telephony.euicc.EuiccManager
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.dx.mockito.inline.extended.ExtendedMockito
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
import com.android.settings.flags.Flags
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.android.settingslib.CustomDialogPreferenceCompat
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.MockitoSession
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.mockito.quality.Strictness
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MobileNetworkEidPreferenceControllerTest {
|
||||||
|
@get:Rule val setFlagsRule: SetFlagsRule = SetFlagsRule()
|
||||||
|
|
||||||
|
private lateinit var mockSession: MockitoSession
|
||||||
|
|
||||||
|
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
|
||||||
|
private val mockFragment = mock<Fragment>{
|
||||||
|
val viewmodel = mockViewModels
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mockEid = String()
|
||||||
|
private val mockTelephonyManager = mock<TelephonyManager> {
|
||||||
|
on {uiccCardsInfo} doReturn listOf()
|
||||||
|
on { createForSubscriptionId(any()) } doReturn mock
|
||||||
|
}
|
||||||
|
private val mockEuiccManager = mock<EuiccManager> {
|
||||||
|
on {isEnabled} doReturn true
|
||||||
|
on {eid} doReturn mockEid
|
||||||
|
}
|
||||||
|
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
|
||||||
|
on { getSystemService(EuiccManager::class.java) } doReturn mockEuiccManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private val controller = MobileNetworkEidPreferenceController(context, TEST_KEY)
|
||||||
|
private val preference = CustomDialogPreferenceCompat(context).apply { key = TEST_KEY }
|
||||||
|
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mockSession = ExtendedMockito.mockitoSession()
|
||||||
|
.initMocks(this)
|
||||||
|
.mockStatic(SubscriptionUtil::class.java)
|
||||||
|
.strictness(Strictness.LENIENT)
|
||||||
|
.startMocking()
|
||||||
|
|
||||||
|
preferenceScreen.addPreference(preference)
|
||||||
|
controller.displayPreference(preferenceScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockSession.finishMocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun refreshData_getEmptyEid_preferenceIsNotVisible() = runBlocking {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||||
|
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||||
|
listOf(
|
||||||
|
SUB_INFO_1,
|
||||||
|
SUB_INFO_2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var mockSubId = 2
|
||||||
|
controller.init(mockFragment, mockSubId)
|
||||||
|
mockEid = String()
|
||||||
|
|
||||||
|
controller.refreshData(SUB_INFO_2)
|
||||||
|
|
||||||
|
assertThat(preference.isVisible).isEqualTo(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun refreshData_getEmptyEid_preferenceSummaryIsExpected() = runBlocking {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||||
|
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||||
|
listOf(
|
||||||
|
SUB_INFO_1,
|
||||||
|
SUB_INFO_2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var mockSubId = 2
|
||||||
|
controller.init(mockFragment, mockSubId)
|
||||||
|
mockEid = "test eid"
|
||||||
|
mockEuiccManager.stub {
|
||||||
|
on {eid} doReturn mockEid
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.refreshData(SUB_INFO_2)
|
||||||
|
|
||||||
|
assertThat(preference.summary).isEqualTo(mockEid)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_notSimHardwareVisible() {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
|
||||||
|
|
||||||
|
val availabilityStatus = controller.availabilityStatus
|
||||||
|
|
||||||
|
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TEST_KEY = "test_key"
|
||||||
|
const val DISPLAY_NAME_1 = "Sub 1"
|
||||||
|
const val DISPLAY_NAME_2 = "Sub 2"
|
||||||
|
|
||||||
|
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(1)
|
||||||
|
setDisplayName(DISPLAY_NAME_1)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(2)
|
||||||
|
setDisplayName(DISPLAY_NAME_2)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.telephony.euicc.EuiccManager
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.dx.mockito.inline.extended.ExtendedMockito
|
||||||
|
import com.android.internal.telephony.PhoneConstants
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.android.settingslib.CustomDialogPreferenceCompat
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.MockitoSession
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.stub
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.mockito.quality.Strictness
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MobileNetworkImeiPreferenceControllerTest {
|
||||||
|
private lateinit var mockSession: MockitoSession
|
||||||
|
|
||||||
|
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
|
||||||
|
private val mockFragment = mock<Fragment>{
|
||||||
|
val viewmodel = mockViewModels
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mockImei = String()
|
||||||
|
private val mockTelephonyManager = mock<TelephonyManager> {
|
||||||
|
on { uiccCardsInfo } doReturn listOf()
|
||||||
|
on { createForSubscriptionId(any()) } doReturn mock
|
||||||
|
on { currentPhoneType } doReturn TelephonyManager.PHONE_TYPE_GSM
|
||||||
|
on { imei } doReturn mockImei
|
||||||
|
on { meid } doReturn mockImei
|
||||||
|
}
|
||||||
|
|
||||||
|
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager
|
||||||
|
}
|
||||||
|
|
||||||
|
private val controller = MobileNetworkImeiPreferenceController(context, TEST_KEY)
|
||||||
|
private val preference = Preference(context).apply { key = TEST_KEY }
|
||||||
|
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mockSession = ExtendedMockito.mockitoSession()
|
||||||
|
.initMocks(this)
|
||||||
|
.mockStatic(SubscriptionUtil::class.java)
|
||||||
|
.strictness(Strictness.LENIENT)
|
||||||
|
.startMocking()
|
||||||
|
|
||||||
|
preferenceScreen.addPreference(preference)
|
||||||
|
controller.displayPreference(preferenceScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockSession.finishMocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun refreshData_getPhoneNumber_preferenceSummaryIsExpected() = runBlocking {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||||
|
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||||
|
listOf(
|
||||||
|
SUB_INFO_1,
|
||||||
|
SUB_INFO_2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var mockSubId = 2
|
||||||
|
controller.init(mockFragment, mockSubId)
|
||||||
|
mockImei = "test imei"
|
||||||
|
mockTelephonyManager.stub {
|
||||||
|
on { imei } doReturn mockImei
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.refreshData(SUB_INFO_2)
|
||||||
|
|
||||||
|
assertThat(preference.summary).isEqualTo(mockImei)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_notSimHardwareVisible() {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
|
||||||
|
|
||||||
|
val availabilityStatus = controller.availabilityStatus
|
||||||
|
|
||||||
|
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TEST_KEY = "test_key"
|
||||||
|
const val DISPLAY_NAME_1 = "Sub 1"
|
||||||
|
const val DISPLAY_NAME_2 = "Sub 2"
|
||||||
|
|
||||||
|
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(1)
|
||||||
|
setDisplayName(DISPLAY_NAME_1)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(2)
|
||||||
|
setDisplayName(DISPLAY_NAME_2)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.dx.mockito.inline.extended.ExtendedMockito
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.MockitoSession
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.mockito.quality.Strictness
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MobileNetworkPhoneNumberPreferenceControllerTest {
|
||||||
|
private lateinit var mockSession: MockitoSession
|
||||||
|
|
||||||
|
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
|
||||||
|
private val mockFragment = mock<Fragment>{
|
||||||
|
val viewmodel = mockViewModels
|
||||||
|
}
|
||||||
|
|
||||||
|
private var mockPhoneNumber = String()
|
||||||
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
private val controller = MobileNetworkPhoneNumberPreferenceController(context, TEST_KEY)
|
||||||
|
private val preference = Preference(context).apply { key = TEST_KEY }
|
||||||
|
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mockSession = ExtendedMockito.mockitoSession()
|
||||||
|
.initMocks(this)
|
||||||
|
.mockStatic(SubscriptionUtil::class.java)
|
||||||
|
.strictness(Strictness.LENIENT)
|
||||||
|
.startMocking()
|
||||||
|
|
||||||
|
preferenceScreen.addPreference(preference)
|
||||||
|
controller.displayPreference(preferenceScreen)
|
||||||
|
|
||||||
|
whenever(SubscriptionUtil.getBidiFormattedPhoneNumber(any(),any())).thenReturn(mockPhoneNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockSession.finishMocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun refreshData_getEmptyPhoneNumber_preferenceIsNotVisible() = runBlocking {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||||
|
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||||
|
listOf(
|
||||||
|
SUB_INFO_1,
|
||||||
|
SUB_INFO_2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var mockSubId = 2
|
||||||
|
controller.init(mockFragment, mockSubId)
|
||||||
|
mockPhoneNumber = String()
|
||||||
|
|
||||||
|
controller.refreshData(SUB_INFO_2)
|
||||||
|
|
||||||
|
assertThat(preference.summary).isEqualTo(
|
||||||
|
context.getString(R.string.device_info_default))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun refreshData_getPhoneNumber_preferenceSummaryIsExpected() = runBlocking {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(true)
|
||||||
|
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(
|
||||||
|
listOf(
|
||||||
|
SUB_INFO_1,
|
||||||
|
SUB_INFO_2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
var mockSubId = 2
|
||||||
|
controller.init(mockFragment, mockSubId)
|
||||||
|
mockPhoneNumber = "test phone number"
|
||||||
|
whenever(SubscriptionUtil.getBidiFormattedPhoneNumber(any(),any())).thenReturn(mockPhoneNumber)
|
||||||
|
|
||||||
|
controller.refreshData(SUB_INFO_2)
|
||||||
|
|
||||||
|
assertThat(preference.summary).isEqualTo(mockPhoneNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAvailabilityStatus_notSimHardwareVisible() {
|
||||||
|
whenever(SubscriptionUtil.isSimHardwareVisible(context)).thenReturn(false)
|
||||||
|
|
||||||
|
val availabilityStatus = controller.availabilityStatus
|
||||||
|
|
||||||
|
assertThat(availabilityStatus).isEqualTo(BasePreferenceController.CONDITIONALLY_UNAVAILABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TEST_KEY = "test_key"
|
||||||
|
const val DISPLAY_NAME_1 = "Sub 1"
|
||||||
|
const val DISPLAY_NAME_2 = "Sub 2"
|
||||||
|
|
||||||
|
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(1)
|
||||||
|
setDisplayName(DISPLAY_NAME_1)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(2)
|
||||||
|
setDisplayName(DISPLAY_NAME_2)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.settings.network.telephony
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.telephony.SubscriptionInfo
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
|
import android.telephony.euicc.EuiccManager
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.dx.mockito.inline.extended.ExtendedMockito
|
||||||
|
import com.android.settings.core.BasePreferenceController
|
||||||
|
import com.android.settings.network.SubscriptionInfoListViewModel
|
||||||
|
import com.android.settings.network.SubscriptionUtil
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.MockitoSession
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.mockito.quality.Strictness
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class MobileNetworkSpnPreferenceControllerTest {
|
||||||
|
private lateinit var mockSession: MockitoSession
|
||||||
|
|
||||||
|
private val mockViewModels = mock<Lazy<SubscriptionInfoListViewModel>>()
|
||||||
|
private val mockFragment = mock<Fragment>{
|
||||||
|
val viewmodel = mockViewModels
|
||||||
|
}
|
||||||
|
|
||||||
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
private val controller = MobileNetworkSpnPreferenceController(context, TEST_KEY)
|
||||||
|
private val preference = Preference(context).apply { key = TEST_KEY }
|
||||||
|
private val preferenceScreen = PreferenceManager(context).createPreferenceScreen(context)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
mockSession = ExtendedMockito.mockitoSession()
|
||||||
|
.initMocks(this)
|
||||||
|
.mockStatic(SubscriptionUtil::class.java)
|
||||||
|
.strictness(Strictness.LENIENT)
|
||||||
|
.startMocking()
|
||||||
|
|
||||||
|
preferenceScreen.addPreference(preference)
|
||||||
|
controller.displayPreference(preferenceScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
mockSession.finishMocking()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun refreshData_getCarrierName_preferenceSummaryIsExpected() = runBlocking {
|
||||||
|
var testList = listOf(
|
||||||
|
SUB_INFO_1,
|
||||||
|
SUB_INFO_2
|
||||||
|
)
|
||||||
|
whenever(SubscriptionUtil.getActiveSubscriptions(any())).thenReturn(testList)
|
||||||
|
var mockSubId = 2
|
||||||
|
controller.init(mockFragment, mockSubId)
|
||||||
|
|
||||||
|
controller.refreshData(testList)
|
||||||
|
|
||||||
|
assertThat(preference.summary).isEqualTo(CARRIER_NAME_2)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TEST_KEY = "test_key"
|
||||||
|
const val CARRIER_NAME_1 = "Sub 1"
|
||||||
|
const val CARRIER_NAME_2 = "Sub 2"
|
||||||
|
|
||||||
|
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(1)
|
||||||
|
setCarrierName(CARRIER_NAME_1)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
|
||||||
|
setId(2)
|
||||||
|
setCarrierName(CARRIER_NAME_2)
|
||||||
|
}.build()
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -98,6 +98,7 @@ public class MobileDataPreferenceControllerTest {
|
|||||||
doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
|
doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE);
|
||||||
|
|
||||||
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
|
when(mContext.getSystemService(SubscriptionManager.class)).thenReturn(mSubscriptionManager);
|
||||||
|
when(mSubscriptionManager.createForAllUserProfiles()).thenReturn(mSubscriptionManager);
|
||||||
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
|
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID);
|
||||||
doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId(
|
doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId(
|
||||||
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
SubscriptionManager.INVALID_SUBSCRIPTION_ID);
|
||||||
@@ -182,7 +183,8 @@ public class MobileDataPreferenceControllerTest {
|
|||||||
|
|
||||||
mController.onPreferenceChange(mPreference, true);
|
mController.onPreferenceChange(mPreference, true);
|
||||||
|
|
||||||
verify(mTelephonyManager).setDataEnabled(true);
|
verify(mTelephonyManager).setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER
|
||||||
|
,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -195,7 +197,8 @@ public class MobileDataPreferenceControllerTest {
|
|||||||
|
|
||||||
mController.onPreferenceChange(mPreference, true);
|
mController.onPreferenceChange(mPreference, true);
|
||||||
|
|
||||||
verify(mTelephonyManager).setDataEnabled(true);
|
verify(mTelephonyManager).setDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER
|
||||||
|
,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
Reference in New Issue
Block a user