Add a higher level warning for antiquated protocols. https://redmine.stoutner.com...
[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 summaryTextView: 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 consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)
79         val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
80
81         // Set the content view.
82         if (bottomAppBar) {
83             setContentView(R.layout.privacy_cell_bottom_appbar)
84         } else {
85             setContentView(R.layout.privacy_cell_top_appbar)
86         }
87
88         // Get handles for the views.
89         drawerLayout = findViewById(R.id.drawerlayout)
90         val toolbar = findViewById<Toolbar>(R.id.toolbar)
91         val stingrayLinearLayout = findViewById<LinearLayout>(R.id.stingray_linearlayout)
92         val stingrayImageView = findViewById<ImageView>(R.id.stingray_imageview)
93         summaryTextView = findViewById(R.id.summary_textview)
94         val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
95         val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
96         val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
97         val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
98         val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
99         val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
100         val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
101         val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
102         val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
103         val navigationView = findViewById<NavigationView>(R.id.navigationview)
104
105         // Set the support action bar.
106         setSupportActionBar(toolbar)
107
108         // Get a handle for the action bar.
109         val actionBar = supportActionBar!!
110
111         // Set a custom view on the action bar.
112         actionBar.setCustomView(R.layout.app_bar_textview)
113
114         // Display the custom view.
115         actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
116
117         // Define a hamburger icon at the start of the app bar.  It will be populated in `onPostCreate()`.
118         actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
119
120         // Listen for touches on the navigation menu.
121         navigationView.setNavigationItemSelectedListener(this)
122
123         // Add a drawer listener.
124         drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
125             override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
126                 // Do nothing.
127             }
128
129             override fun onDrawerOpened(drawerView: View) {
130                 // Do nothing.
131             }
132
133             override fun onDrawerClosed(drawerView: View) {
134                 // Reset the drawer icon when the drawer is closed.  Otherwise, it is an arrow if the drawer is open when the app is restarted.
135                 actionBarDrawerToggle.syncState()
136             }
137
138             override fun onDrawerStateChanged(newState: Int) {
139                 // Do nothing.
140             }
141         })
142
143         // Define the phone state listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
144         phoneStateListener = object : PhoneStateListener() {
145             @SuppressLint("SwitchIntDef")
146             override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
147                 // Declare the stingray dialog type integer.
148                 val summaryDialogTypeInteger: Int
149
150                 // Populate the stingray security information.  <https://source.android.com/devices/tech/connect/acts-5g-testing>
151                 if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_IWLAN) ||
152                     (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN)) {  // This is a secure network.
153                     // Populate the image view.
154                     stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure))
155
156                     // Set the text.
157                     summaryTextView.text = getString(R.string.secure_protocols)
158
159                     // Set the text color.
160                     summaryTextView.setTextColor(getColor(R.color.blue_text))
161
162                     // Set the stingray dialog type integer.
163                     summaryDialogTypeInteger = WebViewDialog.STINGRAY
164                 } else if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_LTE) || (!consider3gAntiquated && (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_1xRTT ||
165                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
166                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_B) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EHRPD) ||
167                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UMTS) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_TD_SCDMA) ||
168                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSDPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSUPA) ||
169                             (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPAP)))) {
170                             // This is an insecure network.
171                     // Populate the image view.
172                     stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.insecure))
173
174                     // Set the text.
175                     summaryTextView.text = getString(R.string.insecure_protocols)
176
177                     // Set the text color.
178                     summaryTextView.setTextColor(getColor(R.color.yellow_900))
179
180                     // Set the stingray dialog type integer.
181                     summaryDialogTypeInteger = WebViewDialog.STINGRAY
182                 } else {  // This is an antiquated network.
183                     // Populate the image view.
184                     stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.antiquated))
185
186                     // Set the text.
187                     summaryTextView.text = getString(R.string.antiquated_protocols)
188
189                     // Set the text color.
190                     summaryTextView.setTextColor(getColor(R.color.red_text))
191
192                     // Set the stingray dialog type integer.
193                     summaryDialogTypeInteger = WebViewDialog.ANTIQUATED_NETWORK
194                 }
195
196                 // Get the strings that correspond to the network information.
197                 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
198                 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
199
200                 // Populate the data network text views.
201                 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
202                 dataNetworkDetailsTextView.text = dataNetworkType[1]
203                 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
204                 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
205
206                 // Set the stingray click listener.
207                 stingrayLinearLayout.setOnClickListener {
208                     // Instantiate the stingray dialog fragment.
209                     val stingrayDialogFragment = WebViewDialog().type(summaryDialogTypeInteger)
210
211                     // Show the alert dialog.
212                     stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
213                 }
214
215                 // Set the data network click listener.
216                 dataNetworkLinearLayout.setOnClickListener {
217                     // Instantiate the data network dialog fragment according to the network type.
218                     val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
219                         TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
220                         TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
221                         TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
222                         TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
223                         TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
224                         TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
225                         TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
226                         TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
227                         TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
228                         TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
229                         TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
230                         TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
231                         TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
232                         TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
233                         TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
234                         TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
235                         TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
236                         TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
237                         TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
238                         TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
239                         else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
240                     }
241
242                     // Show the alert dialog.
243                     dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
244                 }
245
246                 // Set the additional network info click listener.
247                 additionalNetworkInfoLinearLayout.setOnClickListener {
248                     // Instantiate the initial network info dialog fragment according to the network type.
249                     val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
250                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
251                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
252                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
253                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
254                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE)  // Can be removed once the minimum API >= 31.
255                         TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
256                         else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
257                     }
258
259                     // Show the alert dialog.
260                     additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
261                 }
262             }
263
264             override fun onServiceStateChanged(serviceState: ServiceState) {
265                 // 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).
266                 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
267
268                 // Get the voice network type.
269                 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
270
271                 // Populate the voice network text views.
272                 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
273                 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
274
275                 // Set the voice network click listener.
276                 voiceNetworkLinearLayout.setOnClickListener {
277                     // Instantiate the voice network dialog fragment according to the network type.
278                     val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
279                         TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
280                         TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
281                         TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
282                         TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
283                         TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
284                         TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
285                         TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
286                         TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
287                         TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
288                         TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
289                         TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
290                         TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
291                         TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
292                         TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
293                         TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
294                         TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
295                         TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
296                         TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
297                         TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
298                         TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
299                         else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
300                     }
301
302                     // Show the alert dialog.
303                     voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
304                 }
305             }
306         }
307
308         // Start the realtime monitoring service if it is enabled.
309         if (realtimeMonitoring) {
310             // Get a handle for the activity manager.
311             val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
312
313             // 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.
314             val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
315
316             // Start the service if it is not already running.
317             if (runningServiceInfoList.isEmpty()) {
318                 startService(Intent(this, RealtimeMonitoringService::class.java))
319             }
320         }
321     }
322
323     override fun onPostCreate(savedInstanceState: Bundle?) {
324         // Run the default commands.
325         super.onPostCreate(savedInstanceState)
326
327         // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished.  This creates the navigation drawer icon.
328         actionBarDrawerToggle.syncState()
329     }
330
331     override fun onStart() {
332         // Run the default commands.
333         super.onStart()
334
335         // 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.
336         if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {  // The storage permission has been granted.
337             // Register the telephony manager listener.
338             registerTelephonyManagerListener()
339         } else {  // The phone permission has not been granted.
340             // Check if the user has previously denied the storage permission.
341             if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) {  // Show a dialog explaining the request first.
342                 // Check to see if a phone permission dialog is already displayed.  This happens if the app is restarted when the dialog is shown.
343                 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) {  // No dialog is currently shown.
344                     // Instantiate the phone permission dialog fragment.
345                     val phonePermissionDialogFragment = PhonePermissionDialog()
346
347                     // Show the phone permission alert dialog.  The permission will be requested when the dialog is closed.
348                     phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
349                 }
350             } else {  // Show the permission request directly.
351                 // Request the read phone state permission.  There is only one permission request in the app, so it has a request code of 0.
352                 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
353             }
354         }
355     }
356
357     override fun onStop() {
358         // Run the default commands.
359         super.onStop()
360
361         // Get a handle for the telephony manager.
362         val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
363
364         // Unregister the telephony manager listener.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
365         telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
366     }
367
368     override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
369         // Run the commands that correspond to the selected menu item.
370         when (menuItem.itemId) {
371             R.id.settings -> {  // Settings.
372                 // Create an intent to load the Settings activity.
373                 val settingsIntent = Intent(this, SettingsActivity::class.java)
374
375                 // Make it so.
376                 startActivity(settingsIntent)
377             }
378
379             R.id.protocols -> {
380                 // Create an intent to load the Protocols activity.
381                 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
382
383                 // Make it so.
384                 startActivity(protocolsIntent)
385             }
386
387             R.id.logcat -> {  // Logcat.
388                 // Create an intent to load the Logcat activity.
389                 val logcatIntent = Intent(this, LogcatActivity::class.java)
390
391                 // Make it so.
392                 startActivity(logcatIntent)
393             }
394
395             R.id.permissions -> {  // Permissions.
396                 // Instantiate the permissions dialog fragment.
397                 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
398
399                 // Show the alert dialog.
400                 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
401             }
402
403             R.id.privacy_policy -> {  // Privacy Policy.
404                 // Instantiate the privacy policy dialog fragment.
405                 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
406
407                 // Show the alert dialog.
408                 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
409             }
410
411             R.id.changelog -> {  // Changelog.
412                 // Instantiate the changelog dialog fragment.
413                 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
414
415                 // Show the alert dialog.
416                 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
417             }
418
419             R.id.licenses -> {  // Licenses.
420                 // Instantiate the licenses dialog fragment.
421                 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
422
423                 // Show the alert dialog.
424                 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
425             }
426
427             R.id.contributors -> {  // Contributors.
428                 // Instantiate the contributors dialog fragment.
429                 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
430
431                 // Show the alert dialog.
432                 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
433             }
434
435             R.id.news -> {  // News.
436                 // Create a news URL intent.
437                 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
438
439                 // Add the URL to the intent.
440                 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
441
442                 // Make it so.
443                 startActivity(newsUrlIntent)
444             }
445
446             R.id.roadmap -> {  // Roadmap.
447                 // Create a roadmap URL intent.
448                 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
449
450                 // Add the URL to the intent.
451                 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
452
453                 // Make it so.
454                 startActivity(roadmapUrlIntent)
455             }
456
457             R.id.bug_tracker -> {  // Bug tracker.
458                 // Create a bug tracker URL intent.
459                 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
460
461                 // Add the URL to the intent.
462                 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
463
464                 // Make it so.
465                 startActivity(bugTrackerUrlIntent)
466             }
467
468             R.id.forum -> {  // Forum.
469                 // Create a forum URL intent.
470                 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
471
472                 // Add the URL to the intent.
473                 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
474
475                 // Make it so.
476                 startActivity(forumUrlIntent)
477             }
478
479             R.id.donations -> {  // Donations.
480                 // Create a donations URL intent.
481                 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
482
483                 // Add the URL to the intent.
484                 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
485
486                 // Make it so.
487                 startActivity(donationsUrlIntent)
488             }
489         }
490
491         // Close the navigation drawer.
492         drawerLayout.closeDrawer(GravityCompat.START)
493
494         // Consume the click.
495         return true
496     }
497
498     override fun onCloseStoragePermissionDialog() {
499         // Request the read phone state permission.  There is only one permission request in the app, so it has a request code of 0.
500         ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
501     }
502
503     override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
504         // Run the default commands.
505         super.onRequestPermissionsResult(requestCode, permissions, grantResults)
506
507         //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).
508         if (grantResults.isNotEmpty()) {
509             // Check to see if the read phone state permission was granted.  If the dialog was canceled the grant results will be empty.
510             if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {  // The read phone state permission was granted.
511                 // Populate Privacy Cell.
512                 registerTelephonyManagerListener()
513             } else {  // The read phone state permission was denied.
514                 // Display the phone permission text on the main activity.
515                 summaryTextView.text = getString(R.string.phone_permission_text)
516             }
517         }
518     }
519
520     private fun registerTelephonyManagerListener() {
521         // Get a handle for the telephony manager.
522         val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
523
524         // Listen to changes in the cell network state.  The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
525         telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
526     }
527
528     private fun getNetworkType(networkType: Int) : Array<String> {
529         // Return the string that corresponds to the network type.
530         return when(networkType) {
531             TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "")
532             TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detail))
533             TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail))
534             TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail))
535             TelephonyManager.NETWORK_TYPE_CDMA -> arrayOf(getString(R.string.cdma), getString(R.string.cdma_detail))
536             TelephonyManager.NETWORK_TYPE_EVDO_0 -> arrayOf(getString(R.string.evdo_0), getString(R.string.evdo_0_detail))
537             TelephonyManager.NETWORK_TYPE_EVDO_A -> arrayOf(getString(R.string.evdo_a), getString(R.string.evdo_a_detail))
538             TelephonyManager.NETWORK_TYPE_1xRTT -> arrayOf(getString(R.string.rtt), getString(R.string.rtt_detail))
539             TelephonyManager.NETWORK_TYPE_HSDPA -> arrayOf(getString(R.string.hsdpa), getString(R.string.hsdpa_detail))
540             TelephonyManager.NETWORK_TYPE_HSUPA -> arrayOf(getString(R.string.hsupa), getString(R.string.hsupa_detail))
541             TelephonyManager.NETWORK_TYPE_HSPA -> arrayOf(getString(R.string.hspa), getString(R.string.hspa_detail))
542             TelephonyManager.NETWORK_TYPE_IDEN -> arrayOf(getString(R.string.iden), getString(R.string.iden_detail))
543             TelephonyManager.NETWORK_TYPE_EVDO_B -> arrayOf(getString(R.string.evdo_b), getString(R.string.evdo_b_detail))
544             TelephonyManager.NETWORK_TYPE_LTE -> arrayOf(getString(R.string.lte), getString(R.string.lte_detail))
545             TelephonyManager.NETWORK_TYPE_EHRPD -> arrayOf(getString(R.string.ehrpd), getString(R.string.ehrpd_detail))
546             TelephonyManager.NETWORK_TYPE_HSPAP -> arrayOf(getString(R.string.hspap), getString(R.string.hspap_detail))
547             TelephonyManager.NETWORK_TYPE_GSM -> arrayOf(getString(R.string.gsm), getString(R.string.gsm_detail))
548             TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail))
549             TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail))
550             TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail))
551             else -> arrayOf(getString(R.string.error), "")
552         }
553     }
554
555     private fun getAdditionalNetworkInfo(overrideNetworkType: Int) : Array<String> {
556         // Return the string that corresponds to the override network type.
557         return when(overrideNetworkType) {
558             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> arrayOf(getString(R.string.none), "")
559             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
560             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
561             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.nr_nsa), getString(R.string.nr_nsa_detail))
562             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.
563             TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> arrayOf(getString(R.string.nr_advanced), getString(R.string.nr_advanced_detail))
564             else -> arrayOf(getString(R.string.error), "")
565         }
566     }
567 }