Bump the target API to 31. https://redmine.stoutner.com/issues/772
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / activities / PrivacyCellActivity.kt
index 618ef569cd052a79ca5cb67ff85cf119675a60d5..55d7c92e4111d2cc3a58af10497b715093ba3955 100644 (file)
@@ -26,7 +26,7 @@ import android.content.Intent
 import android.content.pm.PackageManager
 import android.net.Uri
 import android.os.Bundle
-import android.telephony.PhoneStateListener
+import android.telephony.PhoneStateListener  // This can be replaced by `TelephonyCallback` once the minimum API >= 31.
 import android.telephony.ServiceState
 import android.telephony.TelephonyDisplayInfo
 import android.telephony.TelephonyManager
@@ -56,21 +56,11 @@ import com.stoutner.privacycell.services.RealtimeMonitoringService
 class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, PhonePermissionDialog.StoragePermissionDialogListener {
     // Declare the class variables.
     private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
+    private lateinit var phoneStateListener: PhoneStateListener  // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
 
-    // Declare the views.
+    // Declare the class views.
     private lateinit var drawerLayout: DrawerLayout
-    private lateinit var stingrayLinearLayout: LinearLayout
-    private lateinit var stingrayImageView: ImageView
     private lateinit var stingrayTextView: TextView
-    private lateinit var voiceNetworkLinearLayout: LinearLayout
-    private lateinit var voiceNetworkTextView: TextView
-    private lateinit var voiceNetworkDetailsTextView: TextView
-    private lateinit var dataNetworkLinearLayout: LinearLayout
-    private lateinit var dataNetworkTextView: TextView
-    private lateinit var dataNetworkDetailsTextView: TextView
-    private lateinit var additionalNetworkInfoLinearLayout: LinearLayout
-    private lateinit var additionalNetworkInfoTextView: TextView
-    private lateinit var additionalNetworkInfoDetailsTextView: TextView
 
     override fun onCreate(savedInstanceState: Bundle?) {
         // Run the default commands.
@@ -93,18 +83,18 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
         // Get handles for the views.
         drawerLayout = findViewById(R.id.drawerlayout)
         val toolbar = findViewById<Toolbar>(R.id.toolbar)
-        stingrayLinearLayout = findViewById(R.id.stingray_linearlayout)
-        stingrayImageView = findViewById(R.id.stingray_imageview)
+        val stingrayLinearLayout = findViewById<LinearLayout>(R.id.stingray_linearlayout)
+        val stingrayImageView = findViewById<ImageView>(R.id.stingray_imageview)
         stingrayTextView = findViewById(R.id.stingray_textview)
-        voiceNetworkLinearLayout = findViewById(R.id.voice_network_linearlayout)
-        voiceNetworkTextView = findViewById(R.id.voice_network)
-        voiceNetworkDetailsTextView = findViewById(R.id.voice_network_details)
-        dataNetworkLinearLayout = findViewById(R.id.data_network_linearlayout)
-        dataNetworkTextView = findViewById(R.id.data_network)
-        dataNetworkDetailsTextView = findViewById(R.id.data_network_details)
-        additionalNetworkInfoLinearLayout = findViewById(R.id.additional_network_info_linearlayout)
-        additionalNetworkInfoTextView = findViewById(R.id.additional_network_info)
-        additionalNetworkInfoDetailsTextView = findViewById(R.id.additional_network_info_details)
+        val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
+        val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
+        val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
+        val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
+        val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
+        val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
+        val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
+        val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
+        val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
         val navigationView = findViewById<NavigationView>(R.id.navigationview)
 
         // Set the support action bar.
@@ -145,6 +135,142 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
             }
         })
 
