2 * Copyright © 2021-2022 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
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.
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.
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/>.
20 // The suppression of deprecation lint can be removed once the minimum API >= 31.
21 @file:Suppress("DEPRECATION")
23 package com.stoutner.privacycell.activities
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
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
53 import com.google.android.material.navigation.NavigationView
55 import com.stoutner.privacycell.R
56 import com.stoutner.privacycell.dialogs.PhonePermissionDialog
57 import com.stoutner.privacycell.dialogs.WebViewDialog
58 import com.stoutner.privacycell.helpers.ProtocolHelper
59 import com.stoutner.privacycell.services.RealtimeMonitoringService
61 class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, PhonePermissionDialog.StoragePermissionDialogListener {
62 // Define the class variables.
63 private var voiceNetworkSecurityStatus = ProtocolHelper.UNPOPULATED
64 private var dataNetworkSecurityStatus = ProtocolHelper.UNPOPULATED
66 // Declare the class variables.
67 private lateinit var phoneStateListener: PhoneStateListener // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
69 // Declare the class views.
70 private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
71 private lateinit var drawerLayout: DrawerLayout
72 private lateinit var overallStatusLinearLayout: LinearLayout
73 private lateinit var overallStatusImageView: ImageView
74 private lateinit var overallStatusTextView: TextView
76 override fun onCreate(savedInstanceState: Bundle?) {
77 // Run the default commands.
78 super.onCreate(savedInstanceState)
80 // Get a handle for the shared preferences.
81 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
83 // Get the preferences.
84 val realtimeMonitoring = sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)
85 val consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)
86 val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
88 // Set the content view.
90 setContentView(R.layout.privacy_cell_bottom_appbar)
92 setContentView(R.layout.privacy_cell_top_appbar)
95 // Get handles for the views.
96 drawerLayout = findViewById(R.id.drawerlayout)
97 val toolbar = findViewById<Toolbar>(R.id.toolbar)
98 overallStatusLinearLayout = findViewById(R.id.overall_status_linearlayout)
99 overallStatusImageView = findViewById(R.id.overall_status_imageview)
100 overallStatusTextView = findViewById(R.id.overall_status_textview)
101 val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
102 val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
103 val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
104 val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
105 val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
106 val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
107 val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
108 val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
109 val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
110 val navigationView = findViewById<NavigationView>(R.id.navigationview)
112 // Set the support action bar.
113 setSupportActionBar(toolbar)
115 // Get a handle for the action bar.
116 val actionBar = supportActionBar!!
118 // Set a custom view on the action bar.
119 actionBar.setCustomView(R.layout.app_bar_textview)
121 // Display the custom view.
122 actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
124 // Define a hamburger icon at the start of the app bar. It will be populated in `onPostCreate()`.
125 actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
127 // Listen for touches on the navigation menu.
128 navigationView.setNavigationItemSelectedListener(this)
130 // Add a drawer listener.
131 drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
132 override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
136 override fun onDrawerOpened(drawerView: View) {
140 override fun onDrawerClosed(drawerView: View) {
141 // Reset the drawer icon when the drawer is closed. Otherwise, it is an arrow if the drawer is open when the app is restarted.
142 actionBarDrawerToggle.syncState()
145 override fun onDrawerStateChanged(newState: Int) {
150 // Instantiate the protocol helper.
151 val protocolHelper = ProtocolHelper()
153 // Define the phone state listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
154 phoneStateListener = object : PhoneStateListener() {
155 @Deprecated("Deprecated in Java")
156 override fun onServiceStateChanged(serviceState: ServiceState) { // Update the voice network.
157 // Get the network registration info for the voice network, which is the second of the three entries (the first appears to be Wi-Fi and the third appears to be the cell data network).
158 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
160 // Get the voice network type int.
161 val voiceNetworkTypeInt = networkRegistrationInfo.accessNetworkTechnology
163 // Get the voice network security status.
164 voiceNetworkSecurityStatus = protocolHelper.checkNetwork(voiceNetworkTypeInt, consider3gAntiquated)
166 // Get the voice network type.
167 val voiceNetworkStringArray = protocolHelper.getNetworkTypeStringArray(voiceNetworkTypeInt, applicationContext)
169 // Populate the voice network text views.
170 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkStringArray[0])
171 voiceNetworkDetailsTextView.text = voiceNetworkStringArray[1]
173 // Set the color of the voice network.
174 when (voiceNetworkSecurityStatus) {
175 ProtocolHelper.SECURE -> voiceNetworkTextView.setTextColor(getColor(R.color.blue_text))
176 ProtocolHelper.INSECURE -> voiceNetworkTextView.setTextColor(getColor(R.color.yellow_text))
177 ProtocolHelper.ANTIQUATED -> voiceNetworkTextView.setTextColor(getColor(R.color.red_text))
180 // Set the voice network click listener.
181 voiceNetworkLinearLayout.setOnClickListener {
182 // Instantiate the voice network dialog fragment according to the network type.
183 val voiceNetworkDialogFragment = when (voiceNetworkTypeInt) {
184 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
185 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
186 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
187 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
188 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
189 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
190 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
191 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
192 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
193 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
194 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
195 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
196 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
197 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
198 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
199 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
200 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
201 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
202 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
203 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
204 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
207 // Show the alert dialog.
208 voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
211 // Populate the overall security status.
212 populateOverallSecurityStatus()
215 @Deprecated("Deprecated in Java")
216 @SuppressLint("SwitchIntDef")
217 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) { // Update the data network.
218 // Get the network type integers. <https://source.android.com/devices/tech/connect/acts-5g-testing>
219 val dataNetworkTypeInt = telephonyDisplayInfo.networkType
220 val additionalNetworkInfoTypeInt = telephonyDisplayInfo.overrideNetworkType
222 // Get the data network security status.
223 dataNetworkSecurityStatus = protocolHelper.checkNetwork(dataNetworkTypeInt, consider3gAntiquated)
225 // Get the strings that correspond to the network information.
226 val dataNetworkStringArray = protocolHelper.getNetworkTypeStringArray(dataNetworkTypeInt, applicationContext)
227 val additionalNetworkInfoStringArray = protocolHelper.getAdditionalNetworkInfoStringArray(additionalNetworkInfoTypeInt, applicationContext)
229 // Populate the data network text views.
230 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkStringArray[0])
231 dataNetworkDetailsTextView.text = dataNetworkStringArray[1]
232 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfoStringArray[0])
233 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfoStringArray[1]
235 // Set the color of the data network.
236 when (dataNetworkSecurityStatus) {
237 ProtocolHelper.SECURE -> dataNetworkTextView.setTextColor(getColor(R.color.blue_text))
238 ProtocolHelper.INSECURE -> dataNetworkTextView.setTextColor(getColor(R.color.yellow_text))
239 ProtocolHelper.ANTIQUATED -> dataNetworkTextView.setTextColor(getColor(R.color.red_text))
242 // Set the color of the additional network info.
243 when (protocolHelper.checkAdditionalNetworkInfo(additionalNetworkInfoTypeInt)) {
244 ProtocolHelper.SECURE -> additionalNetworkInfoTextView.setTextColor(getColor(R.color.blue_text))
245 ProtocolHelper.INSECURE -> additionalNetworkInfoTextView.setTextColor(getColor(R.color.yellow_text))
246 ProtocolHelper.ANTIQUATED -> additionalNetworkInfoTextView.setTextColor(getColor(R.color.red_text))
249 // Set the data network click listener.
250 dataNetworkLinearLayout.setOnClickListener {
251 // Instantiate the data network dialog fragment according to the network type.
252 val dataNetworkDialogFragment = when (dataNetworkTypeInt) {
253 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
254 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
255 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
256 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
257 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
258 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
259 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
260 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
261 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
262 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
263 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
264 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
265 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
266 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
267 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
268 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
269 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
270 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
271 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
272 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
273 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
276 // Show the alert dialog.
277 dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
280 // Set the additional network info click listener.
281 additionalNetworkInfoLinearLayout.setOnClickListener {
282 // Instantiate the initial network info dialog fragment according to the network type.
283 val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
284 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
285 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
286 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
287 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
288 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE) // Can be removed once the minimum API >= 31.
289 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
290 else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
293 // Show the alert dialog.
294 additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
297 // Populate the overall security status.
298 populateOverallSecurityStatus()
302 // Start the realtime monitoring service if it is enabled.
303 if (realtimeMonitoring) {
304 // Get a handle for the activity manager.
305 val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
307 // Get a list of the running service info. The deprecated `getRunningServices()` now only returns services stared by Privacy Cell, but that is all we want to know anyway.
308 val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
310 // Start the service if it is not already running.
311 if (runningServiceInfoList.isEmpty()) {
312 startService(Intent(this, RealtimeMonitoringService::class.java))
317 override fun onPostCreate(savedInstanceState: Bundle?) {
318 // Run the default commands.
319 super.onPostCreate(savedInstanceState)
321 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
322 actionBarDrawerToggle.syncState()
325 override fun onStart() {
326 // Run the default commands.
329 // Check to see if the read phone state permission has been granted. These commands need to be run on every start so that the listener is reregistered.
330 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
331 // Register the telephony manager listener.
332 registerTelephonyManagerListener()
333 } else { // The phone permission has not been granted.
334 // Check if the user has previously denied the storage permission.
335 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first.
336 // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted when the dialog is shown.
337 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown.
338 // Instantiate the phone permission dialog fragment.
339 val phonePermissionDialogFragment = PhonePermissionDialog()
341 // Show the phone permission alert dialog. The permission will be requested when the dialog is closed.
342 phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
344 } else { // Show the permission request directly.
345 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
346 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
351 override fun onStop() {
352 // Run the default commands.
355 // Get a handle for the telephony manager.
356 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
358 // Unregister the telephony manager listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
359 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
362 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
363 // Run the commands that correspond to the selected menu item.
364 when (menuItem.itemId) {
365 R.id.settings -> { // Settings.
366 // Create an intent to load the Settings activity.
367 val settingsIntent = Intent(this, SettingsActivity::class.java)
370 startActivity(settingsIntent)
374 // Create an intent to load the Protocols activity.
375 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
378 startActivity(protocolsIntent)
381 R.id.logcat -> { // Logcat.
382 // Create an intent to load the Logcat activity.
383 val logcatIntent = Intent(this, LogcatActivity::class.java)
386 startActivity(logcatIntent)
389 R.id.permissions -> { // Permissions.
390 // Instantiate the permissions dialog fragment.
391 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
393 // Show the alert dialog.
394 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
397 R.id.privacy_policy -> { // Privacy Policy.
398 // Instantiate the privacy policy dialog fragment.
399 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
401 // Show the alert dialog.
402 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
405 R.id.changelog -> { // Changelog.
406 // Instantiate the changelog dialog fragment.
407 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
409 // Show the alert dialog.
410 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
413 R.id.licenses -> { // Licenses.
414 // Instantiate the licenses dialog fragment.
415 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
417 // Show the alert dialog.
418 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
421 R.id.contributors -> { // Contributors.
422 // Instantiate the contributors dialog fragment.
423 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
425 // Show the alert dialog.
426 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
429 R.id.news -> { // News.
430 // Create a news URL intent.
431 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
433 // Add the URL to the intent.
434 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
437 startActivity(newsUrlIntent)
440 R.id.roadmap -> { // Roadmap.
441 // Create a roadmap URL intent.
442 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
444 // Add the URL to the intent.
445 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
448 startActivity(roadmapUrlIntent)
451 R.id.bug_tracker -> { // Bug tracker.
452 // Create a bug tracker URL intent.
453 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
455 // Add the URL to the intent.
456 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
459 startActivity(bugTrackerUrlIntent)
462 R.id.forum -> { // Forum.
463 // Create a forum URL intent.
464 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
466 // Add the URL to the intent.
467 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
470 startActivity(forumUrlIntent)
473 R.id.donations -> { // Donations.
474 // Create a donations URL intent.
475 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
477 // Add the URL to the intent.
478 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
481 startActivity(donationsUrlIntent)
485 // Close the navigation drawer.
486 drawerLayout.closeDrawer(GravityCompat.START)
488 // Consume the click.
492 override fun onCloseStoragePermissionDialog() {
493 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
494 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
497 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
498 // Run the default commands.
499 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
501 //Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included).
502 if (grantResults.isNotEmpty()) {
503 // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty.
504 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted.
505 // Populate Privacy Cell.
506 registerTelephonyManagerListener()
507 } else { // The read phone state permission was denied.
508 // Display the phone permission text on the main activity.
509 overallStatusTextView.text = getString(R.string.phone_permission_text)
514 private fun registerTelephonyManagerListener() {
515 // Get a handle for the telephony manager.
516 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
518 // Listen to changes in the cell network state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
519 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE or PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
522 private fun populateOverallSecurityStatus() {
523 // Create an overall status dialog type integer.
524 val overallStatusDialogTypeInt: Int
526 // Populate the over security status.
527 if ((voiceNetworkSecurityStatus == ProtocolHelper.ANTIQUATED) || (dataNetworkSecurityStatus == ProtocolHelper.ANTIQUATED)) { // This is an antiquated network.
528 // Populate the image view.
529 overallStatusImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.antiquated))
532 overallStatusTextView.text = getString(R.string.antiquated_protocols)
534 // Set the text color.
535 overallStatusTextView.setTextColor(getColor(R.color.red_text))
537 // Set the stingray dialog type integer.
538 overallStatusDialogTypeInt = WebViewDialog.ANTIQUATED_NETWORK
539 } else if ((voiceNetworkSecurityStatus == ProtocolHelper.INSECURE) || (dataNetworkSecurityStatus == ProtocolHelper.INSECURE)) { // This is an insecure network.
540 // Populate the image view.
541 overallStatusImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.insecure))
544 overallStatusTextView.text = getString(R.string.insecure_protocols)
546 // Set the text color.
547 overallStatusTextView.setTextColor(getColor(R.color.yellow_text))
549 // Set the stingray dialog type integer.
550 overallStatusDialogTypeInt = WebViewDialog.STINGRAY
551 } else { // This is a secure network.
552 // Populate the image view.
553 overallStatusImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure))
556 overallStatusTextView.text = getString(R.string.secure_protocols)
558 // Set the text color.
559 overallStatusTextView.setTextColor(getColor(R.color.blue_text))
561 // Set the stingray dialog type integer.
562 overallStatusDialogTypeInt = WebViewDialog.STINGRAY
565 // Set the overall status click listener.
566 overallStatusLinearLayout.setOnClickListener {
567 // Instantiate the stingray dialog fragment.
568 val stingrayDialogFragment = WebViewDialog().type(overallStatusDialogTypeInt)
570 // Show the alert dialog.
571 stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))