• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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() = when {
68         !SubscriptionUtil.isSimHardwareVisible(mContext)
69             || Utils.isWifiOnly(mContext) -> UNSUPPORTED_ON_DEVICE
70         !mContext.userManager.isAdminUser -> DISABLED_FOR_USER
71         else -> AVAILABLE
72     }
73 
displayPreferencenull74     override fun displayPreference(screen: PreferenceScreen) {
75         super.displayPreference(screen)
76         preference = screen.findPreference(preferenceKey)!!
77     }
78 
onViewCreatednull79     override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) {
80         coroutineScope = viewLifecycleOwner.lifecycleScope
81         coroutineScope?.launch {
82             viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
83                 update()
84             }
85         }
86     }
87 
updatenull88     private suspend fun update() {
89         val isAvailable = withContext(Dispatchers.Default) {
90             getIsAvailableAndUpdateEid()
91         }
92         preference.isVisible = isAvailable
93         if (isAvailable) {
94             val title = withContext(Dispatchers.Default) {
95                 getTitle()
96             }
97             preference.title = title
98             preference.dialogTitle = title
99             preference.summary = eid
100             updateDialog()
101         }
102     }
103 
getIsAvailableAndUpdateEidnull104     private fun getIsAvailableAndUpdateEid(): Boolean {
105         eid = eidStatus?.eid ?: ""
106         return eid.isNotEmpty()
107     }
108 
109     /** Constructs title string. */
getTitlenull110     private fun getTitle(): String {
111         val slotSize = slotSimStatus?.size() ?: 0
112         if (slotSize <= 1) {
113             return mContext.getString(R.string.status_eid)
114         }
115         // Only append slot index to title when more than 1 is available
116         for (idxSlot in 0 until slotSize) {
117             val subInfo = slotSimStatus?.getSubscriptionInfo(idxSlot)
118             if (subInfo != null && subInfo.isEmbedded) {
119                 return mContext.getString(R.string.eid_multi_sim, idxSlot + 1)
120             }
121         }
122         return mContext.getString(R.string.status_eid)
123     }
124 
updateDialognull125     private suspend fun updateDialog() {
126         val dialog = preference.dialog ?: return
127         dialog.window?.setFlags(
128             WindowManager.LayoutParams.FLAG_SECURE,
129             WindowManager.LayoutParams.FLAG_SECURE
130         )
131         dialog.setCanceledOnTouchOutside(false)
132         val textView = dialog.requireViewById<TextView>(R.id.esim_id_value)
133         textView.text = PhoneNumberUtil.expandByTts(eid)
134 
135         val qrCodeView = dialog.requireViewById<ImageView>(R.id.esim_id_qrcode)
136         qrCodeView.setImageBitmap(getEidQrCode(eid))
137     }
138 
handlePreferenceTreeClicknull139     override fun handlePreferenceTreeClick(preference: Preference): Boolean {
140         if (preference.key != preferenceKey) return false
141         this.preference.setOnShowListener {
142             coroutineScope?.launch { updateDialog() }
143         }
144         return true
145     }
146 
updateNonIndexableKeysnull147     override fun updateNonIndexableKeys(keys: MutableList<String>) {
148         if (!isAvailable() || !getIsAvailableAndUpdateEid()) {
149             keys += preferenceKey
150         }
151     }
152 
153     companion object {
154         private const val TAG = "SimEidPreferenceController"
155         private const val QR_CODE_SIZE = 600
156 
157         /**
158          * Gets the QR code for EID
159          * @param eid is the EID string
160          * @return a Bitmap of QR code
161          */
<lambda>null162         private suspend fun getEidQrCode(eid: String): Bitmap? = withContext(Dispatchers.Default) {
163             try {
164                 QrCodeGenerator.encodeQrCode(contents = eid, size = QR_CODE_SIZE)
165             } catch (exception: Exception) {
166                 Log.w(TAG, "Error when creating QR code width $QR_CODE_SIZE", exception)
167                 null
168             }
169         }
170     }
171 }
172