1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.settings.deviceinfo.simstatus 17 18 import android.content.Context 19 import android.graphics.Bitmap 20 import android.util.Log 21 import android.view.WindowManager 22 import android.widget.ImageView 23 import android.widget.TextView 24 import androidx.lifecycle.Lifecycle 25 import androidx.lifecycle.LifecycleOwner 26 import androidx.lifecycle.lifecycleScope 27 import androidx.lifecycle.repeatOnLifecycle 28 import androidx.preference.Preference 29 import androidx.preference.PreferenceScreen 30 import com.android.settings.R 31 import com.android.settings.core.BasePreferenceController 32 import com.android.settings.deviceinfo.PhoneNumberUtil 33 import com.android.settings.network.SubscriptionUtil 34 import com.android.settingslib.CustomDialogPreferenceCompat 35 import com.android.settingslib.Utils 36 import com.android.settingslib.qrcode.QrCodeGenerator 37 import com.android.settingslib.spaprivileged.framework.common.userManager 38 import kotlinx.coroutines.CoroutineScope 39 import kotlinx.coroutines.Dispatchers 40 import kotlinx.coroutines.launch 41 import kotlinx.coroutines.withContext 42 43 /** 44 * This is to show a preference regarding EID of SIM card. 45 * 46 * @param preferenceKey is the key for Preference 47 */ 48 class SimEidPreferenceController(context: Context, preferenceKey: String) : 49 BasePreferenceController(context, preferenceKey) { 50 private var slotSimStatus: SlotSimStatus? = null 51 private var eidStatus: EidStatus? = null 52 private lateinit var preference: CustomDialogPreferenceCompat 53 private var coroutineScope: CoroutineScope? = null 54 private lateinit var eid: String 55 initnull56 fun init(slotSimStatus: SlotSimStatus?, eidStatus: EidStatus?) { 57 this.slotSimStatus = slotSimStatus 58 this.eidStatus = eidStatus 59 } 60 61 /** 62 * Returns available here, if SIM hardware is visible. 63 * 64 * Also check [getIsAvailableAndUpdateEid] for other availability check which retrieved 65 * asynchronously later. 66 */ getAvailabilityStatusnull67 override fun getAvailabilityStatus() = 68 if (SubscriptionUtil.isSimHardwareVisible(mContext)) AVAILABLE else UNSUPPORTED_ON_DEVICE 69 70 override fun displayPreference(screen: PreferenceScreen) { 71 super.displayPreference(screen) 72 preference = screen.findPreference(preferenceKey)!! 73 } 74 onViewCreatednull75 override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { 76 coroutineScope = viewLifecycleOwner.lifecycleScope 77 coroutineScope?.launch { 78 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 79 update() 80 } 81 } 82 } 83 updatenull84 private suspend fun update() { 85 val isAvailable = withContext(Dispatchers.Default) { 86 getIsAvailableAndUpdateEid() 87 } 88 preference.isVisible = isAvailable 89 if (isAvailable) { 90 val title = withContext(Dispatchers.Default) { 91 getTitle() 92 } 93 preference.title = title 94 preference.dialogTitle = title 95 preference.summary = eid 96 updateDialog() 97 } 98 } 99 getIsAvailableAndUpdateEidnull100 private fun getIsAvailableAndUpdateEid(): Boolean { 101 if (!mContext.userManager.isAdminUser || Utils.isWifiOnly(mContext)) return false 102 eid = eidStatus?.eid ?: "" 103 return eid.isNotEmpty() 104 } 105 106 /** Constructs title string. */ getTitlenull107 private fun getTitle(): String { 108 val slotSize = slotSimStatus?.size() ?: 0 109 if (slotSize <= 1) { 110 return mContext.getString(R.string.status_eid) 111 } 112 // Only append slot index to title when more than 1 is available 113 for (idxSlot in 0 until slotSize) { 114 val subInfo = slotSimStatus?.getSubscriptionInfo(idxSlot) 115 if (subInfo != null && subInfo.isEmbedded) { 116 return mContext.getString(R.string.eid_multi_sim, idxSlot + 1) 117 } 118 } 119 return mContext.getString(R.string.status_eid) 120 } 121 updateDialognull122 private suspend fun updateDialog() { 123 val dialog = preference.dialog ?: return 124 dialog.window?.setFlags( 125 WindowManager.LayoutParams.FLAG_SECURE, 126 WindowManager.LayoutParams.FLAG_SECURE 127 ) 128 dialog.setCanceledOnTouchOutside(false) 129 val textView = dialog.requireViewById<TextView>(R.id.esim_id_value) 130 textView.text = PhoneNumberUtil.expandByTts(eid) 131 132 val qrCodeView = dialog.requireViewById<ImageView>(R.id.esim_id_qrcode) 133 qrCodeView.setImageBitmap(getEidQrCode(eid)) 134 } 135 handlePreferenceTreeClicknull136 override fun handlePreferenceTreeClick(preference: Preference): Boolean { 137 if (preference.key != preferenceKey) return false 138 this.preference.setOnShowListener { 139 coroutineScope?.launch { updateDialog() } 140 } 141 return true 142 } 143 updateNonIndexableKeysnull144 override fun updateNonIndexableKeys(keys: MutableList<String>) { 145 if (!isAvailable() || !getIsAvailableAndUpdateEid()) { 146 keys += preferenceKey 147 } 148 } 149 150 companion object { 151 private const val TAG = "SimEidPreferenceController" 152 private const val QR_CODE_SIZE = 600 153 154 /** 155 * Gets the QR code for EID 156 * @param eid is the EID string 157 * @return a Bitmap of QR code 158 */ <lambda>null159 private suspend fun getEidQrCode(eid: String): Bitmap? = withContext(Dispatchers.Default) { 160 try { 161 QrCodeGenerator.encodeQrCode(contents = eid, size = QR_CODE_SIZE) 162 } catch (exception: Exception) { 163 Log.w(TAG, "Error when creating QR code width $QR_CODE_SIZE", exception) 164 null 165 } 166 } 167 } 168 } 169