From c6e8fadf9c8aab0934045e922ca5eed0c96d9183 Mon Sep 17 00:00:00 2001
From: Gert-Jan <138776253+Pruts-Hacker@users.noreply.github.com>
Date: Fri, 24 May 2024 11:11:42 +0200
Subject: [PATCH 1/2] Added SMS Notification Feature and Permission Request for
Silence/app
Summary
This update introduces a new feature to the Silence app that enhances how blocked calls are managed. It includes sending an SMS to blocked numbers and various user interface improvements to inform the user of blocked calls and necessary permissions.
Features and Improvements
SMS Notification to Blocked Numbers:
When a number is blocked, the app automatically sends an SMS to the blocked number.
The SMS requests the caller to send their name and reason for calling.
Permission Request:
Implemented a permission request prompt for the user to allow the app to send SMS messages.
Ensures compliance with user privacy and permission protocols.
User Notification:
Added a popup notification that prompts the user to check their messages whenever a call is blocked.
Enhances user awareness and ensures they are informed about the blocked calls and incoming messages.
Bug Fixes
None in this update.
UI/UX Changes
Popup notification added for informing the user about blocked calls and the need to check their messages.
Code Refactoring and Optimization
No major refactoring or optimization in this update.
Documentation
Updated the user guide to include information about the new SMS notification feature and permission request.
Added in-app tips to help users understand the new functionality.
Please review the changes and provide feedback. Looking forward to your comments and approval.
---
README.md | 1 +
app/src/main/AndroidManifest.xml | 1 +
.../java/me/lucky/silence/MainActivity.kt | 44 ++++++++++++++++++-
.../me/lucky/silence/NotificationManager.kt | 22 ++++++++++
.../silence/screening/CallScreeningService.kt | 3 ++
gradle/libs.versions.toml | 2 +-
gradle/wrapper/gradle-wrapper.properties | 2 +-
7 files changed, 72 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index f617e3c7..a69d33da 100644
--- a/README.md
+++ b/README.md
@@ -54,6 +54,7 @@ If the app rejects calls from contacts on Android 10, allow _Contacts_ permissio
* CALL_SCREENING - block or allow call
* READ_CALL_LOG - check you have called/answered the number and count times the number have called you in X minutes
* READ_SMS - check you have sent a message to the number and you received a message from the number
+* SEND_SMS - send a message to the number if the call is rejected
* NOTIFICATION_LISTENER - find mobile numbers in messages
* READ_PHONE_STATE - check on which SIM the number is calling
* RECEIVE_BOOT_COMPLETED - persist clean expired numbers job across reboots
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e306151f..cc5fd9e0 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,6 +6,7 @@
+
diff --git a/app/src/main/java/me/lucky/silence/MainActivity.kt b/app/src/main/java/me/lucky/silence/MainActivity.kt
index f53eab68..ee436e1a 100644
--- a/app/src/main/java/me/lucky/silence/MainActivity.kt
+++ b/app/src/main/java/me/lucky/silence/MainActivity.kt
@@ -1,5 +1,8 @@
package me.lucky.silence
+import android.Manifest
+import android.app.AlertDialog
+import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import androidx.activity.ComponentActivity
@@ -10,13 +13,26 @@ import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
import androidx.navigation.compose.rememberNavController
import me.lucky.silence.ui.App
+// Define a constant to identify the SEND_SMS permission request
+private const val MY_PERMISSIONS_REQUEST_SEND_SMS = 0
+
open class MainActivity : ComponentActivity() {
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NotificationManager(this).createNotificationChannels()
+
+ // Check and request SMS permission
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS) != PackageManager.PERMISSION_GRANTED) {
+ // If not, request the permission
+ ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.SEND_SMS), MY_PERMISSIONS_REQUEST_SEND_SMS)
+ }
+
setContent {
val isAndroid12OrLater = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val colorScheme = when {
@@ -30,4 +46,30 @@ open class MainActivity : ComponentActivity() {
}
}
}
-}
+
+ // This method is called when the user responds to the permission request
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ when (requestCode) {
+ // If the result is for the SEND_SMS permission request
+ MY_PERMISSIONS_REQUEST_SEND_SMS -> {
+ // If the permission is granted
+ if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
+ // Show a dialog to check SMS messages
+ AlertDialog.Builder(this)
+ .setTitle("Check SMS Messages")
+ .setMessage("Please check your SMS messages when you receive a notification.")
+ .setPositiveButton("OK") { dialog, _ ->
+ // Dismiss the dialog when the OK button is clicked
+ dialog.dismiss()
+ }
+ .show()
+ }
+ return
+ }
+ else -> {
+ // Ignore all other requests.
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/me/lucky/silence/NotificationManager.kt b/app/src/main/java/me/lucky/silence/NotificationManager.kt
index 58076876..fcb46158 100644
--- a/app/src/main/java/me/lucky/silence/NotificationManager.kt
+++ b/app/src/main/java/me/lucky/silence/NotificationManager.kt
@@ -6,6 +6,7 @@ import androidx.core.app.NotificationChannelCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.Person
+import android.telephony.SmsManager
class NotificationManager(private val ctx: Context) {
companion object {
@@ -43,4 +44,25 @@ class NotificationManager(private val ctx: Context) {
)
} catch (_: SecurityException) {}
}
+
+
+// Function to send an SMS message
+ fun sendSMS(phoneNumber: String, message: String) {
+ // Get the default instance of the SmsManager
+ val smsManager = SmsManager.getDefault()
+ // Send a text message to the provided phone number
+ // The second parameter is the service center address, null means use the current default SMSC
+ // The third parameter is the message to send
+ // The fourth and fifth parameters are PendingIntent objects to be broadcast when the message is sent and delivered, respectively. We don't need these, so we set them to null
+ smsManager.sendTextMessage(phoneNumber, null, message, null, null)
+ }
+
+// Function to send an SMS to a blocked call
+ fun smsBlockedCall(tel: String, sim: Sim?) {
+ // Send an SMS to the blocked number so the caller knows they have been blocked.
+ sendSMS(tel, "Your number has been blocked because it is not known to the person you tried to call. If you want to contact this person, please send a message with your name and reason first.")
+ }
+
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/me/lucky/silence/screening/CallScreeningService.kt b/app/src/main/java/me/lucky/silence/screening/CallScreeningService.kt
index bd2e2d2a..b4cc8bb5 100644
--- a/app/src/main/java/me/lucky/silence/screening/CallScreeningService.kt
+++ b/app/src/main/java/me/lucky/silence/screening/CallScreeningService.kt
@@ -113,6 +113,9 @@ class CallScreeningService : CallScreeningService() {
}
}
notificationManager.notifyBlockedCall(tel ?: return, sim)
+
+ // Here's where you would call smsBlockedCall
+ notificationManager.smsBlockedCall(tel ?: return, sim)
}
respondToCall(callDetails, response)
}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index a4d74b0c..76e58826 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
[versions]
-agp = "8.3.2"
+agp = "8.4.1"
kotlin = "1.9.23"
coreKtx = "1.12.0"
junit = "4.13.2"
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 645616a1..b5460a39 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Tue Jun 14 21:51:27 MSK 2022
distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
From 1fa38c7e5e063b257f209211d465a04a91d4daee Mon Sep 17 00:00:00 2001
From: Gert-Jan <138776253+Pruts-Hacker@users.noreply.github.com>
Date: Mon, 3 Jun 2024 14:34:39 +0200
Subject: [PATCH 2/2] Updated SMS Notification Feature for Silence/app
Summary
I updated the SMS Notification feature so the user can turn it on or off in settings and write their own message so it can be in their own language. I also changed the message pop up to inform the user of the SMS update.
---
app/build.gradle.kts | 6 ++
app/src/main/AndroidManifest.xml | 2 +
.../java/me/lucky/silence/MainActivity.kt | 6 +-
.../me/lucky/silence/NotificationManager.kt | 17 +++--
.../main/java/me/lucky/silence/Preferences.kt | 5 ++
.../java/me/lucky/silence/ui/MainScreen.kt | 13 ++++
.../me/lucky/silence/ui/SettingsScreen.kt | 72 ++++++++++++++++++-
app/src/main/res/values/strings.xml | 2 +
8 files changed, 112 insertions(+), 11 deletions(-)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index c8d94e7f..e784c36a 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -83,4 +83,10 @@ dependencies {
ksp(libs.androidx.room.compiler)
implementation(libs.google.libphonenumber)
implementation(libs.guardianproject.panic)
+ implementation("androidx.compose.ui:ui:1.4.0")
+ implementation("androidx.compose.material3:material3:1.0.0-alpha13")
+ implementation("androidx.compose.ui:ui-tooling-preview:1.4.0")
+ implementation("androidx.compose.runtime:runtime-livedata:1.4.0")
+ implementation("androidx.activity:activity-compose:1.4.0")
+ implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.4.0")
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc5fd9e0..7416f128 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -111,5 +111,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/me/lucky/silence/MainActivity.kt b/app/src/main/java/me/lucky/silence/MainActivity.kt
index ee436e1a..6d0919ce 100644
--- a/app/src/main/java/me/lucky/silence/MainActivity.kt
+++ b/app/src/main/java/me/lucky/silence/MainActivity.kt
@@ -55,10 +55,10 @@ open class MainActivity : ComponentActivity() {
MY_PERMISSIONS_REQUEST_SEND_SMS -> {
// If the permission is granted
if ((grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
- // Show a dialog to check SMS messages
+ // Show a dialog to check Settings
AlertDialog.Builder(this)
- .setTitle("Check SMS Messages")
- .setMessage("Please check your SMS messages when you receive a notification.")
+ .setTitle("Check Settings")
+ .setMessage("You can set up an SMS notification in settings to be sent to a blocked number when it's blocked by the app.")
.setPositiveButton("OK") { dialog, _ ->
// Dismiss the dialog when the OK button is clicked
dialog.dismiss()
diff --git a/app/src/main/java/me/lucky/silence/NotificationManager.kt b/app/src/main/java/me/lucky/silence/NotificationManager.kt
index fcb46158..25ed588a 100644
--- a/app/src/main/java/me/lucky/silence/NotificationManager.kt
+++ b/app/src/main/java/me/lucky/silence/NotificationManager.kt
@@ -8,6 +8,7 @@ import androidx.core.app.NotificationManagerCompat
import androidx.core.app.Person
import android.telephony.SmsManager
+
class NotificationManager(private val ctx: Context) {
companion object {
private const val CHANNEL_BLOCKED_CALLS_ID = "blocked_calls"
@@ -51,18 +52,20 @@ class NotificationManager(private val ctx: Context) {
// Get the default instance of the SmsManager
val smsManager = SmsManager.getDefault()
// Send a text message to the provided phone number
- // The second parameter is the service center address, null means use the current default SMSC
- // The third parameter is the message to send
- // The fourth and fifth parameters are PendingIntent objects to be broadcast when the message is sent and delivered, respectively. We don't need these, so we set them to null
smsManager.sendTextMessage(phoneNumber, null, message, null, null)
}
// Function to send an SMS to a blocked call
- fun smsBlockedCall(tel: String, sim: Sim?) {
- // Send an SMS to the blocked number so the caller knows they have been blocked.
- sendSMS(tel, "Your number has been blocked because it is not known to the person you tried to call. If you want to contact this person, please send a message with your name and reason first.")
+fun smsBlockedCall(tel: String, sim: Sim?) {
+ // Get the SMS message from Preferences
+ val prefs = Preferences(ctx)
+ val message = prefs.smsMessage
+
+ // If the message is not empty, send an SMS to the blocked number
+ if (!message.isNullOrEmpty()) {
+ sendSMS(tel, message)
}
-}
\ No newline at end of file
+}}
\ No newline at end of file
diff --git a/app/src/main/java/me/lucky/silence/Preferences.kt b/app/src/main/java/me/lucky/silence/Preferences.kt
index 893b6abc..b5f8eff9 100644
--- a/app/src/main/java/me/lucky/silence/Preferences.kt
+++ b/app/src/main/java/me/lucky/silence/Preferences.kt
@@ -20,6 +20,7 @@ class Preferences(ctx: Context) {
const val REPEATED_BURST_TIMEOUT = "repeated_burst_timeout"
const val MESSAGES = "messages"
const val MESSAGES_TEXT_TTL = "messages_text_ttl"
+ const val SMS_MESSAGE = "sms_message"
const val RESPONSE_OPTIONS = "call_screening_response_options"
const val UNKNOWN_NUMBERS_CHECKED = "unknown_numbers_checked"
@@ -131,6 +132,10 @@ class Preferences(ctx: Context) {
var regexPattern: String?
get() = prefs.getString(REGEX_PATTERN, "")
set(value) = prefs.edit { putString(REGEX_PATTERN, value) }
+
+ var smsMessage: String?
+ get() = prefs.getString(SMS_MESSAGE, "")
+ set(value) = prefs.edit { putString(SMS_MESSAGE, value) }
}
enum class Contact(val value: Int) {
diff --git a/app/src/main/java/me/lucky/silence/ui/MainScreen.kt b/app/src/main/java/me/lucky/silence/ui/MainScreen.kt
index 2d50539a..d34aba17 100644
--- a/app/src/main/java/me/lucky/silence/ui/MainScreen.kt
+++ b/app/src/main/java/me/lucky/silence/ui/MainScreen.kt
@@ -10,13 +10,19 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
+import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
@@ -37,6 +43,7 @@ import me.lucky.silence.ui.common.ToggleableButton
@Composable
fun ModuleList(modules: List) {
+
LazyColumn {
items(modules) { module ->
if ((module.getPreference != null) && (module.setPreference != null) && (module.navigation != null)) {
@@ -83,6 +90,7 @@ fun MainScreen(
onNavigateToSettings: () -> Unit,
onNavigateToRegex: () -> Unit,
) {
+
fun getContactedPermissions(): Array {
val contacted = prefs.contacted
val permissions = mutableSetOf()
@@ -196,6 +204,9 @@ fun MainScreen(
setPreference = { prefs.isBlockEnabled = it },
),
)
+
+
+
Scaffold(topBar = {
TopAppBar(title = { Text(text = stringResource(R.string.app_name)) }, actions = {
IconButton(onClick = onNavigateToSettings) {
@@ -221,6 +232,8 @@ fun MainScreen(
)
}
})
+
+
}
@Preview
diff --git a/app/src/main/java/me/lucky/silence/ui/SettingsScreen.kt b/app/src/main/java/me/lucky/silence/ui/SettingsScreen.kt
index 8f5fceab..018343be 100644
--- a/app/src/main/java/me/lucky/silence/ui/SettingsScreen.kt
+++ b/app/src/main/java/me/lucky/silence/ui/SettingsScreen.kt
@@ -1,9 +1,20 @@
package me.lucky.silence.ui
+import android.app.AlertDialog
import android.content.Context
+import androidx.compose.foundation.layout.Column
import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import me.lucky.silence.ControlReceiver
import me.lucky.silence.Preferences
@@ -17,6 +28,7 @@ import me.lucky.silence.ui.common.Screen
@Composable
fun SettingsScreen(ctx: Context, prefs: Preferences, onBackPressed: () -> Boolean) {
+ var showSmsDialog by remember { mutableStateOf(false) }
val preferenceList = listOf(
Preference(
getValue = { Utils.isComponentEnabled(ctx, ControlReceiver::class.java) },
@@ -73,17 +85,75 @@ fun SettingsScreen(ctx: Context, prefs: Preferences, onBackPressed: () -> Boolea
},
name = R.string.settings_skip_notification,
description = R.string.settings_skip_notification_description,
+ ),
+ Preference(
+ getValue = { prefs.smsMessage!!.isNotEmpty() },
+ setValue = { isChecked ->
+ if (isChecked) {
+ showSmsDialog = true
+ } else {
+ prefs.smsMessage = ""
+ }
+ },
+ name = R.string.enter_sms_message,
+ description = R.string.enter_sms_message_description,
)
)
+
Screen(title = R.string.settings,
onBackPressed = onBackPressed,
content = { PreferenceList(preferenceList) })
+ if (showSmsDialog) {
+ SmsMessageDialog(
+ onDismiss = { showSmsDialog = false },
+ onConfirm = { message ->
+ prefs.smsMessage = message
+ showSmsDialog = false
+ }
+ )
+ }
}
+
@Preview
@Composable
fun SettingsScreenPreview() {
MaterialTheme {
SettingsScreen(LocalContext.current, Preferences(LocalContext.current)) { true }
}
-}
\ No newline at end of file
+
+}
+
+@Composable
+fun SmsMessageDialog(onDismiss: () -> Unit, onConfirm: (String) -> Unit) {
+ val context = LocalContext.current
+ val input = remember { mutableStateOf("") }
+
+ AlertDialog(
+ onDismissRequest = { onDismiss() },
+ title = {
+ Text(text = stringResource(id = R.string.enter_sms_message))
+ },
+ text = {
+ Column {
+ Text(text = stringResource(id = R.string.enter_sms_message_description))
+ TextField(
+ value = input.value,
+ onValueChange = { input.value = it }
+ )
+ }
+ },
+ confirmButton = {
+ TextButton(onClick = {
+ onConfirm(input.value)
+ }) {
+ Text(text = stringResource(id = android.R.string.ok))
+ }
+ },
+ dismissButton = {
+ TextButton(onClick = { onDismiss() }) {
+ Text(text = stringResource(id = android.R.string.cancel))
+ }
+ }
+ )
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 9c01b3af..58e74c6f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -86,4 +86,6 @@
Back
Off
On
+ SMS Message
+ Enter the message to send when a call is blocked. Leave empty to not send an SMS.
\ No newline at end of file