diff --git a/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt b/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt
index 93620b69..f7baa995 100644
--- a/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt
+++ b/data/src/main/java/com/moez/QKSMS/manager/PermissionManagerImpl.kt
@@ -19,6 +19,7 @@
package com.moez.QKSMS.manager
import android.Manifest
+import android.app.NotificationManager
import android.app.role.RoleManager
import android.content.Context
import android.content.pm.PackageManager
@@ -29,6 +30,8 @@ import javax.inject.Inject
class PermissionManagerImpl @Inject constructor(private val context: Context) : PermissionManager {
+ private val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
override fun isDefaultSms(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
context.getSystemService(RoleManager::class.java)?.isRoleHeld(RoleManager.ROLE_SMS) == true
@@ -49,6 +52,13 @@ class PermissionManagerImpl @Inject constructor(private val context: Context) :
return hasPermission(Manifest.permission.READ_CONTACTS)
}
+ override fun hasNotifications(): Boolean {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
+ return true
+ }
+ return notificationManager.areNotificationsEnabled()
+ }
+
override fun hasPhone(): Boolean {
return hasPermission(Manifest.permission.READ_PHONE_STATE)
}
diff --git a/domain/src/main/java/com/moez/QKSMS/manager/PermissionManager.kt b/domain/src/main/java/com/moez/QKSMS/manager/PermissionManager.kt
index 0fd6123a..256fa910 100644
--- a/domain/src/main/java/com/moez/QKSMS/manager/PermissionManager.kt
+++ b/domain/src/main/java/com/moez/QKSMS/manager/PermissionManager.kt
@@ -28,10 +28,12 @@ interface PermissionManager {
fun hasContacts(): Boolean
+ fun hasNotifications(): Boolean
+
fun hasPhone(): Boolean
fun hasCalling(): Boolean
fun hasStorage(): Boolean
-}
\ No newline at end of file
+}
diff --git a/domain/src/main/java/com/moez/QKSMS/util/Preferences.kt b/domain/src/main/java/com/moez/QKSMS/util/Preferences.kt
index d7711f3b..aeee576a 100644
--- a/domain/src/main/java/com/moez/QKSMS/util/Preferences.kt
+++ b/domain/src/main/java/com/moez/QKSMS/util/Preferences.kt
@@ -84,6 +84,7 @@ class Preferences @Inject constructor(
val canUseSubId = rxPrefs.getBoolean("canUseSubId", true)
val version = rxPrefs.getInteger("version", context.versionCode)
val changelogVersion = rxPrefs.getInteger("changelogVersion", context.versionCode)
+ val hasAskedForNotificationPermission = rxPrefs.getBoolean("hasAskedForNotificationPermission", false)
@Deprecated("This should only be accessed when migrating to @blockingManager")
val sia = rxPrefs.getBoolean("sia", false)
diff --git a/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt b/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt
index b0659c99..b55b495a 100644
--- a/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt
+++ b/presentation/src/main/java/com/moez/QKSMS/common/Navigator.kt
@@ -281,6 +281,14 @@ class Navigator @Inject constructor(
startActivityExternal(intent)
}
+ fun showPermissions() {
+ val intent = Intent()
+ intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+ intent.data = Uri.fromParts("package", context.packageName, null)
+
+ startActivity(intent)
+ }
+
fun showNotificationSettings(threadId: Long = 0) {
val intent = Intent(context, NotificationPrefsActivity::class.java)
intent.putExtra("threadId", threadId)
diff --git a/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt b/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt
index 4e767f1f..dbe9c85a 100644
--- a/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt
+++ b/presentation/src/main/java/com/moez/QKSMS/common/util/NotificationManagerImpl.kt
@@ -57,6 +57,7 @@ import com.moez.QKSMS.util.GlideApp
import com.moez.QKSMS.util.PhoneNumberUtils
import com.moez.QKSMS.util.Preferences
import com.moez.QKSMS.util.tryOrNull
+import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@@ -94,6 +95,11 @@ class NotificationManagerImpl @Inject constructor(
return
}
+ if (!permissions.hasNotifications()) {
+ Timber.w("Cannot update notification because we don't have the notification permission")
+ return
+ }
+
val messages = messageRepo.getUnreadUnseenMessages(threadId)
// If there are no messages to be displayed, make sure that the notification is dismissed
diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt
index e84c758c..695f38b3 100644
--- a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt
+++ b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainActivity.kt
@@ -289,7 +289,7 @@ class MainActivity : QkThemedActivity(), MainView {
when (state.syncing) {
is SyncRepository.SyncProgress.Idle -> {
syncing.isVisible = false
- snackbar.isVisible = !state.defaultSms || !state.smsPermission || !state.contactPermission
+ snackbar.isVisible = !state.defaultSms || !state.smsPermission || !state.contactPermission || !state.notificationPermission
}
is SyncRepository.SyncProgress.Running -> {
@@ -319,6 +319,12 @@ class MainActivity : QkThemedActivity(), MainView {
snackbarMessage?.setText(R.string.main_permission_contacts)
snackbarButton?.setText(R.string.main_permission_allow)
}
+
+ !state.notificationPermission -> {
+ snackbarTitle?.setText(R.string.main_permission_required)
+ snackbarMessage?.setText(R.string.main_permission_notifications)
+ snackbarButton?.setText(R.string.main_permission_allow)
+ }
}
}
@@ -350,10 +356,17 @@ class MainActivity : QkThemedActivity(), MainView {
}
override fun requestPermissions() {
- ActivityCompat.requestPermissions(this, arrayOf(
- Manifest.permission.READ_SMS,
- Manifest.permission.SEND_SMS,
- Manifest.permission.READ_CONTACTS), 0)
+ val permissions = mutableListOf(
+ Manifest.permission.READ_SMS,
+ Manifest.permission.SEND_SMS,
+ Manifest.permission.READ_CONTACTS
+ )
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ permissions += Manifest.permission.POST_NOTIFICATIONS
+ }
+
+ ActivityCompat.requestPermissions(this, permissions.toTypedArray(), 0)
}
override fun clearSearch() {
diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainState.kt b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainState.kt
index 01697633..9d66069b 100644
--- a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainState.kt
+++ b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainState.kt
@@ -32,7 +32,8 @@ data class MainState(
val syncing: SyncRepository.SyncProgress = SyncRepository.SyncProgress.Idle,
val defaultSms: Boolean = true,
val smsPermission: Boolean = true,
- val contactPermission: Boolean = true
+ val contactPermission: Boolean = true,
+ val notificationPermission: Boolean = true,
)
sealed class MainPage
diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainViewModel.kt b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainViewModel.kt
index a285c432..1fa12198 100644
--- a/presentation/src/main/java/com/moez/QKSMS/feature/main/MainViewModel.kt
+++ b/presentation/src/main/java/com/moez/QKSMS/feature/main/MainViewModel.kt
@@ -18,6 +18,9 @@
*/
package com.moez.QKSMS.feature.main
+import android.content.Intent
+import android.net.Uri
+import android.provider.Settings
import androidx.recyclerview.widget.ItemTouchHelper
import com.moez.QKSMS.R
import com.moez.QKSMS.common.Navigator
@@ -135,28 +138,56 @@ class MainViewModel @Inject constructor(
!permissionManager.hasReadSms() || !permissionManager.hasContacts() -> view.requestPermissions()
}
- val permissions = view.activityResumedIntent
- .filter { resumed -> resumed }
- .observeOn(Schedulers.io())
- .map { Triple(permissionManager.isDefaultSms(), permissionManager.hasReadSms(), permissionManager.hasContacts()) }
- .distinctUntilChanged()
- .share()
+ // If the default SMS state changes, reflect it in the State
+ view.activityResumedIntent
+ .filter { resumed -> resumed }
+ .observeOn(Schedulers.io())
+ .map { permissionManager.isDefaultSms() }
+ .distinctUntilChanged()
+ .doOnNext { defaultSms -> newState { copy(defaultSms = defaultSms) } }
+ .autoDisposable(view.scope())
+ .subscribe()
- // If the default SMS state or permission states change, update the ViewState
- permissions
- .doOnNext { (defaultSms, smsPermission, contactPermission) ->
- newState { copy(defaultSms = defaultSms, smsPermission = smsPermission, contactPermission = contactPermission) }
- }
- .autoDisposable(view.scope())
- .subscribe()
+ // If the SMS Permission state changes, reflect it in the State
+ view.activityResumedIntent
+ .filter { resumed -> resumed }
+ .observeOn(Schedulers.io())
+ .map { permissionManager.hasReadSms() }
+ .distinctUntilChanged()
+ .doOnNext { smsPermission -> newState { copy(smsPermission = smsPermission) } }
+ .autoDisposable(view.scope())
+ .subscribe()
- // If we go from not having all permissions to having them, sync messages
- permissions
- .skip(1)
- .filter { it.first && it.second && it.third }
- .take(1)
- .autoDisposable(view.scope())
- .subscribe { syncMessages.execute(Unit) }
+ // If the Contacts Permission state changes, reflect it in the State
+ view.activityResumedIntent
+ .filter { resumed -> resumed }
+ .observeOn(Schedulers.io())
+ .map { permissionManager.hasContacts() }
+ .distinctUntilChanged()
+ .doOnNext { contactPermission -> newState { copy(contactPermission = contactPermission) } }
+ .autoDisposable(view.scope())
+ .subscribe()
+
+ // If the Notifications Permission state changes, reflect it in the State
+ view.activityResumedIntent
+ .filter { resumed -> resumed }
+ .observeOn(Schedulers.io())
+ .map { permissionManager.hasNotifications() }
+ .distinctUntilChanged()
+ .doOnNext { notificationPermission -> newState { copy(notificationPermission = notificationPermission) } }
+ .autoDisposable(view.scope())
+ .subscribe()
+
+ // If we go from not having all SMS permissions to having them, sync messages
+ view.activityResumedIntent
+ .filter { resumed -> resumed }
+ .observeOn(Schedulers.io())
+ .map { permissionManager.isDefaultSms() && permissionManager.hasReadSms() && permissionManager.hasContacts() }
+ .distinctUntilChanged()
+ .skip(1)
+ .filter { hasAllPermissions -> hasAllPermissions }
+ .autoDisposable(view.scope())
+ .subscribe { syncMessages.execute(Unit) }
// Launch screen from intent
view.onNewIntentIntent
@@ -448,6 +479,14 @@ class MainViewModel @Inject constructor(
!state.defaultSms -> view.requestDefaultSms()
!state.smsPermission -> view.requestPermissions()
!state.contactPermission -> view.requestPermissions()
+ !state.notificationPermission -> {
+ if (prefs.hasAskedForNotificationPermission.get()) {
+ navigator.showPermissions()
+ } else {
+ prefs.hasAskedForNotificationPermission.set(true)
+ view.requestPermissions()
+ }
+ }
}
}
.autoDisposable(view.scope())
diff --git a/presentation/src/main/res/values/strings.xml b/presentation/src/main/res/values/strings.xml
index 2fe3e330..ba998ac8 100644
--- a/presentation/src/main/res/values/strings.xml
+++ b/presentation/src/main/res/values/strings.xml
@@ -68,6 +68,7 @@
Permission required
QKSMS needs permission to send and view SMS messages
QKSMS needs permission to view your contacts
+ QKSMS needs permission to display notifications
Allow
Inbox