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