Loading res/xml/mobile_network_settings.xml +35 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,23 @@ android:enabled="false" 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 android:key="cdma_lte_data_service_key" android:title="@string/cdma_lte_data_service" Loading Loading @@ -162,6 +179,24 @@ settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController" 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 android:key="calling_category" android:title="@string/call_category" Loading src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java +3 −1 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.preference.TwoStatePreference; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.flags.Flags; import com.android.settings.network.MobileDataContentObserver; import com.android.settings.network.ProxySubscriptionManager; import com.android.settings.network.SubscriptionsChangeListener; Loading Loading @@ -194,7 +195,8 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc @Override public int getAvailabilityStatus(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId) if (Flags.isDualSimOnboardingEnabled() || !SubscriptionManager.isValidSubscriptionId(subId) || SubscriptionManager.getDefaultDataSubscriptionId() == subId || (!hasMobileData())) { return CONDITIONALLY_UNAVAILABLE; Loading src/com/android/settings/network/telephony/DefaultSubscriptionController.java +4 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.flags.Flags; import com.android.settings.network.DefaultSubscriptionReceiver; import com.android.settings.network.MobileNetworkRepository; import com.android.settingslib.core.lifecycle.Lifecycle; Loading Loading @@ -88,6 +89,9 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere @Override public int getAvailabilityStatus(int subId) { if (Flags.isDualSimOnboardingEnabled()) { return CONDITIONALLY_UNAVAILABLE; } return AVAILABLE; } Loading src/com/android/settings/network/telephony/MobileDataPreferenceController.java +4 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.settings.R; import com.android.settings.flags.Flags; import com.android.settings.network.MobileNetworkRepository; import com.android.settings.wifi.WifiPickerTrackerHelper; import com.android.settingslib.core.lifecycle.Lifecycle; Loading Loading @@ -83,6 +84,9 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon @Override public int getAvailabilityStatus(int subId) { if (Flags.isDualSimOnboardingEnabled()) { return CONDITIONALLY_UNAVAILABLE; } return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID ? AVAILABLE : AVAILABLE_UNSEARCHABLE; Loading src/com/android/settings/network/telephony/MobileNetworkEidPreferenceController.kt 0 → 100644 +220 −0 Original line number Diff line number Diff line /* * 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 } } } } Loading
res/xml/mobile_network_settings.xml +35 −0 Original line number Diff line number Diff line Loading @@ -47,6 +47,23 @@ android:enabled="false" 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 android:key="cdma_lte_data_service_key" android:title="@string/cdma_lte_data_service" Loading Loading @@ -162,6 +179,24 @@ settings:controller="com.android.settings.network.telephony.CarrierSettingsVersionPreferenceController" 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 android:key="calling_category" android:title="@string/call_category" Loading
src/com/android/settings/network/telephony/AutoDataSwitchPreferenceController.java +3 −1 Original line number Diff line number Diff line Loading @@ -42,6 +42,7 @@ import androidx.preference.TwoStatePreference; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.datausage.DataUsageUtils; import com.android.settings.flags.Flags; import com.android.settings.network.MobileDataContentObserver; import com.android.settings.network.ProxySubscriptionManager; import com.android.settings.network.SubscriptionsChangeListener; Loading Loading @@ -194,7 +195,8 @@ public class AutoDataSwitchPreferenceController extends TelephonyTogglePreferenc @Override public int getAvailabilityStatus(int subId) { if (!SubscriptionManager.isValidSubscriptionId(subId) if (Flags.isDualSimOnboardingEnabled() || !SubscriptionManager.isValidSubscriptionId(subId) || SubscriptionManager.getDefaultDataSubscriptionId() == subId || (!hasMobileData())) { return CONDITIONALLY_UNAVAILABLE; Loading
src/com/android/settings/network/telephony/DefaultSubscriptionController.java +4 −0 Original line number Diff line number Diff line Loading @@ -32,6 +32,7 @@ import androidx.preference.PreferenceScreen; import com.android.internal.annotations.VisibleForTesting; import com.android.settings.R; import com.android.settings.flags.Flags; import com.android.settings.network.DefaultSubscriptionReceiver; import com.android.settings.network.MobileNetworkRepository; import com.android.settingslib.core.lifecycle.Lifecycle; Loading Loading @@ -88,6 +89,9 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere @Override public int getAvailabilityStatus(int subId) { if (Flags.isDualSimOnboardingEnabled()) { return CONDITIONALLY_UNAVAILABLE; } return AVAILABLE; } Loading
src/com/android/settings/network/telephony/MobileDataPreferenceController.java +4 −0 Original line number Diff line number Diff line Loading @@ -34,6 +34,7 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.settings.R; import com.android.settings.flags.Flags; import com.android.settings.network.MobileNetworkRepository; import com.android.settings.wifi.WifiPickerTrackerHelper; import com.android.settingslib.core.lifecycle.Lifecycle; Loading Loading @@ -83,6 +84,9 @@ public class MobileDataPreferenceController extends TelephonyTogglePreferenceCon @Override public int getAvailabilityStatus(int subId) { if (Flags.isDualSimOnboardingEnabled()) { return CONDITIONALLY_UNAVAILABLE; } return subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID ? AVAILABLE : AVAILABLE_UNSEARCHABLE; Loading
src/com/android/settings/network/telephony/MobileNetworkEidPreferenceController.kt 0 → 100644 +220 −0 Original line number Diff line number Diff line /* * 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 } } } }