Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 85355395 authored by SongFerngWang's avatar SongFerngWang Committed by SongFerng Wang
Browse files

[Sim UI enhancement] MobileNetworkSettings UI enhancement

- Add the phone number
- Add the mobile network(SPN)
- Add the IMEI
- Add the EID
- Remove the mobile data
- Remove the Auto data switch
- Remove the calls and SMS default subscription

Bug: 318310357
Bug: 298898436
Bug: 298891941

Test: atest MobileNetworkEidPreferenceControllerTest
atest MobileNetworkImeiPreferenceControllerTest
atest MobileNetworkPhoneNumberPreferenceControllerTest
atest MobileNetworkSpnPreferenceControllerTest
atest MobileDataPreferenceControllerTest
atest MobileNetworkSettingsTest

Change-Id: Ie2767056dd04d1131390e3a03d6d82d56fe5b2dc
parent c7e0649c
Loading
Loading
Loading
Loading
+35 −0
Original line number Diff line number Diff line
@@ -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"
@@ -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"
+3 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+4 −0
Original line number Diff line number Diff line
@@ -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;
@@ -88,6 +89,9 @@ public abstract class DefaultSubscriptionController extends TelephonyBasePrefere

    @Override
    public int getAvailabilityStatus(int subId) {
        if (Flags.isDualSimOnboardingEnabled()) {
            return CONDITIONALLY_UNAVAILABLE;
        }
        return AVAILABLE;
    }

+4 −0
Original line number Diff line number Diff line
@@ -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;
@@ -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;
+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