2 * Copyright © 2021 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
6 * Privacy Cell is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * Privacy Cell is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Privacy Cell. If not, see <http://www.gnu.org/licenses/>.
20 package com.stoutner.privacycell.activities
22 import android.Manifest
23 import android.app.ActivityManager
24 import android.content.Context
25 import android.content.Intent
26 import android.content.pm.PackageManager
27 import android.net.Uri
28 import android.os.Bundle
29 import android.telephony.PhoneStateListener // This can be replaced by `TelephonyCallback` once the minimum API >= 31.
30 import android.telephony.ServiceState
31 import android.telephony.TelephonyDisplayInfo
32 import android.telephony.TelephonyManager
33 import android.view.MenuItem
34 import android.view.View
35 import android.widget.ImageView
36 import android.widget.LinearLayout
37 import android.widget.TextView
39 import androidx.appcompat.app.ActionBar
40 import androidx.appcompat.app.ActionBarDrawerToggle
41 import androidx.appcompat.app.AppCompatActivity
42 import androidx.appcompat.content.res.AppCompatResources
43 import androidx.appcompat.widget.Toolbar
44 import androidx.core.app.ActivityCompat
45 import androidx.core.view.GravityCompat
46 import androidx.drawerlayout.widget.DrawerLayout
47 import androidx.preference.PreferenceManager
49 import com.google.android.material.navigation.NavigationView
51 import com.stoutner.privacycell.R
52 import com.stoutner.privacycell.dialogs.PhonePermissionDialog
53 import com.stoutner.privacycell.dialogs.WebViewDialog
54 import com.stoutner.privacycell.services.RealtimeMonitoringService
56 class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, PhonePermissionDialog.StoragePermissionDialogListener {
57 // Declare the class variables.
58 private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
59 private lateinit var phoneStateListener: PhoneStateListener // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
61 // Declare the class views.
62 private lateinit var drawerLayout: DrawerLayout
63 private lateinit var stingrayTextView: TextView
65 override fun onCreate(savedInstanceState: Bundle?) {
66 // Run the default commands.
67 super.onCreate(savedInstanceState)
69 // Get a handle for the shared preferences.
70 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
72 // Get the preferences.
73 val realtimeMonitoring = sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)
74 val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
76 // Set the content view.
78 setContentView(R.layout.privacy_cell_bottom_appbar)
80 setContentView(R.layout.privacy_cell_top_appbar)
83 // Get handles for the views.
84 drawerLayout = findViewById(R.id.drawerlayout)
85 val toolbar = findViewById<Toolbar>(R.id.toolbar)
86 val stingrayLinearLayout = findViewById<LinearLayout>(R.id.stingray_linearlayout)
87 val stingrayImageView = findViewById<ImageView>(R.id.stingray_imageview)
88 stingrayTextView = findViewById(R.id.stingray_textview)
89 val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
90 val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
91 val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
92 val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
93 val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
94 val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
95 val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
96 val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
97 val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
98 val navigationView = findViewById<NavigationView>(R.id.navigationview)
100 // Set the support action bar.
101 setSupportActionBar(toolbar)
103 // Get a handle for the action bar.
104 val actionBar = supportActionBar!!
106 // Set a custom view on the action bar.
107 actionBar.setCustomView(R.layout.app_bar_textview)
109 // Display the custom view.
110 actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
112 // Define a hamburger icon at the start of the app bar. It will be populated in `onPostCreate()`.
113 actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
115 // Listen for touches on the navigation menu.
116 navigationView.setNavigationItemSelectedListener(this)
118 // Add a drawer listener.
119 drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
120 override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
124 override fun onDrawerOpened(drawerView: View) {
128 override fun onDrawerClosed(drawerView: View) {
129 // Reset the drawer icon when the drawer is closed. Otherwise, it is an arrow if the drawer is open when the app is restarted.
130 actionBarDrawerToggle.syncState()
133 override fun onDrawerStateChanged(newState: Int) {
138 // Define the phone state listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
139 phoneStateListener = object : PhoneStateListener() {
140 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
141 // Populate the stingray security information. <https://source.android.com/devices/tech/connect/acts-5g-testing>
142 if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) { // This is a secure 5G NR SA network.
143 // Populate the image view.
144 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure_5g_nr_sa))
147 stingrayTextView.text = getString(R.string.secure_from_stingray)
149 // Set the text color.
150 stingrayTextView.setTextColor(getColor(R.color.blue_text))
151 } else { // This is not a secure 5G NR SA network.
152 // Populate the image view.
153 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.not_secure))
156 stingrayTextView.text = getString(R.string.not_secure_from_stingray)
158 // Set the text color.
159 stingrayTextView.setTextColor(getColor(R.color.red_text))
162 // Get the strings that correspond to the network information.
163 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
164 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
166 // Populate the data network text views.
167 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
168 dataNetworkDetailsTextView.text = dataNetworkType[1]
169 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
170 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
172 // Set the stingray click listener.
173 stingrayLinearLayout.setOnClickListener {
174 // Instantiate the stingray dialog fragment.
175 val stingrayDialogFragment = WebViewDialog().type(WebViewDialog.STINGRAY)
177 // Show the alert dialog.
178 stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
181 // Set the data network click listener.
182 dataNetworkLinearLayout.setOnClickListener {
183 // Instantiate the data network dialog fragment according to the network type.
184 val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
185 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
186 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
187 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
188 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
189 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
190 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
191 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
192 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
193 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
194 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
195 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
196 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
197 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
198 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
199 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
200 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
201 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
202 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
203 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
204 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
205 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
208 // Show the alert dialog.
209 dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
212 // Set the additional network info click listener.
213 additionalNetworkInfoLinearLayout.setOnClickListener {
214 // Instantiate the initial network info dialog fragment according to the network type.
215 val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
216 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
217 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
218 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
219 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
220 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE) // Can be removed once the minimum API >= 31.
221 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
222 else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
225 // Show the alert dialog.
226 additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
230 override fun onServiceStateChanged(serviceState: ServiceState) {
231 // 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).
232 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
234 // Get the voice network type.
235 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
237 // Populate the voice network text views.
238 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
239 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
241 // Set the voice network click listener.
242 voiceNetworkLinearLayout.setOnClickListener {
243 // Instantiate the voice network dialog fragment according to the network type.
244 val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
245 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
246 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
247 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
248 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
249 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
250 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
251 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
252 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
253 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
254 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
255 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
256 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
257 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
258 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
259 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
260 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
261 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
262 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
263 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
264 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
265 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
268 // Show the alert dialog.
269 voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
274 // Start the realtime monitoring service if it is enabled.
275 if (realtimeMonitoring) {
276 // Get a handle for the activity manager.
277 val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
279 // Get a list of the running service info. The deprecated `getRunningServices()` now only returns services stared by Privacy Cell, but that is all we want to know anyway.
280 val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
282 // Start the service if it is not already running.
283 if (runningServiceInfoList.isEmpty()) {
284 startService(Intent(this, RealtimeMonitoringService::class.java))
289 override fun onPostCreate(savedInstanceState: Bundle?) {
290 // Run the default commands.
291 super.onPostCreate(savedInstanceState)
293 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
294 actionBarDrawerToggle.syncState()
297 override fun onStart() {
298 // Run the default commands.
301 // 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.
302 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
303 // Register the telephony manager listener.
304 registerTelephonyManagerListener()
305 } else { // The phone permission has not been granted.
306 // Check if the user has previously denied the storage permission.
307 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first.
308 // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted when the dialog is shown.
309 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown.
310 // Instantiate the phone permission dialog fragment.
311 val phonePermissionDialogFragment = PhonePermissionDialog()
313 // Show the phone permission alert dialog. The permission will be requested when the dialog is closed.
314 phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
316 } else { // Show the permission request directly.
317 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
318 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
323 override fun onStop() {
324 // Run the default commands.
327 // Get a handle for the telephony manager.
328 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
330 // Unregister the telephony manager listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
331 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
334 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
335 // Run the commands that correspond to the selected menu item.
336 when (menuItem.itemId) {
337 R.id.settings -> { // Settings.
338 // Create an intent to load the Settings activity.
339 val settingsIntent = Intent(this, SettingsActivity::class.java)
342 startActivity(settingsIntent)
345 R.id.logcat -> { // Logcat.
346 // Create an intent to load the Logcat activity.
347 val logcatIntent = Intent(this, LogcatActivity::class.java)
350 startActivity(logcatIntent)
353 R.id.permissions -> { // Permissions.
354 // Instantiate the permissions dialog fragment.
355 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
357 // Show the alert dialog.
358 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
361 R.id.privacy_policy -> { // Privacy Policy.
362 // Instantiate the privacy policy dialog fragment.
363 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
365 // Show the alert dialog.
366 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
369 R.id.changelog -> { // Changelog.
370 // Instantiate the changelog dialog fragment.
371 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
373 // Show the alert dialog.
374 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
377 R.id.licenses -> { // Licenses.
378 // Instantiate the licenses dialog fragment.
379 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
381 // Show the alert dialog.
382 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
385 R.id.contributors -> { // Contributors.
386 // Instantiate the contributors dialog fragment.
387 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
389 // Show the alert dialog.
390 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
393 R.id.news -> { // News.
394 // Create a news URL intent.
395 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
397 // Add the URL to the intent.
398 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
401 startActivity(newsUrlIntent)
404 R.id.roadmap -> { // Roadmap.
405 // Create a roadmap URL intent.
406 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
408 // Add the URL to the intent.
409 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
412 startActivity(roadmapUrlIntent)
415 R.id.bug_tracker -> { // Bug tracker.
416 // Create a bug tracker URL intent.
417 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
419 // Add the URL to the intent.
420 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
423 startActivity(bugTrackerUrlIntent)
426 R.id.forum -> { // Forum.
427 // Create a forum URL intent.
428 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
430 // Add the URL to the intent.
431 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
434 startActivity(forumUrlIntent)
437 R.id.donations -> { // Donations.
438 // Create a donations URL intent.
439 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
441 // Add the URL to the intent.
442 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
445 startActivity(donationsUrlIntent)
449 // Close the navigation drawer.
450 drawerLayout.closeDrawer(GravityCompat.START)
452 // Consume the click.
456 override fun onCloseStoragePermissionDialog() {
457 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
458 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
461 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
462 // Run the default commands.
463 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
465 //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
466 if (grantResults.isNotEmpty()) {
467 // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty.
468 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted.
469 // Populate Privacy Cell.
470 registerTelephonyManagerListener()
471 } else { // The read phone state permission was denied.
472 // Display the phone permission text on the main activity.
473 stingrayTextView.text = getString(R.string.phone_permission_text)
478 private fun registerTelephonyManagerListener() {
479 // Get a handle for the telephony manager.
480 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
482 // Listen to changes in the cell network state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
483 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
486 private fun getNetworkType(networkType: Int) : Array<String> {
487 // Return the string that corresponds to the network type.
488 return when(networkType) {
489 TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "")
490 TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detail))
491 TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail))
492 TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail))
493 TelephonyManager.NETWORK_TYPE_CDMA -> arrayOf(getString(R.string.cdma), getString(R.string.cdma_detail))
494 TelephonyManager.NETWORK_TYPE_EVDO_0 -> arrayOf(getString(R.string.evdo_0), getString(R.string.evdo_0_detail))
495 TelephonyManager.NETWORK_TYPE_EVDO_A -> arrayOf(getString(R.string.evdo_a), getString(R.string.evdo_a_detail))
496 TelephonyManager.NETWORK_TYPE_1xRTT -> arrayOf(getString(R.string.rtt), getString(R.string.rtt_detail))
497 TelephonyManager.NETWORK_TYPE_HSDPA -> arrayOf(getString(R.string.hsdpa), getString(R.string.hsdpa_detail))
498 TelephonyManager.NETWORK_TYPE_HSUPA -> arrayOf(getString(R.string.hsupa), getString(R.string.hsupa_detail))
499 TelephonyManager.NETWORK_TYPE_HSPA -> arrayOf(getString(R.string.hspa), getString(R.string.hspa_detail))
500 TelephonyManager.NETWORK_TYPE_IDEN -> arrayOf(getString(R.string.iden), getString(R.string.iden_detail))
501 TelephonyManager.NETWORK_TYPE_EVDO_B -> arrayOf(getString(R.string.evdo_b), getString(R.string.evdo_b_detail))
502 TelephonyManager.NETWORK_TYPE_LTE -> arrayOf(getString(R.string.lte), getString(R.string.lte_detail))
503 TelephonyManager.NETWORK_TYPE_EHRPD -> arrayOf(getString(R.string.ehrpd), getString(R.string.ehrpd_detail))
504 TelephonyManager.NETWORK_TYPE_HSPAP -> arrayOf(getString(R.string.hspap), getString(R.string.hspap_detail))
505 TelephonyManager.NETWORK_TYPE_GSM -> arrayOf(getString(R.string.gsm), getString(R.string.gsm_detail))
506 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail))
507 TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail))
508 TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail))
509 else -> arrayOf(getString(R.string.error), "")
513 private fun getAdditionalNetworkInfo(overrideNetworkType: Int) : Array<String> {
514 // Return the string that corresponds to the override network type.
515 return when(overrideNetworkType) {
516 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> arrayOf(getString(R.string.none), "")
517 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
518 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
519 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.nr_nsa), getString(R.string.nr_nsa_detail))
520 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.
521 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> arrayOf(getString(R.string.nr_advanced), getString(R.string.nr_advanced_detail))
522 else -> arrayOf(getString(R.string.error), "")