7d6fd776b7d36726bb069521309de934bcbf1df5
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / activities / PrivacyCellActivity.kt
1 /*
2  * Copyright © 2021-2022 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 // The suppression of deprecation lint can be removed once the minimum API >= 31.
21 @file:Suppress("DEPRECATION")
22
23 package com.stoutner.privacycell.activities
24
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
42
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
52
53 import com.google.android.material.navigation.NavigationView
54
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
59
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.
64
65     // Declare the class views.
66     private lateinit var drawerLayout: DrawerLayout
67     private lateinit var stingrayTextView: TextView
68
69     override fun onCreate(savedInstanceState: Bundle?) {
70         // Run the default commands.
71         super.onCreate(savedInstanceState)
72
73         // Get a handle for the shared preferences.
74         val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
75
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)
79
80         // Set the content view.
81         if (bottomAppBar) {
82             setContentView(R.layout.privacy_cell_bottom_appbar)
83         } else {
84             setContentView(R.layout.privacy_cell_top_appbar)
85         }
86
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)
103
104         // Set the support action bar.
105         setSupportActionBar(toolbar)
106
107         // Get a handle for the action bar.
108         val actionBar = supportActionBar!!
109
110         // Set a custom view on the action bar.
111         actionBar.setCustomView(R.layout.app_bar_textview)
112
113         // Display the custom view.
114         actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
115
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)
118
119         // Listen for touches on the navigation menu.
120         navigationView.setNavigationItemSelectedListener(this)
121
122         // Add a drawer listener.
123         drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
124             override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
125                 // Do nothing.
126             }
127
128             override fun onDrawerOpened(drawerView: View) {
129                 // Do nothing.
130             }
131
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()
135             }
136
137             override fun onDrawerStateChanged(newState: Int) {
138                 // Do nothing.
139             }
140         })
141
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))
150
151                     // Set the text.
152                     stingrayTextView.text = getString(R.string.secure_from_stingray)
153
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))
159
160                     // Set the text.
161                     stingrayTextView.text = getString(R.string.not_secure_from_stingray)
162
163                     // Set the text color.
164                     stingrayTextView.setTextColor(getColor(R.color.red_text))
165                 }
166
167                 // Get the strings that correspond to the network information.
168                 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
169                 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
170
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]
176
177                 // Set the stingray click listener.
178                 stingrayLinearLayout.setOnClickListener {
179                     // Instantiate the stingray dialog fragment.
180                     val stingrayDialogFragment = WebViewDialog().type(WebViewDialog.STINGRAY)
181
182                     // Show the alert dialog.
183                     stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
184                 }
185
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)
211                     }
212
213                     // Show the alert dialog.
214                     dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
215                 }
216
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)
228                     }
229
230                     // Show the alert dialog.
231                     additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
232                 }
233             }
234
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]
238
239                 // Get the voice network type.
240                 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
241
242                 // Populate the voice network text views.
243                 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
244                 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
245
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)
271                     }
272
273                     // Show the alert dialog.
274                     voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
275                 }
276             }
277         }
278
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
283
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)
286
287             // Start the service if it is not already running.
288             if (runningServiceInfoList.isEmpty()) {
289                 startService(Intent(this, RealtimeMonitoringService::class.java))
290             }
291         }
292     }
293
294     override fun onPostCreate(savedInstanceState: Bundle?) {
295         // Run the default commands.
296         super.onPostCreate(savedInstanceState)
297
298         // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished.  This creates the navigation drawer icon.
299         actionBarDrawerToggle.syncState()
300     }
301
302     override fun onStart() {
303         // Run the default commands.
304         super.onStart()
305
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()
317
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))
320                 }
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)
324             }
325         }
326     }
327
328     override fun onStop() {
329         // Run the default commands.
330         super.onStop()
331
332         // Get a handle for the telephony manager.
333         val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
334
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)
337     }
338
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)
345
346                 // Make it so.
347                 startActivity(settingsIntent)
348             }
349
350             R.id.protocols -> {
351                 // Create an intent to load the Protocols activity.
352                 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
353
354                 // Make it so.
355                 startActivity(protocolsIntent)
356             }
357
358             R.id.logcat -> {  // Logcat.
359                 // Create an intent to load the Logcat activity.
360                 val logcatIntent = Intent(this, LogcatActivity::class.java)
361
362                 // Make it so.
363                 startActivity(logcatIntent)
364             }
365
366             R.id.permissions -> {  // Permissions.
367                 // Instantiate the permissions dialog fragment.
368                 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
369
370                 // Show the alert dialog.
371                 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
372             }
373
374             R.id.privacy_policy -> {  // Privacy Policy.
375                 // Instantiate the privacy policy dialog fragment.
376                 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
377
378                 // Show the alert dialog.
379                 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
380             }
381
382             R.id.changelog -> {  // Changelog.
383                 // Instantiate the changelog dialog fragment.
384                 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
385
386                 // Show the alert dialog.
387                 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
388             }
389
390             R.id.licenses -> {  // Licenses.
391                 // Instantiate the licenses dialog fragment.
392                 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
393
394                 // Show the alert dialog.
395                 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
396             }
397
398             R.id.contributors -> {  // Contributors.
399                 // Instantiate the contributors dialog fragment.
400                 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
401
402                 // Show the alert dialog.
403                 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
404             }
405
406             R.id.news -> {  // News.
407                 // Create a news URL intent.
408                 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
409
410                 // Add the URL to the intent.
411                 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
412
413                 // Make it so.
414                 startActivity(newsUrlIntent)
415             }
416
417             R.id.roadmap -> {  // Roadmap.
418                 // Create a roadmap URL intent.
419                 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
420
421                 // Add the URL to the intent.
422                 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
423
424                 // Make it so.
425                 startActivity(roadmapUrlIntent)
426             }
427
428             R.id.bug_tracker -> {  // Bug tracker.
429                 // Create a bug tracker URL intent.
430                 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
431
432                 // Add the URL to the intent.
433                 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
434
435                 // Make it so.
436                 startActivity(bugTrackerUrlIntent)
437             }
438
439             R.id.forum -> {  // Forum.
440                 // Create a forum URL intent.
441                 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
442
443                 // Add the URL to the intent.
444                 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
445
446                 // Make it so.
447                 startActivity(forumUrlIntent)
448             }
449
450             R.id.donations -> {  // Donations.
451                 // Create a donations URL intent.
452                 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
453
454                 // Add the URL to the intent.
455                 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
456
457                 // Make it so.
458                 startActivity(donationsUrlIntent)
459             }
460         }
461
462         // Close the navigation drawer.
463         drawerLayout.closeDrawer(GravityCompat.START)
464
465         // Consume the click.
466         return true
467     }
468
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)
472     }
473
474     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
475         // Run the default commands.
476         super.onRequestPermissionsResult(requestCode, permissions, grantResults)
477
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)
487             }
488         }
489     }
490
491     private fun registerTelephonyManagerListener() {
492         // Get a handle for the telephony manager.
493         val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
494
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)
497     }
498
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), "")
523         }
524     }
525
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), "")
536         }
537     }
538 }