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.services.RealtimeMonitoringService
60 class PrivacyCellActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener, PhonePermissionDialog.StoragePermissionDialogListener {
61 // Declare the class variables.
62 private lateinit var actionBarDrawerToggle: ActionBarDrawerToggle
63 private lateinit var phoneStateListener: PhoneStateListener // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
65 // Declare the class views.
66 private lateinit var drawerLayout: DrawerLayout
67 private lateinit var summaryTextView: TextView
69 override fun onCreate(savedInstanceState: Bundle?) {
70 // Run the default commands.
71 super.onCreate(savedInstanceState)
73 // Get a handle for the shared preferences.
74 val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
76 // Get the preferences.
77 val realtimeMonitoring = sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)
78 val consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)
79 val bottomAppBar = sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)
81 // Set the content view.
83 setContentView(R.layout.privacy_cell_bottom_appbar)
85 setContentView(R.layout.privacy_cell_top_appbar)
88 // Get handles for the views.
89 drawerLayout = findViewById(R.id.drawerlayout)
90 val toolbar = findViewById<Toolbar>(R.id.toolbar)
91 val stingrayLinearLayout = findViewById<LinearLayout>(R.id.stingray_linearlayout)
92 val stingrayImageView = findViewById<ImageView>(R.id.stingray_imageview)
93 summaryTextView = findViewById(R.id.summary_textview)
94 val voiceNetworkLinearLayout = findViewById<LinearLayout>(R.id.voice_network_linearlayout)
95 val voiceNetworkTextView = findViewById<TextView>(R.id.voice_network)
96 val voiceNetworkDetailsTextView = findViewById<TextView>(R.id.voice_network_details)
97 val dataNetworkLinearLayout = findViewById<LinearLayout>(R.id.data_network_linearlayout)
98 val dataNetworkTextView = findViewById<TextView>(R.id.data_network)
99 val dataNetworkDetailsTextView = findViewById<TextView>(R.id.data_network_details)
100 val additionalNetworkInfoLinearLayout = findViewById<LinearLayout>(R.id.additional_network_info_linearlayout)
101 val additionalNetworkInfoTextView = findViewById<TextView>(R.id.additional_network_info)
102 val additionalNetworkInfoDetailsTextView = findViewById<TextView>(R.id.additional_network_info_details)
103 val navigationView = findViewById<NavigationView>(R.id.navigationview)
105 // Set the support action bar.
106 setSupportActionBar(toolbar)
108 // Get a handle for the action bar.
109 val actionBar = supportActionBar!!
111 // Set a custom view on the action bar.
112 actionBar.setCustomView(R.layout.app_bar_textview)
114 // Display the custom view.
115 actionBar.displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM
117 // Define a hamburger icon at the start of the app bar. It will be populated in `onPostCreate()`.
118 actionBarDrawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.open_navigation_drawer, R.string.close_navigation_drawer)
120 // Listen for touches on the navigation menu.
121 navigationView.setNavigationItemSelectedListener(this)
123 // Add a drawer listener.
124 drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
125 override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
129 override fun onDrawerOpened(drawerView: View) {
133 override fun onDrawerClosed(drawerView: View) {
134 // Reset the drawer icon when the drawer is closed. Otherwise, it is an arrow if the drawer is open when the app is restarted.
135 actionBarDrawerToggle.syncState()
138 override fun onDrawerStateChanged(newState: Int) {
143 // Define the phone state listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
144 phoneStateListener = object : PhoneStateListener() {
145 @Deprecated("Deprecated in Java")
146 @SuppressLint("SwitchIntDef")
147 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
148 // Declare the stingray dialog type integer.
149 val summaryDialogTypeInteger: Int
151 // Populate the stingray security information. <https://source.android.com/devices/tech/connect/acts-5g-testing>
152 if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_IWLAN) ||
153 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN)) { // This is a secure network.
154 // Populate the image view.
155 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure))
158 summaryTextView.text = getString(R.string.secure_protocols)
160 // Set the text color.
161 summaryTextView.setTextColor(getColor(R.color.blue_text))
163 // Set the stingray dialog type integer.
164 summaryDialogTypeInteger = WebViewDialog.STINGRAY
165 } else if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_LTE) || (!consider3gAntiquated && (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_1xRTT ||
166 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
167 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_B) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EHRPD) ||
168 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UMTS) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_TD_SCDMA) ||
169 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSDPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSUPA) ||
170 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPAP)))) {
171 // This is an insecure network.
172 // Populate the image view.
173 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.insecure))
176 summaryTextView.text = getString(R.string.insecure_protocols)
178 // Set the text color.
179 summaryTextView.setTextColor(getColor(R.color.yellow_900))
181 // Set the stingray dialog type integer.
182 summaryDialogTypeInteger = WebViewDialog.STINGRAY
183 } else { // This is an antiquated network.
184 // Populate the image view.
185 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.antiquated))
188 summaryTextView.text = getString(R.string.antiquated_protocols)
190 // Set the text color.
191 summaryTextView.setTextColor(getColor(R.color.red_text))
193 // Set the stingray dialog type integer.
194 summaryDialogTypeInteger = WebViewDialog.ANTIQUATED_NETWORK
197 // Get the strings that correspond to the network information.
198 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
199 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
201 // Populate the data network text views.
202 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
203 dataNetworkDetailsTextView.text = dataNetworkType[1]
204 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
205 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
207 // Set the stingray click listener.
208 stingrayLinearLayout.setOnClickListener {
209 // Instantiate the stingray dialog fragment.
210 val stingrayDialogFragment = WebViewDialog().type(summaryDialogTypeInteger)
212 // Show the alert dialog.
213 stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
216 // Set the data network click listener.
217 dataNetworkLinearLayout.setOnClickListener {
218 // Instantiate the data network dialog fragment according to the network type.
219 val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
220 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
221 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
222 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
223 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
224 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
225 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
226 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
227 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
228 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
229 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
230 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
231 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
232 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
233 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
234 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
235 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
236 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
237 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
238 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
239 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
240 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
243 // Show the alert dialog.
244 dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
247 // Set the additional network info click listener.
248 additionalNetworkInfoLinearLayout.setOnClickListener {
249 // Instantiate the initial network info dialog fragment according to the network type.
250 val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
251 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
252 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
253 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
254 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
255 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE) // Can be removed once the minimum API >= 31.
256 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
257 else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
260 // Show the alert dialog.
261 additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
265 @Deprecated("Deprecated in Java")
266 override fun onServiceStateChanged(serviceState: ServiceState) {
267 // 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).
268 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
270 // Get the voice network type.
271 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
273 // Populate the voice network text views.
274 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
275 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
277 // Set the voice network click listener.
278 voiceNetworkLinearLayout.setOnClickListener {
279 // Instantiate the voice network dialog fragment according to the network type.
280 val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
281 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
282 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
283 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
284 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
285 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
286 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
287 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
288 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
289 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
290 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
291 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
292 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
293 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
294 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
295 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
296 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
297 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
298 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
299 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
300 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
301 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
304 // Show the alert dialog.
305 voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
310 // Start the realtime monitoring service if it is enabled.
311 if (realtimeMonitoring) {
312 // Get a handle for the activity manager.
313 val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
315 // 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.
316 val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
318 // Start the service if it is not already running.
319 if (runningServiceInfoList.isEmpty()) {
320 startService(Intent(this, RealtimeMonitoringService::class.java))
325 override fun onPostCreate(savedInstanceState: Bundle?) {
326 // Run the default commands.
327 super.onPostCreate(savedInstanceState)
329 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
330 actionBarDrawerToggle.syncState()
333 override fun onStart() {
334 // Run the default commands.
337 // 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.
338 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
339 // Register the telephony manager listener.
340 registerTelephonyManagerListener()
341 } else { // The phone permission has not been granted.
342 // Check if the user has previously denied the storage permission.
343 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first.
344 // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted when the dialog is shown.
345 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown.
346 // Instantiate the phone permission dialog fragment.
347 val phonePermissionDialogFragment = PhonePermissionDialog()
349 // Show the phone permission alert dialog. The permission will be requested when the dialog is closed.
350 phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
352 } else { // Show the permission request directly.
353 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
354 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
359 override fun onStop() {
360 // Run the default commands.
363 // Get a handle for the telephony manager.
364 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
366 // Unregister the telephony manager listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
367 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
370 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
371 // Run the commands that correspond to the selected menu item.
372 when (menuItem.itemId) {
373 R.id.settings -> { // Settings.
374 // Create an intent to load the Settings activity.
375 val settingsIntent = Intent(this, SettingsActivity::class.java)
378 startActivity(settingsIntent)
382 // Create an intent to load the Protocols activity.
383 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
386 startActivity(protocolsIntent)
389 R.id.logcat -> { // Logcat.
390 // Create an intent to load the Logcat activity.
391 val logcatIntent = Intent(this, LogcatActivity::class.java)
394 startActivity(logcatIntent)
397 R.id.permissions -> { // Permissions.
398 // Instantiate the permissions dialog fragment.
399 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
401 // Show the alert dialog.
402 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
405 R.id.privacy_policy -> { // Privacy Policy.
406 // Instantiate the privacy policy dialog fragment.
407 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
409 // Show the alert dialog.
410 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
413 R.id.changelog -> { // Changelog.
414 // Instantiate the changelog dialog fragment.
415 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
417 // Show the alert dialog.
418 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
421 R.id.licenses -> { // Licenses.
422 // Instantiate the licenses dialog fragment.
423 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
425 // Show the alert dialog.
426 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
429 R.id.contributors -> { // Contributors.
430 // Instantiate the contributors dialog fragment.
431 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
433 // Show the alert dialog.
434 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
437 R.id.news -> { // News.
438 // Create a news URL intent.
439 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
441 // Add the URL to the intent.
442 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
445 startActivity(newsUrlIntent)
448 R.id.roadmap -> { // Roadmap.
449 // Create a roadmap URL intent.
450 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
452 // Add the URL to the intent.
453 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
456 startActivity(roadmapUrlIntent)
459 R.id.bug_tracker -> { // Bug tracker.
460 // Create a bug tracker URL intent.
461 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
463 // Add the URL to the intent.
464 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
467 startActivity(bugTrackerUrlIntent)
470 R.id.forum -> { // Forum.
471 // Create a forum URL intent.
472 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
474 // Add the URL to the intent.
475 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
478 startActivity(forumUrlIntent)
481 R.id.donations -> { // Donations.
482 // Create a donations URL intent.
483 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
485 // Add the URL to the intent.
486 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
489 startActivity(donationsUrlIntent)
493 // Close the navigation drawer.
494 drawerLayout.closeDrawer(GravityCompat.START)
496 // Consume the click.
500 override fun onCloseStoragePermissionDialog() {
501 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
502 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
505 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
506 // Run the default commands.
507 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
509 //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).
510 if (grantResults.isNotEmpty()) {
511 // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty.
512 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted.
513 // Populate Privacy Cell.
514 registerTelephonyManagerListener()
515 } else { // The read phone state permission was denied.
516 // Display the phone permission text on the main activity.
517 summaryTextView.text = getString(R.string.phone_permission_text)
522 private fun registerTelephonyManagerListener() {
523 // Get a handle for the telephony manager.
524 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
526 // Listen to changes in the cell network state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
527 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
530 private fun getNetworkType(networkType: Int) : Array<String> {
531 // Return the string that corresponds to the network type.
532 return when(networkType) {
533 TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "")
534 TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detail))
535 TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail))
536 TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail))
537 TelephonyManager.NETWORK_TYPE_CDMA -> arrayOf(getString(R.string.cdma), getString(R.string.cdma_detail))
538 TelephonyManager.NETWORK_TYPE_EVDO_0 -> arrayOf(getString(R.string.evdo_0), getString(R.string.evdo_0_detail))
539 TelephonyManager.NETWORK_TYPE_EVDO_A -> arrayOf(getString(R.string.evdo_a), getString(R.string.evdo_a_detail))
540 TelephonyManager.NETWORK_TYPE_1xRTT -> arrayOf(getString(R.string.rtt), getString(R.string.rtt_detail))
541 TelephonyManager.NETWORK_TYPE_HSDPA -> arrayOf(getString(R.string.hsdpa), getString(R.string.hsdpa_detail))
542 TelephonyManager.NETWORK_TYPE_HSUPA -> arrayOf(getString(R.string.hsupa), getString(R.string.hsupa_detail))
543 TelephonyManager.NETWORK_TYPE_HSPA -> arrayOf(getString(R.string.hspa), getString(R.string.hspa_detail))
544 TelephonyManager.NETWORK_TYPE_IDEN -> arrayOf(getString(R.string.iden), getString(R.string.iden_detail))
545 TelephonyManager.NETWORK_TYPE_EVDO_B -> arrayOf(getString(R.string.evdo_b), getString(R.string.evdo_b_detail))
546 TelephonyManager.NETWORK_TYPE_LTE -> arrayOf(getString(R.string.lte), getString(R.string.lte_detail))
547 TelephonyManager.NETWORK_TYPE_EHRPD -> arrayOf(getString(R.string.ehrpd), getString(R.string.ehrpd_detail))
548 TelephonyManager.NETWORK_TYPE_HSPAP -> arrayOf(getString(R.string.hspap), getString(R.string.hspap_detail))
549 TelephonyManager.NETWORK_TYPE_GSM -> arrayOf(getString(R.string.gsm), getString(R.string.gsm_detail))
550 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail))
551 TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail))
552 TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail))
553 else -> arrayOf(getString(R.string.error), "")
557 private fun getAdditionalNetworkInfo(overrideNetworkType: Int) : Array<String> {
558 // Return the string that corresponds to the override network type.
559 return when(overrideNetworkType) {
560 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> arrayOf(getString(R.string.none), "")
561 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
562 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
563 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.nr_nsa), getString(R.string.nr_nsa_detail))
564 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> arrayOf(getString(R.string.nr_nsa_mmwave), getString(R.string.nr_nsa_mmwave_detail)) // Can be removed once the minimum API >= 31.
565 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> arrayOf(getString(R.string.nr_advanced), getString(R.string.nr_advanced_detail))
566 else -> arrayOf(getString(R.string.error), "")