X-Git-Url: https://gitweb.stoutner.com/?p=PrivacyCell.git;a=blobdiff_plain;f=app%2Fsrc%2Fmain%2Fjava%2Fcom%2Fstoutner%2Fprivacycell%2Fservices%2FRealtimeMonitoringService.kt;h=a48757f987f6cc4fc45b28e3fae6604d91a17c55;hp=681a3da2b41ccb1c8f0d88f30d685533a19c790b;hb=7467e75bda34246c91c4e4989b1c32a81314d53c;hpb=fb7c0422487bfb7cbb65427468c2688c24b4b99b diff --git a/app/src/main/java/com/stoutner/privacycell/services/RealtimeMonitoringService.kt b/app/src/main/java/com/stoutner/privacycell/services/RealtimeMonitoringService.kt index 681a3da..a48757f 100644 --- a/app/src/main/java/com/stoutner/privacycell/services/RealtimeMonitoringService.kt +++ b/app/src/main/java/com/stoutner/privacycell/services/RealtimeMonitoringService.kt @@ -1,5 +1,5 @@ /* - * Copyright © 2021 Soren Stoutner . + * Copyright © 2021-2022 Soren Stoutner . * * This file is part of Privacy Cell . * @@ -17,8 +17,12 @@ * along with Privacy Cell. If not, see . */ +// The suppression of deprecation lint can be removed once the minimum API >= 31. +@file:Suppress("DEPRECATION") + package com.stoutner.privacycell.services +import android.Manifest import android.app.Notification import android.app.NotificationChannel import android.app.NotificationChannelGroup @@ -27,19 +31,22 @@ import android.app.PendingIntent import android.app.Service import android.content.Context import android.content.Intent +import android.content.pm.PackageManager import android.os.Binder import android.os.IBinder -import android.telephony.PhoneStateListener +import android.telephony.PhoneStateListener // This can be replaced by `TelephonyCallback` once the minimum API >= 31. import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyManager +import androidx.core.app.ActivityCompat +import androidx.preference.PreferenceManager import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.PeriodicWorkRequestBuilder import androidx.work.WorkManager import com.stoutner.privacycell.R import com.stoutner.privacycell.activities.PrivacyCellActivity -import com.stoutner.privacycell.workers.RegisterRealtimeListener +import com.stoutner.privacycell.workers.RegisterRealtimeListenerWorker import java.util.concurrent.TimeUnit @@ -53,11 +60,12 @@ class RealtimeMonitoringService : Service() { // Define the public constants. These are used in the settings fragment to launch intents to edit the sound that plays for each channel. const val SECURE_NETWORK = "secure_network" const val INSECURE_NETWORK = "insecure_network" + const val ANTIQUATED_NETWORK = "antiquated_network" } // Define the class variables. private var currentStatus = "" - private lateinit var phoneStateListener: PhoneStateListener + private lateinit var phoneStateListener: PhoneStateListener // The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31. inner class ServiceBinder : Binder() { // Get a copy of this service as a binder. @@ -70,6 +78,9 @@ class RealtimeMonitoringService : Service() { } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + // Get a handle for the shared preferences. + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this) + // Get a handle for the notification manager. val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -79,25 +90,30 @@ class RealtimeMonitoringService : Service() { // Prepare the notification channels. val secureNetworkChannel = NotificationChannel(SECURE_NETWORK, getString(R.string.secure_network_channel), NotificationManager.IMPORTANCE_HIGH) val insecureNetworkChannel = NotificationChannel(INSECURE_NETWORK, getString(R.string.insecure_network_channel), NotificationManager.IMPORTANCE_HIGH) + val antiquatedNetworkChannel = NotificationChannel(ANTIQUATED_NETWORK, getString(R.string.antiquated_network_channel), NotificationManager.IMPORTANCE_HIGH) val unknownNetworkChannel = NotificationChannel(UNKNOWN_NETWORK, getString(R.string.unknown_network_channel), NotificationManager.IMPORTANCE_LOW) // Set the notification channel group. secureNetworkChannel.group = REALTIME_MONITORING insecureNetworkChannel.group = REALTIME_MONITORING + antiquatedNetworkChannel.group = REALTIME_MONITORING unknownNetworkChannel.group = REALTIME_MONITORING // Disable the notification dots. secureNetworkChannel.setShowBadge(false) insecureNetworkChannel.setShowBadge(false) + antiquatedNetworkChannel.setShowBadge(false) unknownNetworkChannel.setShowBadge(false) - // Set the notifications to be public for the secure and insecure networks. + // Set the primary channel notifications to be public. secureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC insecureNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC + antiquatedNetworkChannel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC // Create the notification channels. notificationManager.createNotificationChannel(secureNetworkChannel) notificationManager.createNotificationChannel(insecureNetworkChannel) + notificationManager.createNotificationChannel(antiquatedNetworkChannel) notificationManager.createNotificationChannel(unknownNetworkChannel) // Create a notification builder. @@ -116,19 +132,23 @@ class RealtimeMonitoringService : Service() { notificationBuilder.setContentText(getString(R.string.unknown_network)) // Set the notification icon. - notificationBuilder.setSmallIcon(R.drawable.insecure_notification) + notificationBuilder.setSmallIcon(R.drawable.antiquated_notification_enabled) // Set the color. - notificationBuilder.setColor(getColor(R.color.red_text)) + notificationBuilder.setColor(getColor(R.color.red_notification_icon)) // Start the foreground notification. startForeground(NOTIFICATION_ID, notificationBuilder.build()) - // Define the phone state listener. + // Define the phone state listener. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31. phoneStateListener = object : PhoneStateListener() { override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo) { + // Get the consider 3G antiquated preference. + val consider3gAntiquated = sharedPreferences.getBoolean(getString(R.string.consider_3g_antiquated_key), false) + // Populate the notification according to the network type. - if (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) { // This is a secure 5G NR SA network. + if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_NR) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_IWLAN) || + (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UNKNOWN)) { // This is a secure network. // Only update the notification if the network status has changed. if (currentStatus != SECURE_NETWORK) { // Create a secure network notification builder. @@ -141,10 +161,10 @@ class RealtimeMonitoringService : Service() { secureNetworkNotificationBuilder.setContentText(getString(R.string.secure_network)) // Set the notification icon. - secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification) + secureNetworkNotificationBuilder.setSmallIcon(R.drawable.secure_notification_enabled) // Set the color. - secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_text)) + secureNetworkNotificationBuilder.setColor(getColor(R.color.blue_icon)) // Update the notification. notificationManager.notify(NOTIFICATION_ID, secureNetworkNotificationBuilder.build()) @@ -152,7 +172,13 @@ class RealtimeMonitoringService : Service() { // Store the new network status. currentStatus = SECURE_NETWORK } - } else { // This is not a secure 5G NR SA network. + } else if ((telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_LTE) || (!consider3gAntiquated && (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_1xRTT || + (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_0) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_A) || + (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EVDO_B) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_EHRPD) || + (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_UMTS) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_TD_SCDMA) || + (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSDPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSUPA) || + (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPA) || (telephonyDisplayInfo.networkType == TelephonyManager.NETWORK_TYPE_HSPAP)))) { + // This is an insecure network. // Only update the notification if the network status has changed. if (currentStatus != INSECURE_NETWORK) { // Create an insecure network notification builder. @@ -165,10 +191,10 @@ class RealtimeMonitoringService : Service() { insecureNetworkNotificationBuilder.setContentText(getString(R.string.insecure_network)) // Set the notification icon. - insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification) + insecureNetworkNotificationBuilder.setSmallIcon(R.drawable.insecure_notification_enabled) // Set the color. - insecureNetworkNotificationBuilder.setColor(getColor(R.color.red_text)) + insecureNetworkNotificationBuilder.setColor(getColor(R.color.yellow_notification_icon)) // Update the notification. notificationManager.notify(NOTIFICATION_ID, insecureNetworkNotificationBuilder.build()) @@ -176,29 +202,59 @@ class RealtimeMonitoringService : Service() { // Store the new network status. currentStatus = INSECURE_NETWORK } + } else { // This is an antiquated network. + // Only update the notification if the network status has changed. + if (currentStatus != ANTIQUATED_NETWORK) { + // Create an antiquated network notification builder. + val antiquatedNetworkNotificationBuilder = Notification.Builder(applicationContext, ANTIQUATED_NETWORK) + + // Set the notification to open Privacy Cell. + antiquatedNetworkNotificationBuilder.setContentIntent(privacyCellPendingIntent) + + // Set the notification text. + antiquatedNetworkNotificationBuilder.setContentText(getString(R.string.antiquated_network)) + + // Set the notification icon. + antiquatedNetworkNotificationBuilder.setSmallIcon(R.drawable.antiquated_notification_enabled) + + // Set the color. + antiquatedNetworkNotificationBuilder.setColor(getColor(R.color.red_notification_icon)) + + // Update the notification. + notificationManager.notify(NOTIFICATION_ID, antiquatedNetworkNotificationBuilder.build()) + + // Store the new network status. + currentStatus = ANTIQUATED_NETWORK + } } } } - // Create a register realtime listener work request that fires every hour. For some reason, when the service launches it will initially register the listener and then unregister it. - // This periodic request will fire shortly thereafter (it fires about every hour near the beginning of the hour) and will reregister the listener, which will stick this time. - val registerRealtimeListenerWorkRequest = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS).build() + // Check to see if the read phone state permission has been granted. + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { + // Create a register realtime listener work request that fires every hour. + // This periodic request will fire shortly after being created (it fires about every hour near the beginning of the hour) and will reregister the listener if it gets garbage collected. + val registerRealtimeListenerWorkRequest = PeriodicWorkRequestBuilder(1, TimeUnit.HOURS).build() - // Register the realtime listener work request. - WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest) + // Register the realtime listener work request. + WorkManager.getInstance(this).enqueueUniquePeriodicWork(getString(R.string.register_listener_work_request), ExistingPeriodicWorkPolicy.REPLACE, registerRealtimeListenerWorkRequest) + } // Return a sticky service. return START_STICKY } fun registerTelephonyManagerListener() { - // Get a handle for the telephony manager. - val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + // Check to see if the read phone state permission has been granted. + if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) { + // Get a handle for the telephony manager. + val telephonyManager = getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager - // Cancel the current listener if it exists. - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) + // Cancel the current listener if it exists. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31. + telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_NONE) - // Listen for changes to the phone state. - telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) + // Listen for changes to the phone state. The `PhoneStateListener` can be replaced by `TelephonyCallback` once the minimum API >= 31. + telephonyManager.listen(phoneStateListener, PhoneStateListener.LISTEN_DISPLAY_INFO_CHANGED) + } } } \ No newline at end of file