X-Git-Url: https://gitweb.stoutner.com/?a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacycell%2Ffragments%2FSettingsFragment.kt;h=dc8545ffb0abcfa96225bc19f86dcb6f29b250b9;hb=81346128a1dcde5b65d83f24c1af507cd80802d3;hp=3708bc225716db77487878f5a1be766cdea89218;hpb=dc76c6f07fd9a5718049d160e079842fa60ff406;p=PrivacyCell.git diff --git a/app/src/main/java/com/stoutner/privacycell/fragments/SettingsFragment.kt b/app/src/main/java/com/stoutner/privacycell/fragments/SettingsFragment.kt index 3708bc2..dc8545f 100644 --- a/app/src/main/java/com/stoutner/privacycell/fragments/SettingsFragment.kt +++ b/app/src/main/java/com/stoutner/privacycell/fragments/SettingsFragment.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Soren Stoutner . + * Copyright 2021-2022 Soren Stoutner . * * This file is part of Privacy Cell . * @@ -22,7 +22,9 @@ package com.stoutner.privacycell.fragments import android.Manifest import android.content.Intent import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.os.Handler import android.os.Looper @@ -34,16 +36,31 @@ import androidx.preference.PreferenceFragmentCompat import androidx.work.WorkManager import com.stoutner.privacycell.R +import com.stoutner.privacycell.activities.PrivacyCellActivity +import com.stoutner.privacycell.dialogs.NotificationPermissionDialog import com.stoutner.privacycell.services.RealtimeMonitoringService +// Define the class constants. +private const val SCROLL_Y = "scroll_y" + class SettingsFragment : PreferenceFragmentCompat() { // Declare the class variables. - private lateinit var sharedPreferenceChangeListener: SharedPreferences.OnSharedPreferenceChangeListener + private lateinit var sharedPreferenceChangeListener: OnSharedPreferenceChangeListener // Declare the class views. private lateinit var realtimeMonitoringPreference: Preference + private lateinit var secureNetworkNotificationPreference: Preference + private lateinit var insecureNetworkNotificationPreference: Preference + private lateinit var antiquatedNetworkNotificationPreference: Preference + private lateinit var consider3gAntiquatedPreference: Preference private lateinit var bottomAppBarPreference: Preference + companion object { + // Declare the private static class variables. Otherwise, onRestart will not pull the same values that are populated from the saved instance state. + private var fragmentRestarted: Boolean = false + private var scrollY: Int = 0 + } + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { // Load the preferences from the XML file. setPreferencesFromResource(R.xml.preferences, rootKey) @@ -53,8 +70,10 @@ class SettingsFragment : PreferenceFragmentCompat() { // Get handles for the preferences. realtimeMonitoringPreference = findPreference(getString(R.string.realtime_monitoring_key))!! - val secureNetworkNotificationPreference = findPreference(getString(R.string.secure_network_notification_key))!! - val insecureNetworkNotificationPreference = findPreference(getString(R.string.insecure_network_notification_key))!! + secureNetworkNotificationPreference = findPreference(getString(R.string.secure_network_notification_key))!! + insecureNetworkNotificationPreference = findPreference(getString(R.string.insecure_network_notification_key))!! + antiquatedNetworkNotificationPreference = findPreference(getString(R.string.antiquated_network_notification_key))!! + consider3gAntiquatedPreference = findPreference(getString(R.string.consider_3g_antiquated_key))!! bottomAppBarPreference = findPreference(getString(R.string.bottom_app_bar_key))!! // Only enable the realtime monitoring preference if the READ_PHONE_STATE permission has been granted. @@ -64,20 +83,30 @@ class SettingsFragment : PreferenceFragmentCompat() { if (realtimeMonitoringPreference.isEnabled) { // Set the realtime monitoring preference icon. if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) { - // Set the enabled icon. - realtimeMonitoringPreference.setIcon(R.drawable.realtime_monitoring_enabled) + // Set the icons. + realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_enabled) + secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_enabled) + insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_enabled) + antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_enabled) } else { - // Set the disabled icon. - realtimeMonitoringPreference.setIcon(R.drawable.realtime_monitoring_disabled) + // Set the icons. + realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_disabled) + secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_disabled) + insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_disabled) + antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_disabled) } } else { - // Set the ghosted icon. - realtimeMonitoringPreference.setIcon(R.drawable.realtime_monitoring_ghosted) + // Set the icons. + realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_ghosted) + secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_ghosted) + insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_ghosted) + antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_ghosted) } // Set the notification preferences to depend on the realtime monitoring preference. secureNetworkNotificationPreference.dependency = getString(R.string.realtime_monitoring_key) insecureNetworkNotificationPreference.dependency = getString(R.string.realtime_monitoring_key) + antiquatedNetworkNotificationPreference.dependency = getString(R.string.realtime_monitoring_key) // Create the notification intents. val secureNetworkNotificationIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) @@ -86,19 +115,121 @@ class SettingsFragment : PreferenceFragmentCompat() { val insecureNetworkNotificationIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) .putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName) .putExtra(Settings.EXTRA_CHANNEL_ID, RealtimeMonitoringService.INSECURE_NETWORK) + val antiquatedNetworkNotificationIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS) + .putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName) + .putExtra(Settings.EXTRA_CHANNEL_ID, RealtimeMonitoringService.ANTIQUATED_NETWORK) // Set the notification preference intents. secureNetworkNotificationPreference.intent = secureNetworkNotificationIntent insecureNetworkNotificationPreference.intent = insecureNetworkNotificationIntent + antiquatedNetworkNotificationPreference.intent = antiquatedNetworkNotificationIntent + + // Set the consider 3G antiquated preference icon. + if (sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)) { + consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_enabled) + } else { + consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_disabled) + } // Set the bottom app bar preference icon. if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)) { - // Set the enabled icon. bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled) } else { - // Set the disabled icon. bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled) } + + // Check if the fragment has been restarted. + if (savedInstanceState != null) { + // Set the fragment restored flag. + fragmentRestarted = true + + // Save the scroll Y. + scrollY = savedInstanceState.getInt(SCROLL_Y) + } + } + + private fun getSharedPreferenceChangeListener(): OnSharedPreferenceChangeListener { + // Return the shared preference change listener. + return OnSharedPreferenceChangeListener { sharedPreferences: SharedPreferences, key: String? -> + when (key) { + getString(R.string.realtime_monitoring_key) -> { + // Update the icon. + if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) { + // Set the icons. + realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_enabled) + secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_enabled) + insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_enabled) + antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_enabled) + } else { + // Set the icons. + realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_disabled) + secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_disabled) + insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_disabled) + antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_disabled) + } + + // Start or stop the service. + if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) { // Realtime monitoring has been enabled. + // Start the service according to the API. + if (Build.VERSION.SDK_INT >= 33) { // The device API is >= 33. + // Check to see if the post notification has been granted. + if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) { // The permission has been granted. + // Start the realtime monitoring service. + requireActivity().startService(Intent(context, RealtimeMonitoringService::class.java)) + } else { // The post notification permission has not been granted. + // Check if the user has previously denied the post notifications permission. + if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.POST_NOTIFICATIONS)) { // Show a dialog explaining the request first. + // Check to see if a notification permission dialog is already displayed. This happens if the app is restarted while the dialog is shown. + if (requireActivity().supportFragmentManager.findFragmentByTag(getString(R.string.notification_permission)) == null) { // No dialog is currently shown. + // Instantiate the notification permission dialog fragment. + val notificationPermissionDialogFragment = NotificationPermissionDialog() + + // Show the notification permission alert dialog. The permission will be requested when the dialog is closed. + notificationPermissionDialogFragment.show(requireActivity().supportFragmentManager, getString(R.string.notification_permission)) + } + } else { // Show the permission request directly. + // Request the post notifications permission directly. + ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.POST_NOTIFICATIONS), PrivacyCellActivity.NOTIFICATION_PERMISSION_REQUEST_CODE) + } + } + } else { // The device API is < 33. + // Start the realtime monitoring service. + requireActivity().startService(Intent(context, RealtimeMonitoringService::class.java)) + } + } else { // Realtime monitoring has been disabled. + // Stop the realtime monitoring service. + requireActivity().stopService(Intent(context, RealtimeMonitoringService::class.java)) + + // Cancel the realtime listener work request. + WorkManager.getInstance(requireContext()).cancelUniqueWork(getString(R.string.register_listener_work_request)) + } + } + + getString(R.string.consider_3g_antiquated_key) -> { + // Update the icon. + if (sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)) { + consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_enabled) + } else { + consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_disabled) + } + + // Restart Privacy Cell. + restartPrivacyCell() + } + + getString(R.string.bottom_app_bar_key) -> { + // Update the icon. + if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)) { + bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled) + } else { + bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled) + } + + // Restart Privacy Cell after 400 milliseconds. + restartPrivacyCell() + } + } + } } // The listener should be unregistered when the app is paused. @@ -126,64 +257,59 @@ class SettingsFragment : PreferenceFragmentCompat() { // Re-register the shared preference listener. sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener) - } - private fun getSharedPreferenceChangeListener(): SharedPreferences.OnSharedPreferenceChangeListener { - // Return the shared preference change listener. - return SharedPreferences.OnSharedPreferenceChangeListener {sharedPreferences, key -> - when (key) { - getString(R.string.realtime_monitoring_key) -> { - // Update the icon. - if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) { - // Set the enabled icon. - realtimeMonitoringPreference.setIcon(R.drawable.realtime_monitoring_enabled) - } else { - // Set the disabled icon. - realtimeMonitoringPreference.setIcon(R.drawable.realtime_monitoring_disabled) - } + // Update the realtime monitoring preference summary. + updateRealtimeMonitoringSummary() - // Start or stop the service. - if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) { // Realtime monitoring has been enabled. - // Start the realtime monitoring service. - requireActivity().startService(Intent(context, RealtimeMonitoringService::class.java)) - } else { // Realtime monitoring has been disabled. - // Stop the realtime monitoring service. - requireActivity().stopService(Intent(context, RealtimeMonitoringService::class.java)) + // Restore the scroll position if the fragment has been restarted. + if (fragmentRestarted) { + // Reset the fragment restarted flag. + fragmentRestarted = false - // Cancel the realtime listener work request. - WorkManager.getInstance(requireContext()).cancelUniqueWork(getString(R.string.register_listener_work_request)) - } - } + // Set the scroll position. + listView.smoothScrollBy(0, scrollY) + } + } - getString(R.string.bottom_app_bar_key) -> { - // Update the icon. - if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)) { - // Set the enabled icon. - bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled) - } else { - // Set the disabled icon. - bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled) - } + override fun onSaveInstanceState(savedInstanceState: Bundle) { + // Run the default commands. + super.onSaveInstanceState(savedInstanceState) - // Create an intent to restart Privacy Cell. - val restartIntent = requireActivity().parentActivityIntent!! + // Save the scroll position. + savedInstanceState.putInt(SCROLL_Y, listView.computeVerticalScrollOffset()) + } - // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. - restartIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + private fun restartPrivacyCell() { + // Create an intent to restart Privacy Cell. + val restartIntent = requireActivity().parentActivityIntent!! - // Create a handler to restart the activity. - val restartHandler = Handler(Looper.getMainLooper()) + // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack. It requires `Intent.FLAG_ACTIVITY_NEW_TASK`. + restartIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - // Create a runnable to restart the activity. - val restartRunnable = Runnable { - // Restart the activity. - startActivity(restartIntent) - } + // Create a handler to restart the activity. + val restartHandler = Handler(Looper.getMainLooper()) - // Restart the activity after 400 milliseconds, so that the app has enough time to save the change to the preference. - restartHandler.postDelayed(restartRunnable, 400) - } - } + // Create a runnable to restart the activity. + val restartRunnable = Runnable { + // Restart the activity. + startActivity(restartIntent) + } + + // Restart the activity after 400 milliseconds, so that the app has enough time to save the change to the preference. + restartHandler.postDelayed(restartRunnable, 400) + } + + fun updateRealtimeMonitoringSummary() { + // Update the summary according to the API. + if (Build.VERSION.SDK_INT >= 33) { // The API >= 33. + // Set the summary according to the notification permission status. + if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) + realtimeMonitoringPreference.summary = getString(R.string.realtime_monitoring_summary) + else + realtimeMonitoringPreference.summary = (getString(R.string.realtime_monitoring_summary) + " " + getString(R.string.notification_permission_denied)) + } else { // The API is < 33. + // Set the realtime monitoring summary. + realtimeMonitoringPreference.summary = getString(R.string.realtime_monitoring_summary) } } -} \ No newline at end of file +}