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