From 39f361f43999bae84d0ae9048c1f9ee173a5b6bf Mon Sep 17 00:00:00 2001 From: Victor Andreasson Date: Sat, 11 May 2024 23:42:18 +0200 Subject: [PATCH] Ensure the "alarms & reminders" permission is granted before syncing Without this, the first sync after installing the app leads to a SecurityException and app crash on API >= 33. --- .../java/com/orgzly/android/sync/SyncState.kt | 3 ++ .../com/orgzly/android/sync/SyncWorker.kt | 30 +++++++++++++++++++ .../com/orgzly/android/ui/CommonFragment.kt | 1 + .../orgzly/android/ui/sync/SyncFragment.kt | 1 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 36 insertions(+) diff --git a/app/src/main/java/com/orgzly/android/sync/SyncState.kt b/app/src/main/java/com/orgzly/android/sync/SyncState.kt index d1b00d213..8eeb4369b 100644 --- a/app/src/main/java/com/orgzly/android/sync/SyncState.kt +++ b/app/src/main/java/com/orgzly/android/sync/SyncState.kt @@ -24,6 +24,7 @@ data class SyncState(val type: Type, val message: String? = null, val current: I FAILED_NO_CONNECTION, FAILED_NO_STORAGE_PERMISSION, FAILED_NO_BOOKS_FOUND, + FAILED_NO_ALARMS_PERMISSION, FAILED_EXCEPTION } @@ -33,6 +34,7 @@ data class SyncState(val type: Type, val message: String? = null, val current: I Type.FAILED_NO_CONNECTION, Type.FAILED_NO_STORAGE_PERMISSION, Type.FAILED_NO_BOOKS_FOUND, + Type.FAILED_NO_ALARMS_PERMISSION, Type.FAILED_EXCEPTION -> true else -> @@ -83,6 +85,7 @@ data class SyncState(val type: Type, val message: String? = null, val current: I Type.FAILED_NO_REPOS -> getString(R.string.no_repos) Type.FAILED_NO_CONNECTION -> getString(R.string.no_connection) Type.FAILED_NO_STORAGE_PERMISSION -> getString(R.string.storage_permissions_missing) + Type.FAILED_NO_ALARMS_PERMISSION -> getString(R.string.alarms_permissions_missing) Type.FAILED_NO_BOOKS_FOUND -> getString(R.string.no_books) Type.FAILED_EXCEPTION -> message } diff --git a/app/src/main/java/com/orgzly/android/sync/SyncWorker.kt b/app/src/main/java/com/orgzly/android/sync/SyncWorker.kt index e06428a94..02f2b9bc9 100644 --- a/app/src/main/java/com/orgzly/android/sync/SyncWorker.kt +++ b/app/src/main/java/com/orgzly/android/sync/SyncWorker.kt @@ -1,6 +1,10 @@ package com.orgzly.android.sync import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.provider.Settings import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo import androidx.work.WorkerParameters @@ -15,6 +19,7 @@ import com.orgzly.android.prefs.AppPreferences import com.orgzly.android.reminders.RemindersScheduler import com.orgzly.android.repos.* import com.orgzly.android.ui.notifications.SyncNotifications +import com.orgzly.android.ui.util.getAlarmManager import com.orgzly.android.ui.util.haveNetworkConnection import com.orgzly.android.util.AppPermissions import com.orgzly.android.util.LogMajorEvents @@ -162,6 +167,31 @@ class SyncWorker(val context: Context, val params: WorkerParameters) : } } + /* Make sure we have permission to set alarms & reminders, + * since this typically happens when new books are parsed. + */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + if (!context.getAlarmManager().canScheduleExactAlarms()) { + if ( + AppPreferences.remindersForDeadlineEnabled(context) || + AppPreferences.remindersForScheduledEnabled(context) || + AppPreferences.remindersForEventsEnabled(context) + ) { + if (App.getCurrentActivity() != null) { + val uri = Uri.parse("package:" + BuildConfig.APPLICATION_ID) + App.getCurrentActivity().startActivity( + Intent( + Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM, + uri + ) + ) + } + return SyncState.getInstance((SyncState.Type.FAILED_NO_ALARMS_PERMISSION)) + } + } + } + + return null } diff --git a/app/src/main/java/com/orgzly/android/ui/CommonFragment.kt b/app/src/main/java/com/orgzly/android/ui/CommonFragment.kt index 89c6cc971..b75cf87ed 100644 --- a/app/src/main/java/com/orgzly/android/ui/CommonFragment.kt +++ b/app/src/main/java/com/orgzly/android/ui/CommonFragment.kt @@ -69,6 +69,7 @@ open class CommonFragment : Fragment() { FAILED_NO_REPOS, FAILED_NO_CONNECTION, FAILED_NO_STORAGE_PERMISSION, + FAILED_NO_ALARMS_PERMISSION, FAILED_NO_BOOKS_FOUND, FAILED_EXCEPTION -> progressIndicator.visibility = View.GONE diff --git a/app/src/main/java/com/orgzly/android/ui/sync/SyncFragment.kt b/app/src/main/java/com/orgzly/android/ui/sync/SyncFragment.kt index f1d67ab7b..eb02be66e 100644 --- a/app/src/main/java/com/orgzly/android/ui/sync/SyncFragment.kt +++ b/app/src/main/java/com/orgzly/android/ui/sync/SyncFragment.kt @@ -204,6 +204,7 @@ class SyncFragment : Fragment() { SyncState.Type.FAILED_NO_REPOS, SyncState.Type.FAILED_NO_CONNECTION, SyncState.Type.FAILED_NO_STORAGE_PERMISSION, + SyncState.Type.FAILED_NO_ALARMS_PERMISSION, SyncState.Type.FAILED_NO_BOOKS_FOUND, SyncState.Type.FAILED_EXCEPTION -> { setAnimation(false) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 391d646a2..84a81d6fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -593,6 +593,7 @@ No storage permission + No permission to set reminders File does not exist: %s Import from ā€œ%1$sā€?