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