+        // Define the phone state listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
+        phoneStateListener = object : PhoneStateListener() {
+            override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
+                // Populate the stingray security information.  <https://source.android.com/devices/tech/connect/acts-5g-testing>
+                if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) {  // This is a secure 5G NR SA network.
+                    // Populate the image view.
+                    stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure_5g_nr_sa))
+
+                    // Set the text.
+                    stingrayTextView.text = getString(R.string.secure_from_stingray)
+
+                    // Set the text color.
+                    stingrayTextView.setTextColor(getColor(R.color.blue_text))
+                } else {  // This is not a secure 5G NR SA network.
+                    // Populate the image view.
+                    stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.not_secure))
+
+                    // Set the text.
+                    stingrayTextView.text = getString(R.string.not_secure_from_stingray)
+
+                    // Set the text color.
+                    stingrayTextView.setTextColor(getColor(R.color.red_text))
+                }
+
+                // Get the strings that correspond to the network information.
+                val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
+                val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
+
+                // Populate the data network text views.
+                dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
+                dataNetworkDetailsTextView.text = dataNetworkType[1]
+                additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
+                additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
+
+                // Set the stingray click listener.
+                stingrayLinearLayout.setOnClickListener {
+                    // Instantiate the stingray dialog fragment.
+                    val stingrayDialogFragment = WebViewDialog().type(WebViewDialog.STINGRAY)
+
+                    // Show the alert dialog.
+                    stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
+                }
+
+                // Set the data network click listener.
+                dataNetworkLinearLayout.setOnClickListener {
+                    // Instantiate the data network dialog fragment according to the network type.
+                    val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
+                        TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
+                        TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
+                        TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
+                        TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
+                        TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
+                        TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
+                        TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
+                        TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
+                        TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
+                        TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
+                        TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
+                        TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
+                        TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
+                        TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
+                        TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
+                        TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
+                        TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
+                        TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
+                        TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
+                        else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
+                    }
+
+                    // Show the alert dialog.
+                    dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
+                }
+
+                // Set the additional network info click listener.
+                additionalNetworkInfoLinearLayout.setOnClickListener {
+                    // Instantiate the initial network info dialog fragment according to the network type.
+                    val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE)  // Can be removed once the minimum API >= 31.
+                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
+                        else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
+                    }
+
+                    // Show the alert dialog.
+                    additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
+                }
+            }
+
+            override fun onServiceStateChanged(serviceState: ServiceState) {
+                // Get the network registration info for the voice network, which is the second of the three entries (the first appears to be Wi-Fi and the third appears to be the cell data network).
+                val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
+
+                // Get the voice network type.
+                val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
+
+                // Populate the voice network text views.
+                voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
+                voiceNetworkDetailsTextView.text = voiceNetworkType[1]
+
+                // Set the voice network click listener.
+                voiceNetworkLinearLayout.setOnClickListener {
+                    // Instantiate the voice network dialog fragment according to the network type.
+                    val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
+                        TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
+                        TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
+                        TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
+                        TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
+                        TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
+                        TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
+                        TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
+                        TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
+                        TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
+                        TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
+                        TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
+                        TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
+                        TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
+                        TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
+                        TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
+                        TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
+                        TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
+                        TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
+                        TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
+                        TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
+                        else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
+                    }
+
+                    // Show the alert dialog.
+                    voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
+                }
+            }
+        }
+
         // Start the realtime monitoring service if it is enabled.
         if (realtimeMonitoring) {
             // Get a handle for the activity manager.
@@ -168,14 +294,14 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
         actionBarDrawerToggle.syncState()
     }
 
-    override fun onResume() {
+    override fun onStart() {
         // Run the default commands.
-        super.onResume()
+        super.onStart()
 
-        // Check to see if the read phone state permission has been granted.  These commands need to be run on every resume so that the listener gets reassigned as it is automatically paused.
+        // Check to see if the read phone state permission has been granted.  These commands need to be run on every start so that the listener is reregistered.
         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
-            // Populate Privacy Cell.
-            populatePrivacyCell(this)
+            // Register the telephony manager listener.
+            registerTelephonyManagerListener()
         } else {  // The phone permission has not been granted.
             // Check if the user has previously denied the storage permission.
             if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) {  // Show a dialog explaining the request first.
@@ -194,6 +320,17 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
         }
     }
 
+    override fun onStop() {
+        // Run the default commands.
+        super.onStop()
+
+        // Get a handle for the telephony manager.
+        val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
+
+        // Unregister the telephony manager listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
+        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
+    }
+
     override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
         // Run the commands that correspond to the selected menu item.
         when (menuItem.itemId) {
@@ -330,7 +467,7 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
             // Check to see if the read phone state permission was granted.  If the dialog was canceled the grant results will be empty.
             if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The read phone state permission was granted.
                 // Populate Privacy Cell.
-                populatePrivacyCell(this)
+                registerTelephonyManagerListener()
             } else {  // The read phone state permission was denied.
                 // Display the phone permission text on the main activity.
                 stingrayTextView.text = getString(R.string.phone_permission_text)
@@ -338,144 +475,12 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
         }
     }
 
