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