Skip to content

Commit 12c21da

Browse files
authored
Fix exact alarm permission on Android 14 (#298)
1 parent 8649679 commit 12c21da

File tree

2 files changed

+59
-25
lines changed

2 files changed

+59
-25
lines changed

app/src/main/kotlin/com/github/gotify/init/InitializationActivity.kt

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
package com.github.gotify.init
22

33
import android.Manifest
4+
import android.app.AlarmManager
45
import android.app.NotificationManager
56
import android.content.Context
67
import android.content.Intent
8+
import android.net.Uri
79
import android.os.Build
810
import android.os.Bundle
11+
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
12+
import androidx.annotation.RequiresApi
913
import androidx.appcompat.app.AppCompatActivity
14+
import androidx.core.content.ContextCompat
1015
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
1116
import androidx.preference.PreferenceManager
1217
import com.github.gotify.NotificationSupport
@@ -34,6 +39,12 @@ internal class InitializationActivity : AppCompatActivity() {
3439
private lateinit var settings: Settings
3540
private var splashScreenActive = true
3641

42+
@RequiresApi(Build.VERSION_CODES.S)
43+
private val activityResultLauncher =
44+
registerForActivityResult(StartActivityForResult()) {
45+
requestAlarmPermissionOrAuthenticate()
46+
}
47+
3748
override fun onCreate(savedInstanceState: Bundle?) {
3849
super.onCreate(savedInstanceState)
3950
Log.init(this)
@@ -54,14 +65,30 @@ internal class InitializationActivity : AppCompatActivity() {
5465
installSplashScreen().setKeepOnScreenCondition { splashScreenActive }
5566

5667
if (settings.tokenExists()) {
57-
runWithNeededPermissions {
58-
tryAuthenticate()
68+
runWithPostNotificationsPermission {
69+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU) {
70+
// Android 14 and above
71+
requestAlarmPermissionOrAuthenticate()
72+
} else {
73+
// Android 13 and below
74+
tryAuthenticate()
75+
}
5976
}
6077
} else {
6178
showLogin()
6279
}
6380
}
6481

82+
@RequiresApi(Build.VERSION_CODES.S)
83+
private fun requestAlarmPermissionOrAuthenticate() {
84+
val manager = ContextCompat.getSystemService(this, AlarmManager::class.java)
85+
if (manager?.canScheduleExactAlarms() == true) {
86+
tryAuthenticate()
87+
} else {
88+
alarmDialog()
89+
}
90+
}
91+
6592
private fun showLogin() {
6693
splashScreenActive = false
6794
startActivity(Intent(this, LoginActivity::class.java))
@@ -109,6 +136,22 @@ internal class InitializationActivity : AppCompatActivity() {
109136
.show()
110137
}
111138

139+
@RequiresApi(Build.VERSION_CODES.S)
140+
private fun alarmDialog() {
141+
MaterialAlertDialogBuilder(this)
142+
.setMessage(getString(R.string.permissions_alarm_prompt))
143+
.setPositiveButton(getString(R.string.permissions_dialog_grant)) { _, _ ->
144+
Intent(
145+
android.provider.Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM,
146+
Uri.parse("package:$packageName")
147+
).apply {
148+
activityResultLauncher.launch(this)
149+
}
150+
}
151+
.setCancelable(false)
152+
.show()
153+
}
154+
112155
private fun authenticated(user: User) {
113156
Log.i("Authenticated as ${user.name}")
114157

@@ -146,40 +189,30 @@ internal class InitializationActivity : AppCompatActivity() {
146189
.enqueue(Callback.callInUI(this, callback, errorCallback))
147190
}
148191

149-
private fun runWithNeededPermissions(action: () -> Unit) {
150-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
192+
private fun runWithPostNotificationsPermission(action: () -> Unit) {
193+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
194+
// Android 13 and above
151195
val quickPermissionsOption = QuickPermissionsOptions(
152196
handleRationale = true,
153197
handlePermanentlyDenied = true,
154198
rationaleMethod = { req -> processPermissionRationale(req) },
155199
permissionsDeniedMethod = { req -> processPermissionRationale(req) },
156200
permanentDeniedMethod = { req -> processPermissionsPermanentDenied(req) }
157201
)
158-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
159-
// Android 13 and above
160-
runWithPermissions(
161-
Manifest.permission.SCHEDULE_EXACT_ALARM,
162-
Manifest.permission.POST_NOTIFICATIONS,
163-
options = quickPermissionsOption,
164-
callback = action
165-
)
166-
} else {
167-
// Android 12 and Android 12L
168-
runWithPermissions(
169-
Manifest.permission.SCHEDULE_EXACT_ALARM,
170-
options = quickPermissionsOption,
171-
callback = action
172-
)
173-
}
202+
runWithPermissions(
203+
Manifest.permission.POST_NOTIFICATIONS,
204+
options = quickPermissionsOption,
205+
callback = action
206+
)
174207
} else {
175-
// Android 11 and below
208+
// Android 12 and below
176209
action()
177210
}
178211
}
179212

180213
private fun processPermissionRationale(req: QuickPermissionsRequest) {
181214
MaterialAlertDialogBuilder(this)
182-
.setMessage(getString(R.string.permissions_denied_temp))
215+
.setMessage(getString(R.string.permissions_notification_denied_temp))
183216
.setPositiveButton(getString(R.string.permissions_dialog_grant)) { _, _ ->
184217
req.proceed()
185218
}
@@ -189,7 +222,7 @@ internal class InitializationActivity : AppCompatActivity() {
189222

190223
private fun processPermissionsPermanentDenied(req: QuickPermissionsRequest) {
191224
MaterialAlertDialogBuilder(this)
192-
.setMessage(getString(R.string.permissions_denied_permanent))
225+
.setMessage(getString(R.string.permissions_notification_denied_permanent))
193226
.setPositiveButton(getString(R.string.permissions_dialog_grant)) { _, _ ->
194227
req.openAppSettings()
195228
}

app/src/main/res/values/strings.xml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@
4646
<string name="login">Login</string>
4747
<string name="check_url">Check URL</string>
4848
<string name="permissions_dialog_grant">Grant</string>
49-
<string name="permissions_denied_temp">Gotify requires permission to display push notifications.</string>
50-
<string name="permissions_denied_permanent">Gotify requires permission to display push notifications. Please grant the required permission in the settings.</string>
49+
<string name="permissions_notification_denied_temp">Gotify requires permission to display push notifications.</string>
50+
<string name="permissions_notification_denied_permanent">Gotify requires permission to display push notifications. Please grant the required permission in the settings.</string>
51+
<string name="permissions_alarm_prompt">Gotify requires permission to schedule reconnecting after connection is lost.</string>
5152
<string name="gotify_logo">Gotify logo</string>
5253
<string name="refresh_all">Refresh all</string>
5354
<string name="logout_confirm">Do you really want to logout?</string>

0 commit comments

Comments
 (0)