2 * Copyright © 2021-2022 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 // The suppression of deprecation lint can be removed once the minimum API >= 31.
21 @file:Suppress("DEPRECATION")
23 package com.stoutner.privacycell.activities
25 import android.Manifest
26 import android.annotation.SuppressLint
27 import android.app.ActivityManager
28 import android.content.Context
29 import android.content.Intent
30 import android.content.pm.PackageManager
31 import android.net.Uri
32 import android.os.Bundle
33 import android.telephony.PhoneStateListener // This can be replaced by `TelephonyCallback` once the minimum API >= 31.
34 import android.telephony.ServiceState
35 import android.telephony.TelephonyDisplayInfo
36 import android.telephony.TelephonyManager
37 import android.view.MenuItem
38 import android.view.View
39 import android.widget.ImageView
40 import android.widget.LinearLayout
41 import android.widget.TextView
43 import androidx.appcompat.app.ActionBar
44 import androidx.appcompat.app.ActionBarDrawerToggle
45 import androidx.appcompat.app.AppCompatActivity
46 import androidx.appcompat.content.res.AppCompatResources
47 import androidx.appcompat.widget.Toolbar
48 import androidx.core.app.ActivityCompat
49 import androidx.core.view.GravityCompat
50 import androidx.drawerlayout.widget.DrawerLayout
51 import androidx.preference.PreferenceManager
53 import com.google.android.material.navigation.NavigationView
55 import com.stoutner.privacycell.R
56 import com.stoutner.privacycell.dialogs.PhonePermissionDialog
57 import com.stoutner.privacycell.dialogs.WebViewDialog
58 import com.stoutner.privacycell.services.RealtimeMonitoringService
60 class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, PhonePermissionDialog.StoragePermissionDialogListener {
61 // Declare the class variables.
62 private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
63 private lateinit var phoneStateListener: PhoneStateListener // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
65 // Declare the class views.
66 private lateinit var drawerLayout: DrawerLayout
67 private lateinit var stingrayTextView: TextView
69 override fun onCreate(savedInstanceState: Bundle?) {
70 // Run the default commands.
71 super.onCreate(savedInstanceState)
73 // Get a handle for the shared preferences.
74 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
76 // Get the preferences.
77 val realtimeMonitoring = sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)
78 val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
80 // Set the content view.
82 setContentView(R.layout.privacy_cell_bottom_appbar)
84 setContentView(R.layout.privacy_cell_top_appbar)
87 // Get handles for the views.
88 drawerLayout = findViewById(R.id.drawerlayout)
89 val toolbar = findViewById<Toolbar>(R.id.toolbar)
90 val stingrayLinearLayout = findViewById<LinearLayout>(R.id.stingray_linearlayout)
91 val stingrayImageView = findViewById<ImageView>(R.id.stingray_imageview)
92 stingrayTextView = findViewById(R.id.stingray_textview)
93 val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
94 val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
95 val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
96 val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
97 val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
98 val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
99 val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
100 val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
101 val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
102 val navigationView = findViewById<NavigationView>(R.id.navigationview)
104 // Set the support action bar.
105 setSupportActionBar(toolbar)
107 // Get a handle for the action bar.
108 val actionBar = supportActionBar!!
110 // Set a custom view on the action bar.
111 actionBar.setCustomView(R.layout.app_bar_textview)
113 // Display the custom view.
114 actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
116 // Define a hamburger icon at the start of the app bar. It will be populated in `onPostCreate()`.
117 actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
119 // Listen for touches on the navigation menu.
120 navigationView.setNavigationItemSelectedListener(this)
122 // Add a drawer listener.
123 drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
124 override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
128 override fun onDrawerOpened(drawerView: View) {
132 override fun onDrawerClosed(drawerView: View) {
133 // Reset the drawer icon when the drawer is closed. Otherwise, it is an arrow if the drawer is open when the app is restarted.
134 actionBarDrawerToggle.syncState()
137 override fun onDrawerStateChanged(newState: Int) {
142 // Define the phone state listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
143 phoneStateListener = object : PhoneStateListener() {
144 @SuppressLint("SwitchIntDef")
145 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
146 // Populate the stingray security information. <https://source.android.com/devices/tech/connect/acts-5g-testing>
147 if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) { // This is a secure 5G NR SA network.
148 // Populate the image view.
149 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure_5g_nr_sa))
152 stingrayTextView.text = getString(R.string.secure_from_stingray)
154 // Set the text color.
155 stingrayTextView.setTextColor(getColor(R.color.blue_text))
156 } else { // This is not a secure 5G NR SA network.
157 // Populate the image view.
158 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.not_secure))
161 stingrayTextView.text = getString(R.string.not_secure_from_stingray)
163 // Set the text color.
164 stingrayTextView.setTextColor(getColor(R.color.red_text))
167 // Get the strings that correspond to the network information.
168 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
169 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
171 // Populate the data network text views.
172 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
173 dataNetworkDetailsTextView.text = dataNetworkType[1]
174 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
175 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
177 // Set the stingray click listener.
178 stingrayLinearLayout.setOnClickListener {
179 // Instantiate the stingray dialog fragment.
180 val stingrayDialogFragment = WebViewDialog().type(WebViewDialog.STINGRAY)
182 // Show the alert dialog.
183 stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
186 // Set the data network click listener.
187 dataNetworkLinearLayout.setOnClickListener {
188 // Instantiate the data network dialog fragment according to the network type.
189 val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
190 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
191 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
192 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
193 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
194 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
195 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
196 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
197 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
198 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
199 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
200 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
201 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
202 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
203 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
204 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
205 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
206 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
207 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
208 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
209 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
210 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
213 // Show the alert dialog.
214 dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
217 // Set the additional network info click listener.
218 additionalNetworkInfoLinearLayout.setOnClickListener {
219 // Instantiate the initial network info dialog fragment according to the network type.
220 val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
221 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
222 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
223 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
224 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
225 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE) // Can be removed once the minimum API >= 31.
226 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
227 else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
230 // Show the alert dialog.
231 additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
235 override fun onServiceStateChanged(serviceState: ServiceState) {
236 // 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).
237 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
239 // Get the voice network type.
240 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
242 // Populate the voice network text views.
243 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
244 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
246 // Set the voice network click listener.
247 voiceNetworkLinearLayout.setOnClickListener {
248 // Instantiate the voice network dialog fragment according to the network type.
249 val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
250 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
251 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
252 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
253 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
254 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
255 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
256 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
257 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
258 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
259 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
260 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
261 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
262 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
263 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
264 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
265 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
266 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
267 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
268 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
269 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
270 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
273 // Show the alert dialog.
274 voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
279 // Start the realtime monitoring service if it is enabled.
280 if (realtimeMonitoring) {
281 // Get a handle for the activity manager.
282 val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
284 // 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.
285 val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
287 // Start the service if it is not already running.
288 if (runningServiceInfoList.isEmpty()) {
289 startService(Intent(this, RealtimeMonitoringService::class.java))
294 override fun onPostCreate(savedInstanceState: Bundle?) {
295 // Run the default commands.
296 super.onPostCreate(savedInstanceState)
298 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
299 actionBarDrawerToggle.syncState()
302 override fun onStart() {
303 // Run the default commands.
306 // 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.
307 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
308 // Register the telephony manager listener.
309 registerTelephonyManagerListener()
310 } else { // The phone permission has not been granted.
311 // Check if the user has previously denied the storage permission.
312 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first.
313 // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted when the dialog is shown.
314 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown.
315 // Instantiate the phone permission dialog fragment.
316 val phonePermissionDialogFragment = PhonePermissionDialog()
318 // Show the phone permission alert dialog. The permission will be requested when the dialog is closed.
319 phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
321 } else { // Show the permission request directly.
322 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
323 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
328 override fun onStop() {
329 // Run the default commands.
332 // Get a handle for the telephony manager.
333 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
335 // Unregister the telephony manager listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
336 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
339 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
340 // Run the commands that correspond to the selected menu item.
341 when (menuItem.itemId) {
342 R.id.settings -> { // Settings.
343 // Create an intent to load the Settings activity.
344 val settingsIntent = Intent(this, SettingsActivity::class.java)
347 startActivity(settingsIntent)
351 // Create an intent to load the Protocols activity.
352 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
355 startActivity(protocolsIntent)
358 R.id.logcat -> { // Logcat.
359 // Create an intent to load the Logcat activity.
360 val logcatIntent = Intent(this, LogcatActivity::class.java)
363 startActivity(logcatIntent)
366 R.id.permissions -> { // Permissions.
367 // Instantiate the permissions dialog fragment.
368 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
370 // Show the alert dialog.
371 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
374 R.id.privacy_policy -> { // Privacy Policy.
375 // Instantiate the privacy policy dialog fragment.
376 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
378 // Show the alert dialog.
379 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
382 R.id.changelog -> { // Changelog.
383 // Instantiate the changelog dialog fragment.
384 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
386 // Show the alert dialog.
387 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
390 R.id.licenses -> { // Licenses.
391 // Instantiate the licenses dialog fragment.
392 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
394 // Show the alert dialog.
395 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
398 R.id.contributors -> { // Contributors.
399 // Instantiate the contributors dialog fragment.
400 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
402 // Show the alert dialog.
403 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
406 R.id.news -> { // News.
407 // Create a news URL intent.
408 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
410 // Add the URL to the intent.
411 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
414 startActivity(newsUrlIntent)
417 R.id.roadmap -> { // Roadmap.
418 // Create a roadmap URL intent.
419 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
421 // Add the URL to the intent.
422 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
425 startActivity(roadmapUrlIntent)
428 R.id.bug_tracker -> { // Bug tracker.
429 // Create a bug tracker URL intent.
430 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
432 // Add the URL to the intent.
433 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
436 startActivity(bugTrackerUrlIntent)
439 R.id.forum -> { // Forum.
440 // Create a forum URL intent.
441 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
443 // Add the URL to the intent.
444 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
447 startActivity(forumUrlIntent)
450 R.id.donations -> { // Donations.
451 // Create a donations URL intent.
452 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
454 // Add the URL to the intent.
455 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
458 startActivity(donationsUrlIntent)
462 // Close the navigation drawer.
463 drawerLayout.closeDrawer(GravityCompat.START)
465 // Consume the click.
469 override fun onCloseStoragePermissionDialog() {
470 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
471 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
474 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
475 // Run the default commands.
476 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
478 //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).
479 if (grantResults.isNotEmpty()) {
480 // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty.
481 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted.
482 // Populate Privacy Cell.
483 registerTelephonyManagerListener()
484 } else { // The read phone state permission was denied.
485 // Display the phone permission text on the main activity.
486 stingrayTextView.text = getString(R.string.phone_permission_text)
491 private fun registerTelephonyManagerListener() {
492 // Get a handle for the telephony manager.
493 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
495 // Listen to changes in the cell network state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
496 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
499 private fun getNetworkType(networkType: Int) : Array<String> {
500 // Return the string that corresponds to the network type.
501 return when(networkType) {
502 TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "")
503 TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detail))
504 TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail))
505 TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail))
506 TelephonyManager.NETWORK_TYPE_CDMA -> arrayOf(getString(R.string.cdma), getString(R.string.cdma_detail))
507 TelephonyManager.NETWORK_TYPE_EVDO_0 -> arrayOf(getString(R.string.evdo_0), getString(R.string.evdo_0_detail))
508 TelephonyManager.NETWORK_TYPE_EVDO_A -> arrayOf(getString(R.string.evdo_a), getString(R.string.evdo_a_detail))
509 TelephonyManager.NETWORK_TYPE_1xRTT -> arrayOf(getString(R.string.rtt), getString(R.string.rtt_detail))
510 TelephonyManager.NETWORK_TYPE_HSDPA -> arrayOf(getString(R.string.hsdpa), getString(R.string.hsdpa_detail))
511 TelephonyManager.NETWORK_TYPE_HSUPA -> arrayOf(getString(R.string.hsupa), getString(R.string.hsupa_detail))
512 TelephonyManager.NETWORK_TYPE_HSPA -> arrayOf(getString(R.string.hspa), getString(R.string.hspa_detail))
513 TelephonyManager.NETWORK_TYPE_IDEN -> arrayOf(getString(R.string.iden), getString(R.string.iden_detail))
514 TelephonyManager.NETWORK_TYPE_EVDO_B -> arrayOf(getString(R.string.evdo_b), getString(R.string.evdo_b_detail))
515 TelephonyManager.NETWORK_TYPE_LTE -> arrayOf(getString(R.string.lte), getString(R.string.lte_detail))
516 TelephonyManager.NETWORK_TYPE_EHRPD -> arrayOf(getString(R.string.ehrpd), getString(R.string.ehrpd_detail))
517 TelephonyManager.NETWORK_TYPE_HSPAP -> arrayOf(getString(R.string.hspap), getString(R.string.hspap_detail))
518 TelephonyManager.NETWORK_TYPE_GSM -> arrayOf(getString(R.string.gsm), getString(R.string.gsm_detail))
519 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail))
520 TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail))
521 TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail))
522 else -> arrayOf(getString(R.string.error), "")
526 private fun getAdditionalNetworkInfo(overrideNetworkType: Int) : Array<String> {
527 // Return the string that corresponds to the override network type.
528 return when(overrideNetworkType) {
529 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> arrayOf(getString(R.string.none), "")
530 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
531 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
532 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.nr_nsa), getString(R.string.nr_nsa_detail))
533 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.
534 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> arrayOf(getString(R.string.nr_advanced), getString(R.string.nr_advanced_detail))
535 else -> arrayOf(getString(R.string.error), "")