]> gitweb.stoutner.com Git - PrivacyCell.git/blob - app/src/main/java/com/stoutner/privacycell/fragments/SettingsFragment.kt
Disable swiping to dismiss realtime notifications. https://redmine.stoutner.com/issue...
[PrivacyCell.git] / app / src / main / java / com / stoutner / privacycell / fragments / SettingsFragment.kt
1 /*
2  * Copyright 2021-2022 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Cell <https://www.stoutner.com/privacy-cell>.
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
20 package com.stoutner.privacycell.fragments
21
22 import android.Manifest
23 import android.content.Intent
24 import android.content.SharedPreferences
25 import android.content.SharedPreferences.OnSharedPreferenceChangeListener
26 import android.content.pm.PackageManager
27 import android.os.Build
28 import android.os.Bundle
29 import android.os.Handler
30 import android.os.Looper
31 import android.provider.Settings
32
33 import androidx.core.app.ActivityCompat
34 import androidx.preference.Preference
35 import androidx.preference.PreferenceFragmentCompat
36 import androidx.work.WorkManager
37
38 import com.stoutner.privacycell.R
39 import com.stoutner.privacycell.activities.PrivacyCellActivity
40 import com.stoutner.privacycell.dialogs.NotificationPermissionDialog
41 import com.stoutner.privacycell.services.RealtimeMonitoringService
42
43 class SettingsFragment : PreferenceFragmentCompat() {
44     // Declare the class variables.
45     private lateinit var sharedPreferenceChangeListener: OnSharedPreferenceChangeListener
46
47     // Declare the class views.
48     private lateinit var realtimeMonitoringPreference: Preference
49     private lateinit var secureNetworkNotificationPreference: Preference
50     private lateinit var insecureNetworkNotificationPreference: Preference
51     private lateinit var antiquatedNetworkNotificationPreference: Preference
52     private lateinit var consider3gAntiquatedPreference: Preference
53     private lateinit var bottomAppBarPreference: Preference
54
55     override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
56         // Load the preferences from the XML file.
57         setPreferencesFromResource(R.xml.preferences, rootKey)
58
59         // Get a handle for the shared preferences.
60         val sharedPreferences = preferenceScreen.sharedPreferences!!
61
62         // Get handles for the preferences.
63         realtimeMonitoringPreference = findPreference(getString(R.string.realtime_monitoring_key))!!
64         secureNetworkNotificationPreference = findPreference(getString(R.string.secure_network_notification_key))!!
65         insecureNetworkNotificationPreference = findPreference(getString(R.string.insecure_network_notification_key))!!
66         antiquatedNetworkNotificationPreference = findPreference(getString(R.string.antiquated_network_notification_key))!!
67         consider3gAntiquatedPreference = findPreference(getString(R.string.consider_3g_antiquated_key))!!
68         bottomAppBarPreference = findPreference(getString(R.string.bottom_app_bar_key))!!
69
70         // Only enable the realtime monitoring preference if the READ_PHONE_STATE permission has been granted.
71         realtimeMonitoringPreference.isEnabled = (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED)
72
73         // Set the realtime monitoring icon according to the status.
74         if (realtimeMonitoringPreference.isEnabled) {
75             // Set the realtime monitoring preference icon.
76             if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) {
77                 // Set the icons.
78                 realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_enabled)
79                 secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_enabled)
80                 insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_enabled)
81                 antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_enabled)
82             } else {
83                 // Set the icons.
84                 realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_disabled)
85                 secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_disabled)
86                 insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_disabled)
87                 antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_disabled)
88             }
89         } else {
90             // Set the icons.
91             realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_ghosted)
92             secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_ghosted)
93             insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_ghosted)
94             antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_ghosted)
95         }
96
97         // Set the notification preferences to depend on the realtime monitoring preference.
98         secureNetworkNotificationPreference.dependency = getString(R.string.realtime_monitoring_key)
99         insecureNetworkNotificationPreference.dependency = getString(R.string.realtime_monitoring_key)
100         antiquatedNetworkNotificationPreference.dependency = getString(R.string.realtime_monitoring_key)
101
102         // Create the notification intents.
103         val secureNetworkNotificationIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
104             .putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
105             .putExtra(Settings.EXTRA_CHANNEL_ID, RealtimeMonitoringService.SECURE_NETWORK)
106         val insecureNetworkNotificationIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
107             .putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
108             .putExtra(Settings.EXTRA_CHANNEL_ID, RealtimeMonitoringService.INSECURE_NETWORK)
109         val antiquatedNetworkNotificationIntent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
110             .putExtra(Settings.EXTRA_APP_PACKAGE, requireContext().packageName)
111             .putExtra(Settings.EXTRA_CHANNEL_ID, RealtimeMonitoringService.ANTIQUATED_NETWORK)
112
113         // Set the notification preference intents.
114         secureNetworkNotificationPreference.intent = secureNetworkNotificationIntent
115         insecureNetworkNotificationPreference.intent = insecureNetworkNotificationIntent
116         antiquatedNetworkNotificationPreference.intent = antiquatedNetworkNotificationIntent
117
118         // Set the consider 3G antiquated preference icon.
119         if (sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)) {
120             consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_enabled)
121         } else {
122             consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_disabled)
123         }
124
125         // Set the bottom app bar preference icon.
126         if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)) {
127             bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled)
128         } else {
129             bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled)
130         }
131     }
132
133     // The listener should be unregistered when the app is paused.
134     override fun onPause() {
135         // Run the default commands.
136         super.onPause()
137
138         // Get a handle for the shared preferences.
139         val sharedPreferences = preferenceScreen.sharedPreferences!!
140
141         // Unregister the shared preference listener.
142         sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
143     }
144
145     // The listener should be re-registered when the app is resumed.
146     override fun onResume() {
147         // Run the default commands.
148         super.onResume()
149
150         // Get a new shared preference change listener.
151         sharedPreferenceChangeListener = getSharedPreferenceChangeListener()
152
153         // Get a handle for the shared preferences.
154         val sharedPreferences = preferenceScreen.sharedPreferences!!
155
156         // Re-register the shared preference listener.
157         sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener)
158
159         // Update the realtime monitoring preference summary.
160         updateRealtimeMonitoringSummary()
161     }
162
163     private fun getSharedPreferenceChangeListener(): OnSharedPreferenceChangeListener {
164         // Return the shared preference change listener.
165         return OnSharedPreferenceChangeListener { sharedPreferences: SharedPreferences, key: String? ->
166             when (key) {
167                 getString(R.string.realtime_monitoring_key) -> {
168                     // Update the icon.
169                     if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) {
170                         // Set the icons.
171                         realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_enabled)
172                         secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_enabled)
173                         insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_enabled)
174                         antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_enabled)
175                     } else {
176                         // Set the icons.
177                         realtimeMonitoringPreference.setIcon(R.drawable.secure_notification_disabled)
178                         secureNetworkNotificationPreference.setIcon(R.drawable.secure_notification_disabled)
179                         insecureNetworkNotificationPreference.setIcon(R.drawable.insecure_notification_disabled)
180                         antiquatedNetworkNotificationPreference.setIcon(R.drawable.antiquated_notification_disabled)
181                     }
182
183                     // Start or stop the service.
184                     if (sharedPreferences.getBoolean(getString(R.string.realtime_monitoring_key), false)) {  // Realtime monitoring has been enabled.
185                         // Start the service according to the API.
186                         if (Build.VERSION.SDK_INT >= 33) {  // The device API is >= 33.
187                             // Check to see if the post notification has been granted.
188                             if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {  // The permission has been granted.
189                                 // Start the realtime monitoring service.
190                                 requireActivity().startService(Intent(context, RealtimeMonitoringService::class.java))
191                             } else {  // The post notification permission has not been granted.
192                                 // Check if the user has previously denied the post notifications permission.
193                                 if (ActivityCompat.shouldShowRequestPermissionRationale(requireActivity(), Manifest.permission.POST_NOTIFICATIONS)) {  // Show a dialog explaining the request first.
194                                     // Check to see if a notification permission dialog is already displayed.  This happens if the app is restarted while the dialog is shown.
195                                     if (requireActivity().supportFragmentManager.findFragmentByTag(getString(R.string.notification_permission)) == null) {  // No dialog is currently shown.
196                                         // Instantiate the notification permission dialog fragment.
197                                         val notificationPermissionDialogFragment = NotificationPermissionDialog()
198
199                                         // Show the notification permission alert dialog.  The permission will be requested when the dialog is closed.
200                                         notificationPermissionDialogFragment.show(requireActivity().supportFragmentManager, getString(R.string.notification_permission))
201                                     }
202                                 } else {  // Show the permission request directly.
203                                     // Request the post notifications permission directly.
204                                     ActivityCompat.requestPermissions(requireActivity(), arrayOf(Manifest.permission.POST_NOTIFICATIONS), PrivacyCellActivity.NOTIFICATION_PERMISSION_REQUEST_CODE)
205                                 }
206                             }
207                         } else {   // The device API is < 33.
208                             // Start the realtime monitoring service.
209                             requireActivity().startService(Intent(context, RealtimeMonitoringService::class.java))
210                         }
211                     } else {  // Realtime monitoring has been disabled.
212                         // Stop the realtime monitoring service.
213                         requireActivity().stopService(Intent(context, RealtimeMonitoringService::class.java))
214
215                         // Cancel the realtime listener work request.
216                         WorkManager.getInstance(requireContext()).cancelUniqueWork(getString(R.string.register_listener_work_request))
217                     }
218                 }
219
220                 getString(R.string.consider_3g_antiquated_key) -> {
221                     // Update the icon.
222                     if (sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false)) {
223                         consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_enabled)
224                     } else {
225                         consider3gAntiquatedPreference.setIcon(R.drawable.antiquated_3g_disabled)
226                     }
227
228                     // Restart Privacy Cell.
229                     restartPrivacyCell()
230                 }
231
232                 getString(R.string.bottom_app_bar_key) -> {
233                     // Update the icon.
234                     if (sharedPreferences.getBoolean(getString(R.string.bottom_app_bar_key), false)) {
235                         bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_enabled)
236                     } else {
237                         bottomAppBarPreference.setIcon(R.drawable.bottom_app_bar_disabled)
238                     }
239
240                     // Restart Privacy Cell after 400 milliseconds.
241                     restartPrivacyCell()
242                 }
243             }
244         }
245     }
246
247     private fun restartPrivacyCell() {
248         // Create an intent to restart Privacy Cell.
249         val restartIntent = requireActivity().parentActivityIntent!!
250
251         // `Intent.FLAG_ACTIVITY_CLEAR_TASK` removes all activities from the stack.  It requires `Intent.FLAG_ACTIVITY_NEW_TASK`.
252         restartIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
253
254         // Create a handler to restart the activity.
255         val restartHandler = Handler(Looper.getMainLooper())
256
257         // Create a runnable to restart the activity.
258         val restartRunnable = Runnable {
259             // Restart the activity.
260             startActivity(restartIntent)
261         }
262
263         // Restart the activity after 400 milliseconds, so that the app has enough time to save the change to the preference.
264         restartHandler.postDelayed(restartRunnable, 400)
265     }
266
267     fun updateRealtimeMonitoringSummary() {
268         // Update the summary according to the API.
269         if (Build.VERSION.SDK_INT >= 33) {  // The API >= 33.
270             // Set the summary according to the notification permission status.
271             if (ActivityCompat.checkSelfPermission(requireContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED)
272                 realtimeMonitoringPreference.summary = getString(R.string.realtime_monitoring_summary)
273             else
274                 realtimeMonitoringPreference.summary = (getString(R.string.realtime_monitoring_summary) + " " + getString(R.string.notification_permission_denied))
275         } else {  // The API is < 33.
276             // Set the realtime monitoring summary.
277             realtimeMonitoringPreference.summary = getString(R.string.realtime_monitoring_summary)
278         }
279     }
280 }