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.PhonePermissionDialog.StoragePermissionDialogListener
58 import com.stoutner.privacycell.dialogs.WebViewDialog
59 import com.stoutner.privacycell.helpers.ProtocolHelper
60 import com.stoutner.privacycell.services.RealtimeMonitoringService
62 class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, StoragePermissionDialogListener {
63 // Define the class variables.
64 private var voiceNetworkSecurityStatus = ProtocolHelper.UNPOPULATED
65 private var dataNetworkSecurityStatus = ProtocolHelper.UNPOPULATED
67 // Declare the class variables.
68 private lateinit var phoneStateListener: PhoneStateListener // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
70 // Declare the class views.
71 private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
72 private lateinit var drawerLayout: DrawerLayout
73 private lateinit var overallStatusLinearLayout: LinearLayout
74 private lateinit var overallStatusImageView: ImageView
75 private lateinit var overallStatusTextView: TextView
78 // Define the public constants.
79 const val PHONE_PERMISSION_REQUEST_CODE = 0
80 const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1
83 override fun onCreate(savedInstanceState: Bundle?) {
84 // Run the default commands.
85 super.onCreate(savedInstanceState)
87 // Get a handle for the shared preferences.
88 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
90 // Get the preferences.
91 val realtimeMonitoring = sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)
92 val consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)
93 val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
95 // Set the content view.
97 setContentView(R.layout.privacy_cell_bottom_appbar)
99 setContentView(R.layout.privacy_cell_top_appbar)
102 // Get handles for the views.
103 drawerLayout = findViewById(R.id.drawerlayout)
104 val toolbar = findViewById<Toolbar>(R.id.toolbar)
105 overallStatusLinearLayout = findViewById(R.id.overall_status_linearlayout)
106 overallStatusImageView = findViewById(R.id.overall_status_imageview)
107 overallStatusTextView = findViewById(R.id.overall_status_textview)
108 val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
109 val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
110 val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
111 val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
112 val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
113 val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
114 val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
115 val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
116 val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
117 val navigationView = findViewById<NavigationView>(R.id.navigationview)
119 // Set the support action bar.
120 setSupportActionBar(toolbar)
122 // Get a handle for the action bar.
123 val actionBar = supportActionBar!!
125 // Set a custom view on the action bar.
126 actionBar.setCustomView(R.layout.app_bar_textview)
128 // Display the custom view.
129 actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
131 // Define a hamburger icon at the start of the app bar. It will be populated in `onPostCreate()`.
132 actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
134 // Listen for touches on the navigation menu.
135 navigationView.setNavigationItemSelectedListener(this)
137 // Add a drawer listener.
138 drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
139 override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
143 override fun onDrawerOpened(drawerView: View) {
147 override fun onDrawerClosed(drawerView: View) {
148 // Reset the drawer icon when the drawer is closed. Otherwise, it is an arrow if the drawer is open when the app is restarted.
149 actionBarDrawerToggle.syncState()
152 override fun onDrawerStateChanged(newState: Int) {
157 // Instantiate the protocol helper.
158 val protocolHelper = ProtocolHelper()
160 // Define the phone state listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
161 phoneStateListener = object : PhoneStateListener() {
162 @Deprecated("Deprecated in Java")
163 override fun onServiceStateChanged(serviceState: ServiceState) { // Update the voice network.
164 // Get the network registration info for the voice network, which is the second of the three entries (the first appears to be Wi-Fi and the third appears to be the cell data network).
165 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
167 // Get the voice network type int.
168 val voiceNetworkTypeInt = networkRegistrationInfo.accessNetworkTechnology
170 // Get the voice network security status.
171 voiceNetworkSecurityStatus = protocolHelper.checkNetwork(voiceNetworkTypeInt, consider3gAntiquated)
173 // Get the voice network type.
174 val voiceNetworkStringArray = protocolHelper.getNetworkTypeStringArray(voiceNetworkTypeInt, applicationContext)
176 // Populate the voice network text views.
177 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkStringArray[0])
178 voiceNetworkDetailsTextView.text = voiceNetworkStringArray[1]
180 // Set the color of the voice network.
181 when (voiceNetworkSecurityStatus) {
182 ProtocolHelper.SECURE -> voiceNetworkTextView.setTextColor(getColor(R.color.blue_text))
183 ProtocolHelper.INSECURE -> voiceNetworkTextView.setTextColor(getColor(R.color.yellow_text))
184 ProtocolHelper.ANTIQUATED -> voiceNetworkTextView.setTextColor(getColor(R.color.red_text))
187 // Set the voice network click listener.
188 voiceNetworkLinearLayout.setOnClickListener {
189 // Instantiate the voice network dialog fragment according to the network type.
190 val voiceNetworkDialogFragment = when (voiceNetworkTypeInt) {
191 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
192 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
193 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
194 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
195 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
196 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
197 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
198 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
199 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
200 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
201 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
202 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
203 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
204 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
205 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
206 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
207 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
208 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
209 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
210 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
211 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
214 // Show the alert dialog.
215 voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
218 // Populate the overall security status.
219 populateOverallSecurityStatus()
222 @Deprecated("Deprecated in Java")
223 @SuppressLint("SwitchIntDef")
224 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) { // Update the data network.
225 // Get the network type integers. <https://source.android.com/devices/tech/connect/acts-5g-testing>
226 val dataNetworkTypeInt = telephonyDisplayInfo.networkType
227 val additionalNetworkInfoTypeInt = telephonyDisplayInfo.overrideNetworkType
229 // Get the data network security status.
230 dataNetworkSecurityStatus = protocolHelper.checkNetwork(dataNetworkTypeInt, consider3gAntiquated)
232 // Get the strings that correspond to the network information.
233 val dataNetworkStringArray = protocolHelper.getNetworkTypeStringArray(dataNetworkTypeInt, applicationContext)
234 val additionalNetworkInfoStringArray = protocolHelper.getAdditionalNetworkInfoStringArray(additionalNetworkInfoTypeInt, applicationContext)
236 // Populate the data network text views.
237 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkStringArray[0])
238 dataNetworkDetailsTextView.text = dataNetworkStringArray[1]
239 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfoStringArray[0])
240 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfoStringArray[1]
242 // Set the color of the data network.
243 when (dataNetworkSecurityStatus) {
244 ProtocolHelper.SECURE -> dataNetworkTextView.setTextColor(getColor(R.color.blue_text))
245 ProtocolHelper.INSECURE -> dataNetworkTextView.setTextColor(getColor(R.color.yellow_text))
246 ProtocolHelper.ANTIQUATED -> dataNetworkTextView.setTextColor(getColor(R.color.red_text))
249 // Set the color of the additional network info.
250 when (protocolHelper.checkAdditionalNetworkInfo(additionalNetworkInfoTypeInt)) {
251 ProtocolHelper.SECURE -> additionalNetworkInfoTextView.setTextColor(getColor(R.color.blue_text))
252 ProtocolHelper.INSECURE -> additionalNetworkInfoTextView.setTextColor(getColor(R.color.yellow_text))
253 ProtocolHelper.ANTIQUATED -> additionalNetworkInfoTextView.setTextColor(getColor(R.color.red_text))
256 // Set the data network click listener.
257 dataNetworkLinearLayout.setOnClickListener {
258 // Instantiate the data network dialog fragment according to the network type.
259 val dataNetworkDialogFragment = when (dataNetworkTypeInt) {
260 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
261 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
262 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
263 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
264 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
265 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
266 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
267 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
268 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
269 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
270 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
271 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
272 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
273 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
274 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
275 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
276 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
277 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
278 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
279 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
280 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
283 // Show the alert dialog.
284 dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
287 // Set the additional network info click listener.
288 additionalNetworkInfoLinearLayout.setOnClickListener {
289 // Instantiate the initial network info dialog fragment according to the network type.
290 val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
291 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
292 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
293 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
294 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
295 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE) // Can be removed once the minimum API >= 31.
296 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
297 else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
300 // Show the alert dialog.
301 additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
304 // Populate the overall security status.
305 populateOverallSecurityStatus()
309 // Start the realtime monitoring service if it is enabled.
310 if (realtimeMonitoring) {
311 // Get a handle for the activity manager.
312 val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
314 // Get a list of the running service info. The deprecated `getRunningServices()` now only returns services stared by Privacy Cell, but that is all we want to know anyway.
315 val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
317 // Start the service if it is not already running.
318 if (runningServiceInfoList.isEmpty()) {
319 startService(Intent(this, RealtimeMonitoringService::class.java))
324 override fun onPostCreate(savedInstanceState: Bundle?) {
325 // Run the default commands.
326 super.onPostCreate(savedInstanceState)
328 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
329 actionBarDrawerToggle.syncState()
332 override fun onStart() {
333 // Run the default commands.
336 // Check to see if the read phone state permission has been granted. These commands need to be run on every start so that the listener is reregistered.
337 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
338 // Register the telephony manager listener.
339 registerTelephonyManagerListener()
340 } else { // The phone permission has not been granted.
341 // Check if the user has previously denied the storage permission.
342 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first.
343 // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted while the dialog is shown.
344 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown.
345 // Instantiate the phone permission dialog fragment.
346 val phonePermissionDialogFragment = PhonePermissionDialog()
348 // Show the phone permission alert dialog. The permission will be requested when the dialog is closed.
349 phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
351 } else { // Show the permission request directly.
352 // Request the read phone state permission.
353 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), PHONE_PERMISSION_REQUEST_CODE)
358 override fun onStop() {
359 // Run the default commands.
362 // Get a handle for the telephony manager.
363 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
365 // Unregister the telephony manager listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
366 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
369 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
370 // Run the commands that correspond to the selected menu item.
371 when (menuItem.itemId) {
372 R.id.settings -> { // Settings.
373 // Create an intent to load the Settings activity.
374 val settingsIntent = Intent(this, SettingsActivity::class.java)
377 startActivity(settingsIntent)
381 // Create an intent to load the Protocols activity.
382 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
385 startActivity(protocolsIntent)
388 R.id.logcat -> { // Logcat.
389 // Create an intent to load the Logcat activity.
390 val logcatIntent = Intent(this, LogcatActivity::class.java)
393 startActivity(logcatIntent)
396 R.id.permissions -> { // Permissions.
397 // Instantiate the permissions dialog fragment.
398 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
400 // Show the alert dialog.
401 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
404 R.id.privacy_policy -> { // Privacy Policy.
405 // Instantiate the privacy policy dialog fragment.
406 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
408 // Show the alert dialog.
409 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
412 R.id.changelog -> { // Changelog.
413 // Instantiate the changelog dialog fragment.
414 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
416 // Show the alert dialog.
417 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
420 R.id.licenses -> { // Licenses.
421 // Instantiate the licenses dialog fragment.
422 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
424 // Show the alert dialog.
425 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
428 R.id.contributors -> { // Contributors.
429 // Instantiate the contributors dialog fragment.
430 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
432 // Show the alert dialog.
433 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
436 R.id.news -> { // News.
437 // Create a news URL intent.
438 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
440 // Add the URL to the intent.
441 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
444 startActivity(newsUrlIntent)
447 R.id.roadmap -> { // Roadmap.
448 // Create a roadmap URL intent.
449 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
451 // Add the URL to the intent.
452 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
455 startActivity(roadmapUrlIntent)
458 R.id.bug_tracker -> { // Bug tracker.
459 // Create a bug tracker URL intent.
460 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
462 // Add the URL to the intent.
463 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
466 startActivity(bugTrackerUrlIntent)
469 R.id.forum -> { // Forum.
470 // Create a forum URL intent.
471 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
473 // Add the URL to the intent.
474 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
477 startActivity(forumUrlIntent)
480 R.id.donations -> { // Donations.
481 // Create a donations URL intent.
482 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
484 // Add the URL to the intent.
485 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
488 startActivity(donationsUrlIntent)
492 // Close the navigation drawer.
493 drawerLayout.closeDrawer(GravityCompat.START)
495 // Consume the click.
499 override fun onCloseStoragePermissionDialog() {
500 // Request the read phone state permission.
501 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), PHONE_PERMISSION_REQUEST_CODE)
504 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
505 // Run the default commands.
506 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
508 // Only process the results if they exist (this method is triggered when a dialog is presented the first time for an app, but no grant results are included) and the result is for the phone permission.
509 if (grantResults.isNotEmpty() && (requestCode == PHONE_PERMISSION_REQUEST_CODE)) {
510 // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty.
511 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted.
512 // Populate Privacy Cell.
513 registerTelephonyManagerListener()
514 } else { // The read phone state permission was denied.
515 // Display the phone permission text on the main activity.
516 overallStatusTextView.text = getString(R.string.phone_permission_text)
521 private fun registerTelephonyManagerListener() {
522 // Get a handle for the telephony manager.
523 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
525 // Listen to changes in the cell network state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
526 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE or PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED)
529 private fun populateOverallSecurityStatus() {
530 // Create an overall status dialog type integer.
531 val overallStatusDialogTypeInt: Int
533 // Populate the over security status.
534 if ((voiceNetworkSecurityStatus == ProtocolHelper.ANTIQUATED) || (dataNetworkSecurityStatus == ProtocolHelper.ANTIQUATED)) { // This is an antiquated network.
535 // Populate the image view.
536 overallStatusImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.antiquated))
539 overallStatusTextView.text = getString(R.string.antiquated_protocols)
541 // Set the text color.
542 overallStatusTextView.setTextColor(getColor(R.color.red_text))
544 // Set the stingray dialog type integer.
545 overallStatusDialogTypeInt = WebViewDialog.ANTIQUATED_NETWORK
546 } else if ((voiceNetworkSecurityStatus == ProtocolHelper.INSECURE) || (dataNetworkSecurityStatus == ProtocolHelper.INSECURE)) { // This is an insecure network.
547 // Populate the image view.
548 overallStatusImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.insecure))
551 overallStatusTextView.text = getString(R.string.insecure_protocols)
553 // Set the text color.
554 overallStatusTextView.setTextColor(getColor(R.color.yellow_text))
556 // Set the stingray dialog type integer.
557 overallStatusDialogTypeInt = WebViewDialog.STINGRAY
558 } else { // This is a secure network.
559 // Populate the image view.
560 overallStatusImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure))
563 overallStatusTextView.text = getString(R.string.secure_protocols)
565 // Set the text color.
566 overallStatusTextView.setTextColor(getColor(R.color.blue_text))
568 // Set the stingray dialog type integer.
569 overallStatusDialogTypeInt = WebViewDialog.STINGRAY
572 // Set the overall status click listener.
573 overallStatusLinearLayout.setOnClickListener {
574 // Instantiate the stingray dialog fragment.
575 val stingrayDialogFragment = WebViewDialog().type(overallStatusDialogTypeInt)
577 // Show the alert dialog.
578 stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))