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 @SuppressLint("SwitchIntDef")
146 override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) {
147 // Declare the stingray dialog type integer.
148 val summaryDialogTypeInteger: Int
150 // Populate the stingray security information. <https://source.android.com/devices/tech/connect/acts-5g-testing>
151 if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_IWLAN) ||
152 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN)) { // This is a secure network.
153 // Populate the image view.
154 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.secure))
157 summaryTextView.text = getString(R.string.secure_protocols)
159 // Set the text color.
160 summaryTextView.setTextColor(getColor(R.color.blue_text))
162 // Set the stingray dialog type integer.
163 summaryDialogTypeInteger = WebViewDialog.STINGRAY
164 } else if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_LTE) || (!consider3gAntiquated && (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_1xRTT ||
165 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_A) ||
166 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_B) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EHRPD) ||
167 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UMTS) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_TD_SCDMA) ||
168 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSDPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSUPA) ||
169 (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPAP)))) {
170 // This is an insecure network.
171 // Populate the image view.
172 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.insecure))
175 summaryTextView.text = getString(R.string.insecure_protocols)
177 // Set the text color.
178 summaryTextView.setTextColor(getColor(R.color.yellow_900))
180 // Set the stingray dialog type integer.
181 summaryDialogTypeInteger = WebViewDialog.STINGRAY
182 } else { // This is an antiquated network.
183 // Populate the image view.
184 stingrayImageView.setImageDrawable(AppCompatResources.getDrawable(applicationContext, R.drawable.antiquated))
187 summaryTextView.text = getString(R.string.antiquated_protocols)
189 // Set the text color.
190 summaryTextView.setTextColor(getColor(R.color.red_text))
192 // Set the stingray dialog type integer.
193 summaryDialogTypeInteger = WebViewDialog.ANTIQUATED_NETWORK
196 // Get the strings that correspond to the network information.
197 val dataNetworkType = getNetworkType(telephonyDisplayInfo.networkType)
198 val additionalNetworkInfo = getAdditionalNetworkInfo(telephonyDisplayInfo.overrideNetworkType)
200 // Populate the data network text views.
201 dataNetworkTextView.text = getString(R.string.data_network, dataNetworkType[0])
202 dataNetworkDetailsTextView.text = dataNetworkType[1]
203 additionalNetworkInfoTextView.text = getString(R.string.additional_network_info, additionalNetworkInfo[0])
204 additionalNetworkInfoDetailsTextView.text = additionalNetworkInfo[1]
206 // Set the stingray click listener.
207 stingrayLinearLayout.setOnClickListener {
208 // Instantiate the stingray dialog fragment.
209 val stingrayDialogFragment = WebViewDialog().type(summaryDialogTypeInteger)
211 // Show the alert dialog.
212 stingrayDialogFragment.show(supportFragmentManager, getString(R.string.stingrays))
215 // Set the data network click listener.
216 dataNetworkLinearLayout.setOnClickListener {
217 // Instantiate the data network dialog fragment according to the network type.
218 val dataNetworkDialogFragment = when (telephonyDisplayInfo.networkType) {
219 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
220 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
221 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
222 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
223 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
224 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
225 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
226 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
227 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
228 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
229 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
230 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
231 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
232 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
233 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
234 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
235 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
236 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
237 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
238 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
239 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
242 // Show the alert dialog.
243 dataNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
246 // Set the additional network info click listener.
247 additionalNetworkInfoLinearLayout.setOnClickListener {
248 // Instantiate the initial network info dialog fragment according to the network type.
249 val additionalNetworkInfoDialogFragment = when (telephonyDisplayInfo.overrideNetworkType) {
250 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
251 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_CA)
252 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_LTE_ADVANCED_PRO)
253 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA)
254 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA_MMWAVE -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_NSA_MMWAVE) // Can be removed once the minimum API >= 31.
255 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NR_ADVANCED)
256 else -> WebViewDialog().type(WebViewDialog.OVERRIDE_NETWORK_NONE)
259 // Show the alert dialog.
260 additionalNetworkInfoDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
264 override fun onServiceStateChanged(serviceState: ServiceState) {
265 // 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).
266 val networkRegistrationInfo = serviceState.networkRegistrationInfoList[1]
268 // Get the voice network type.
269 val voiceNetworkType = getNetworkType(networkRegistrationInfo.accessNetworkTechnology)
271 // Populate the voice network text views.
272 voiceNetworkTextView.text = getString(R.string.voice_network, voiceNetworkType[0])
273 voiceNetworkDetailsTextView.text = voiceNetworkType[1]
275 // Set the voice network click listener.
276 voiceNetworkLinearLayout.setOnClickListener {
277 // Instantiate the voice network dialog fragment according to the network type.
278 val voiceNetworkDialogFragment = when (networkRegistrationInfo.accessNetworkTechnology) {
279 TelephonyManager.NETWORK_TYPE_UNKNOWN -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
280 TelephonyManager.NETWORK_TYPE_GPRS -> WebViewDialog().type(WebViewDialog.NETWORK_GPRS)
281 TelephonyManager.NETWORK_TYPE_EDGE -> WebViewDialog().type(WebViewDialog.NETWORK_EDGE)
282 TelephonyManager.NETWORK_TYPE_UMTS -> WebViewDialog().type(WebViewDialog.NETWORK_UMTS)
283 TelephonyManager.NETWORK_TYPE_CDMA -> WebViewDialog().type(WebViewDialog.NETWORK_CDMA)
284 TelephonyManager.NETWORK_TYPE_EVDO_0 -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_0)
285 TelephonyManager.NETWORK_TYPE_EVDO_A -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_A)
286 TelephonyManager.NETWORK_TYPE_1xRTT -> WebViewDialog().type(WebViewDialog.NETWORK_1xRTT)
287 TelephonyManager.NETWORK_TYPE_HSDPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSDPA)
288 TelephonyManager.NETWORK_TYPE_HSUPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSUPA)
289 TelephonyManager.NETWORK_TYPE_HSPA -> WebViewDialog().type(WebViewDialog.NETWORK_HSPA)
290 TelephonyManager.NETWORK_TYPE_IDEN -> WebViewDialog().type(WebViewDialog.NETWORK_IDEN)
291 TelephonyManager.NETWORK_TYPE_EVDO_B -> WebViewDialog().type(WebViewDialog.NETWORK_EVDO_B)
292 TelephonyManager.NETWORK_TYPE_LTE -> WebViewDialog().type(WebViewDialog.NETWORK_LTE)
293 TelephonyManager.NETWORK_TYPE_EHRPD -> WebViewDialog().type(WebViewDialog.NETWORK_EHRPD)
294 TelephonyManager.NETWORK_TYPE_HSPAP -> WebViewDialog().type(WebViewDialog.NETWORK_HSPAP)
295 TelephonyManager.NETWORK_TYPE_GSM -> WebViewDialog().type(WebViewDialog.NETWORK_GSM)
296 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> WebViewDialog().type(WebViewDialog.NETWORK_TD_SCDMA)
297 TelephonyManager.NETWORK_TYPE_IWLAN -> WebViewDialog().type(WebViewDialog.NETWORK_IWLAN)
298 TelephonyManager.NETWORK_TYPE_NR -> WebViewDialog().type(WebViewDialog.NETWORK_NR)
299 else -> WebViewDialog().type(WebViewDialog.NETWORK_UNKNOWN)
302 // Show the alert dialog.
303 voiceNetworkDialogFragment.show(supportFragmentManager, getString(R.string.voice_network))
308 // Start the realtime monitoring service if it is enabled.
309 if (realtimeMonitoring) {
310 // Get a handle for the activity manager.
311 val activityManager: ActivityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
313 // 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.
314 val runningServiceInfoList: List<ActivityManager.RunningServiceInfo> = activityManager.getRunningServices(1)
316 // Start the service if it is not already running.
317 if (runningServiceInfoList.isEmpty()) {
318 startService(Intent(this, RealtimeMonitoringService::class.java))
323 override fun onPostCreate(savedInstanceState: Bundle?) {
324 // Run the default commands.
325 super.onPostCreate(savedInstanceState)
327 // Sync the state of the DrawerToggle after the default `onRestoreInstanceState()` has finished. This creates the navigation drawer icon.
328 actionBarDrawerToggle.syncState()
331 override fun onStart() {
332 // Run the default commands.
335 // 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.
336 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { // The storage permission has been granted.
337 // Register the telephony manager listener.
338 registerTelephonyManagerListener()
339 } else { // The phone permission has not been granted.
340 // Check if the user has previously denied the storage permission.
341 if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_PHONE_STATE)) { // Show a dialog explaining the request first.
342 // Check to see if a phone permission dialog is already displayed. This happens if the app is restarted when the dialog is shown.
343 if (supportFragmentManager.findFragmentByTag(getString(R.string.phone_permission)) == null) { // No dialog is currently shown.
344 // Instantiate the phone permission dialog fragment.
345 val phonePermissionDialogFragment = PhonePermissionDialog()
347 // Show the phone permission alert dialog. The permission will be requested when the dialog is closed.
348 phonePermissionDialogFragment.show(supportFragmentManager, getString(R.string.phone_permission))
350 } else { // Show the permission request directly.
351 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
352 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
357 override fun onStop() {
358 // Run the default commands.
361 // Get a handle for the telephony manager.
362 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
364 // Unregister the telephony manager listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
365 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE)
368 override fun onNavigationItemSelected(menuItem: MenuItem) : Boolean {
369 // Run the commands that correspond to the selected menu item.
370 when (menuItem.itemId) {
371 R.id.settings -> { // Settings.
372 // Create an intent to load the Settings activity.
373 val settingsIntent = Intent(this, SettingsActivity::class.java)
376 startActivity(settingsIntent)
380 // Create an intent to load the Protocols activity.
381 val protocolsIntent = Intent(this, ProtocolsActivity::class.java)
384 startActivity(protocolsIntent)
387 R.id.logcat -> { // Logcat.
388 // Create an intent to load the Logcat activity.
389 val logcatIntent = Intent(this, LogcatActivity::class.java)
392 startActivity(logcatIntent)
395 R.id.permissions -> { // Permissions.
396 // Instantiate the permissions dialog fragment.
397 val permissionsDialogFragment = WebViewDialog().type(WebViewDialog.PERMISSIONS)
399 // Show the alert dialog.
400 permissionsDialogFragment.show(supportFragmentManager, getString(R.string.permissions))
403 R.id.privacy_policy -> { // Privacy Policy.
404 // Instantiate the privacy policy dialog fragment.
405 val privacyPolicyDialogFragment = WebViewDialog().type(WebViewDialog.PRIVACY_POLICY)
407 // Show the alert dialog.
408 privacyPolicyDialogFragment.show(supportFragmentManager, getString(R.string.privacy_policy))
411 R.id.changelog -> { // Changelog.
412 // Instantiate the changelog dialog fragment.
413 val changelogDialogFragment = WebViewDialog().type(WebViewDialog.CHANGELOG)
415 // Show the alert dialog.
416 changelogDialogFragment.show(supportFragmentManager, getString(R.string.changelog))
419 R.id.licenses -> { // Licenses.
420 // Instantiate the licenses dialog fragment.
421 val licensesDialogFragment = WebViewDialog().type(WebViewDialog.LICENSES)
423 // Show the alert dialog.
424 licensesDialogFragment.show(supportFragmentManager, getString(R.string.licenses))
427 R.id.contributors -> { // Contributors.
428 // Instantiate the contributors dialog fragment.
429 val contributorsDialogFragment = WebViewDialog().type(WebViewDialog.CONTRIBUTORS)
431 // Show the alert dialog.
432 contributorsDialogFragment.show(supportFragmentManager, getString(R.string.contributors))
435 R.id.news -> { // News.
436 // Create a news URL intent.
437 val newsUrlIntent = Intent(Intent.ACTION_VIEW)
439 // Add the URL to the intent.
440 newsUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell/")
443 startActivity(newsUrlIntent)
446 R.id.roadmap -> { // Roadmap.
447 // Create a roadmap URL intent.
448 val roadmapUrlIntent = Intent(Intent.ACTION_VIEW)
450 // Add the URL to the intent.
451 roadmapUrlIntent.data = Uri.parse("https://www.stoutner.com/category/privacy-cell-roadmap/")
454 startActivity(roadmapUrlIntent)
457 R.id.bug_tracker -> { // Bug tracker.
458 // Create a bug tracker URL intent.
459 val bugTrackerUrlIntent = Intent(Intent.ACTION_VIEW)
461 // Add the URL to the intent.
462 bugTrackerUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/issues")
465 startActivity(bugTrackerUrlIntent)
468 R.id.forum -> { // Forum.
469 // Create a forum URL intent.
470 val forumUrlIntent = Intent(Intent.ACTION_VIEW)
472 // Add the URL to the intent.
473 forumUrlIntent.data = Uri.parse("https://redmine.stoutner.com/projects/privacy-cell/boards")
476 startActivity(forumUrlIntent)
479 R.id.donations -> { // Donations.
480 // Create a donations URL intent.
481 val donationsUrlIntent = Intent(Intent.ACTION_VIEW)
483 // Add the URL to the intent.
484 donationsUrlIntent.data = Uri.parse("https://www.stoutner.com/donations/")
487 startActivity(donationsUrlIntent)
491 // Close the navigation drawer.
492 drawerLayout.closeDrawer(GravityCompat.START)
494 // Consume the click.
498 override fun onCloseStoragePermissionDialog() {
499 // Request the read phone state permission. There is only one permission request in the app, so it has a request code of 0.
500 ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 0)
503 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
504 // Run the default commands.
505 super.onRequestPermissionsResult(requestCode, permissions, grantResults)
507 //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).
508 if (grantResults.isNotEmpty()) {
509 // Check to see if the read phone state permission was granted. If the dialog was canceled the grant results will be empty.
510 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { // The read phone state permission was granted.
511 // Populate Privacy Cell.
512 registerTelephonyManagerListener()
513 } else { // The read phone state permission was denied.
514 // Display the phone permission text on the main activity.
515 summaryTextView.text = getString(R.string.phone_permission_text)
520 private fun registerTelephonyManagerListener() {
521 // Get a handle for the telephony manager.
522 val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
524 // Listen to changes in the cell network state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31.
525 telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED or PhoneStateListener.LISTEN_SERVICE_STATE)
528 private fun getNetworkType(networkType: Int) : Array<String> {
529 // Return the string that corresponds to the network type.
530 return when(networkType) {
531 TelephonyManager.NETWORK_TYPE_UNKNOWN -> arrayOf(getString(R.string.unknown), "")
532 TelephonyManager.NETWORK_TYPE_GPRS -> arrayOf(getString(R.string.gprs), getString(R.string.gprs_detail))
533 TelephonyManager.NETWORK_TYPE_EDGE -> arrayOf(getString(R.string.edge), getString(R.string.edge_detail))
534 TelephonyManager.NETWORK_TYPE_UMTS -> arrayOf(getString(R.string.umts), getString(R.string.umts_detail))
535 TelephonyManager.NETWORK_TYPE_CDMA -> arrayOf(getString(R.string.cdma), getString(R.string.cdma_detail))
536 TelephonyManager.NETWORK_TYPE_EVDO_0 -> arrayOf(getString(R.string.evdo_0), getString(R.string.evdo_0_detail))
537 TelephonyManager.NETWORK_TYPE_EVDO_A -> arrayOf(getString(R.string.evdo_a), getString(R.string.evdo_a_detail))
538 TelephonyManager.NETWORK_TYPE_1xRTT -> arrayOf(getString(R.string.rtt), getString(R.string.rtt_detail))
539 TelephonyManager.NETWORK_TYPE_HSDPA -> arrayOf(getString(R.string.hsdpa), getString(R.string.hsdpa_detail))
540 TelephonyManager.NETWORK_TYPE_HSUPA -> arrayOf(getString(R.string.hsupa), getString(R.string.hsupa_detail))
541 TelephonyManager.NETWORK_TYPE_HSPA -> arrayOf(getString(R.string.hspa), getString(R.string.hspa_detail))
542 TelephonyManager.NETWORK_TYPE_IDEN -> arrayOf(getString(R.string.iden), getString(R.string.iden_detail))
543 TelephonyManager.NETWORK_TYPE_EVDO_B -> arrayOf(getString(R.string.evdo_b), getString(R.string.evdo_b_detail))
544 TelephonyManager.NETWORK_TYPE_LTE -> arrayOf(getString(R.string.lte), getString(R.string.lte_detail))
545 TelephonyManager.NETWORK_TYPE_EHRPD -> arrayOf(getString(R.string.ehrpd), getString(R.string.ehrpd_detail))
546 TelephonyManager.NETWORK_TYPE_HSPAP -> arrayOf(getString(R.string.hspap), getString(R.string.hspap_detail))
547 TelephonyManager.NETWORK_TYPE_GSM -> arrayOf(getString(R.string.gsm), getString(R.string.gsm_detail))
548 TelephonyManager.NETWORK_TYPE_TD_SCDMA -> arrayOf(getString(R.string.td_scdma), getString(R.string.td_scdma_detail))
549 TelephonyManager.NETWORK_TYPE_IWLAN -> arrayOf(getString(R.string.iwlan), getString(R.string.iwlan_detail))
550 TelephonyManager.NETWORK_TYPE_NR -> arrayOf(getString(R.string.nr), getString(R.string.nr_detail))
551 else -> arrayOf(getString(R.string.error), "")
555 private fun getAdditionalNetworkInfo(overrideNetworkType: Int) : Array<String> {
556 // Return the string that corresponds to the override network type.
557 return when(overrideNetworkType) {
558 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE -> arrayOf(getString(R.string.none), "")
559 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA -> arrayOf(getString(R.string.lte_ca), getString(R.string.lte_ca_detail))
560 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO -> arrayOf(getString(R.string.lte_advanced_pro), getString(R.string.lte_advanced_pro_detail))
561 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_NSA -> arrayOf(getString(R.string.nr_nsa), getString(R.string.nr_nsa_detail))
562 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.
563 TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NR_ADVANCED -> arrayOf(getString(R.string.nr_advanced), getString(R.string.nr_advanced_detail))
564 else -> arrayOf(getString(R.string.error), "")