-    private fun populatePrivacyCell(context: Context) {
+    private fun registerTelephonyManagerListener() {
         // Get a handle for the telephony manager.
         val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
 
-        // Listen to changes in the cell network state.
-        telephonyManager.listen(object : PhoneStateListener() {
-            override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
-                // Populate the stingray security information.  <https://source.android.com/devices/tech/connect/acts-5g-testing>
-                if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) {  // This is a secure 5G NR SA network.
-                    // Populate the image view.
-                    stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.secure_5g_nr_sa))
-
-                    // Set the text.
-                    stingrayTextView.text = getString(R.string.secure_from_stingray)
-
-                    // Set the text color.
-                    stingrayTextView.setTextColor(getColor(R.color.blue_text))
-                } else {  // This is not a secure 5G NR SA network.
-                    // Populate the image view.
-                    stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(context, R.drawable.not_secure))
-
-                    // Set the text.
-                    stingrayTextView.text = getString(R.string.not_secure_from_stingray)
-
-                    // Set the text color.
-                    stingrayTextView.setTextColor(getColor(R.color.red_text))
-                }
-
-                // Get the strings that correspond to the network information.
-                val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
-                val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
-
-                // Populate the data network text views.
-                dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
-                dataNetworkDetailsTextView.text = dataNetworkType[1]
-                additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
-                additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
-
-                // Set the stingray click listener.
-                stingrayLinearLayout.setOnClickListener {
-                    // Instantiate the stingray dialog fragment.
-                    val stingrayDialogFragment = WebViewDialog().type(WebViewDialog.STINGRAY)
-
-                    // Show the alert dialog.
-                    stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
-                }
-
-                // Set the data network click listener.
-                dataNetworkLinearLayout.setOnClickListener {
-                    // Instantiate the data network dialog fragment according to the network type.
-                    val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
-                        TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
-                        TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
-                        TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
-                        TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
-                        TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
-                        TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
-                        TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
-                        TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
-                        TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
-                        TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
-                        TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
-                        TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
-                        TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
-                        TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
-                        TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
-                        TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
-                        TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
-                        TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
-                        TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
-                        else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
-                    }
-
-                    // Show the alert dialog.
-                    dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
-                }
-
-                // Set the additional network info click listener.
-                additionalNetworkInfoLinearLayout.setOnClickListener {
-                    // Instantiate the initial network info dialog fragment according to the network type.
-                    val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
-                        TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE)
-                        else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE)
-                    }
-
-                    // Show the alert dialog.
-                    additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
-                }
-            }
-
-            override fun onServiceStateChanged(serviceState: ServiceState) {
-                // Get the network registration info for the voice network, which is the second of the three entries (the first appears to be Wi-Fi and the third appears to be the cell data network).
-                val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
-
-                // Get the voice network type.
-                val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
-
-                // Populate the voice network text views.
-                voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
-                voiceNetworkDetailsTextView.text = voiceNetworkType[1]
-
-                // Set the voice network click listener.
-                voiceNetworkLinearLayout.setOnClickListener {
-                    // Instantiate the voice network dialog fragment according to the network type.
-                    val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
-                        TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
-                        TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
-                        TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
-                        TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
-                        TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
-                        TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
-                        TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
-                        TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
-                        TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
-                        TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
-                        TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
-                        TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
-                        TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
-                        TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
-                        TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
-                        TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
-                        TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
-                        TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
-                        TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
-                        TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
-                        else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
-                    }
-
-                    // Show the alert dialog.
-                    voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
-                }
-            }
-        }, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
+        // Listen to changes in the cell network state.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
+        telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
     }
 
     private fun getNetworkType(networkType: Int) : Array<String> {
@@ -512,7 +517,8 @@ class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItem
             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.nr_nsa), getString(R.string.nr_nsa_detail))
-            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> arrayOf(getString(R.string.nr_nsa_mmwave), getString(R.string.nr_nsa_mmwave_detail))
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> arrayOf(getString(R.string.nr_nsa_mmwave), getString(R.string.nr_nsa_mmwave_detail))  // Can be removed once the minimum API >= 31.
+            TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> arrayOf(getString(R.string.nr_advanced), getString(R.string.nr_advanced_detail))
             else -> arrayOf(getString(R.string.error), "")
         }
     }