From 705ad5c8a9628fef75f8b84ea532a18a64aa5ae6 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 28 Nov 2024 15:28:48 +0100 Subject: [PATCH 01/17] feature: implementation notification to let user relogin --- app/src/main/AndroidManifest.xml | 8 +- .../davdroid/ReLoginWithOidcService.kt | 92 +++++++++++++++++++ .../receiver/BootCompletedReceiver.kt | 59 +++++++++++- .../bitfire/davdroid/ui/NotificationUtils.kt | 4 +- app/src/main/res/values/strings.xml | 2 + 5 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 30e04639b..ae77090bf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -189,8 +189,10 @@ - + @@ -341,7 +343,7 @@ android:name="android.content.SyncAdapter" android:resource="@xml/eelo_sync_tasks" /> - + - + . + */ +package at.bitfire.davdroid + +import android.accounts.AccountManager +import android.accounts.AccountManagerFuture +import android.app.Service +import android.content.Context +import android.content.Intent +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES +import android.os.Bundle +import android.os.IBinder +import androidx.core.content.ContextCompat +import at.bitfire.davdroid.log.Logger +import org.apache.commons.lang3.NotImplementedException + +class ReLoginWithOidcService: Service() { + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val context = this.applicationContext + + val accountManager = AccountManager.get(context) + val eAccountType = context.getString(R.string.eelo_account_type) + + val accountRemoved = removeAccount(eAccountType, accountManager) + + if (accountRemoved) { + openEAccountLoginForm(accountManager, eAccountType, context) + } + + return START_STICKY + } + + private fun removeAccount(accountType: String, accountManager: AccountManager): Boolean { + val account = accountManager.getAccountsByType(accountType).firstOrNull() + + return account?.let { + accountManager.removeAccountExplicitly(it) + } ?: false + } + + private fun openEAccountLoginForm(accountManager: AccountManager, eAccountType: String, context: Context) { + accountManager.addAccount(eAccountType, + null, + null, + null, + null, + { future -> + val intent = getIntentFromFuture(future) + + intent?.let { + ContextCompat.startActivity(context, it, null) + } + }, + null) + } + + private fun getIntentFromFuture(future: AccountManagerFuture): Intent? { + val bundle = future.result + + return try { + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + bundle?.getParcelable(AccountManager.KEY_INTENT, Intent::class.java) + } else { + @Suppress("DEPRECATION") + bundle?.getParcelable(AccountManager.KEY_INTENT) + } + } catch (exception: Exception) { + Logger.log.warning("${exception.javaClass}: can't add account: ${exception.message}") + null + } + + } + + override fun onBind(intent: Intent?): IBinder? { + throw NotImplementedException() + } +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index d76975081..d7f792c03 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -4,12 +4,22 @@ package at.bitfire.davdroid.receiver +import android.accounts.Account +import android.accounts.AccountManager +import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import androidx.core.app.NotificationManagerCompat +import at.bitfire.davdroid.R +import at.bitfire.davdroid.ReLoginWithOidcService import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.syncadapter.AccountUtils +import at.bitfire.davdroid.ui.NotificationUtils +import at.bitfire.davdroid.ui.NotificationUtils.CHANNEL_GENERAL +import at.bitfire.davdroid.ui.NotificationUtils.NOTIFY_SWITCH_TO_OPENID +import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible /** * There are circumstances when Android drops automatic sync of accounts and resets them @@ -22,12 +32,53 @@ class BootCompletedReceiver: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { Logger.log.info("Device has been rebooted; checking sync intervals etc.") - // sync intervals are checked in App.onCreate() + + val eAccountType = context.getString(R.string.eelo_account_type) + val accountManager = AccountManager.get(context) + AccountUtils.getMainAccounts(context) .forEach { - val accountSettings = AccountSettings(context, it) - accountSettings.initSync() + if (eAccountType == it.type && !isLoggedWithOpenId(it, accountManager)) { + notifySwitchToOpenId(it, context) + } else { // sync intervals are checked in App.onCreate() + val accountSettings = AccountSettings(context, it) + accountSettings.initSync() + } } } -} \ No newline at end of file + private fun isLoggedWithOpenId(account: Account, accountManager: AccountManager): Boolean { + val hasAuthStateData = accountManager.getUserData(account, AccountSettings.KEY_AUTH_STATE) != null + val isPasswordNull = accountManager.getPassword(account).isNullOrEmpty() + return isPasswordNull && hasAuthStateData + } + + private fun notifySwitchToOpenId(account: Account, context: Context) { + val tag = "Switch to openID" + val title = context.getString(R.string.notification_account_title, account.name) + val contentText = context.getString(R.string.notification_switch_to_openId_text) + val intent = generateReLoginIntent(context) + + val notification = NotificationUtils.newBuilder(context, CHANNEL_GENERAL) + .setContentTitle(title) + .setContentText(contentText) + .setSmallIcon(R.drawable.ic_info) + .setContentIntent(intent) + .setAutoCancel(true) + .build() + + val notificationManager = NotificationManagerCompat.from(context) + notificationManager.notifyIfPossible(tag, NOTIFY_SWITCH_TO_OPENID, notification) + } + + private fun generateReLoginIntent(context: Context): PendingIntent { + val reLoginIntent = Intent(context, ReLoginWithOidcService::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + + return PendingIntent.getActivity(context, + 0, + reLoginIntent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + } +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/NotificationUtils.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/NotificationUtils.kt index e41544d53..143b8329d 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/NotificationUtils.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/NotificationUtils.kt @@ -34,6 +34,7 @@ object NotificationUtils { const val NOTIFY_SYNC_EXPEDITED = 14 const val NOTIFY_TASKS_PROVIDER_TOO_OLD = 20 const val NOTIFY_PERMISSIONS = 21 + const val NOTIFY_SWITCH_TO_OPENID = 22 const val NOTIFY_LICENSE = 100 @@ -80,7 +81,6 @@ object NotificationUtils { NotificationCompat.Builder(context, channel) .setColor(ResourcesCompat.getColor(context.resources, R.color.primaryColor, null)) - fun NotificationManagerCompat.notifyIfPossible(tag: String?, id: Int, notification: Notification) { try { notify(tag, id, notification) @@ -92,4 +92,4 @@ object NotificationUtils { fun NotificationManagerCompat.notifyIfPossible(id: Int, notification: Notification) = notifyIfPossible(null, id, notification) -} \ No newline at end of file +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 47579443b..60cb62446 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -53,6 +53,8 @@ Non-fatal synchronization problems like certain invalid files Network and I/O errors Timeouts, connection problems, etc. (often temporary) + Your account %1$s + A new login service for a better experience is available. Tap the notification to start using it. Your data. Your choice. -- GitLab From ed9ef60dcc548d6d521f301b9f84892d1f2961b5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 29 Nov 2024 13:52:06 +0100 Subject: [PATCH 02/17] fix: replace PendingIntent.gtActivity by getservice --- .../at/bitfire/davdroid/receiver/BootCompletedReceiver.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index d7f792c03..82e2e3a62 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -72,11 +72,9 @@ class BootCompletedReceiver: BroadcastReceiver() { } private fun generateReLoginIntent(context: Context): PendingIntent { - val reLoginIntent = Intent(context, ReLoginWithOidcService::class.java).apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK - } + val reLoginIntent = Intent(context, ReLoginWithOidcService::class.java) - return PendingIntent.getActivity(context, + return PendingIntent.getService(context, 0, reLoginIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) -- GitLab From d6cbb8aee49f8fad36040ab68f76b20f1eb3a280 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Fri, 29 Nov 2024 12:58:35 +0000 Subject: [PATCH 03/17] fix: apply rabbit's suggestion --- .../main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index 93be2c535..364e8e612 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -87,6 +87,6 @@ class ReLoginWithOidcService: Service() { } override fun onBind(intent: Intent?): IBinder? { - throw NotImplementedException() + return null } } -- GitLab From d0d1f37efa406de1ef2d882722084d3b2d18dad4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 29 Nov 2024 14:19:36 +0100 Subject: [PATCH 04/17] fix: apply rabbit's suggestion --- .../davdroid/ReLoginWithOidcService.kt | 3 -- .../receiver/BootCompletedReceiver.kt | 29 ++++++++++++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index 364e8e612..0e5073253 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -26,8 +26,6 @@ import android.os.Bundle import android.os.IBinder import androidx.core.content.ContextCompat import at.bitfire.davdroid.log.Logger -import org.apache.commons.lang3.NotImplementedException - class ReLoginWithOidcService: Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { @@ -83,7 +81,6 @@ class ReLoginWithOidcService: Service() { Logger.log.warning("${exception.javaClass}: can't add account: ${exception.message}") null } - } override fun onBind(intent: Intent?): IBinder? { diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index 82e2e3a62..8da28d845 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -4,12 +4,16 @@ package at.bitfire.davdroid.receiver +import android.Manifest.permission.POST_NOTIFICATIONS import android.accounts.Account import android.accounts.AccountManager import android.app.PendingIntent import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.content.pm.PackageManager.PERMISSION_GRANTED +import android.os.Build.VERSION +import android.os.Build.VERSION_CODES import androidx.core.app.NotificationManagerCompat import at.bitfire.davdroid.R import at.bitfire.davdroid.ReLoginWithOidcService @@ -35,10 +39,13 @@ class BootCompletedReceiver: BroadcastReceiver() { val eAccountType = context.getString(R.string.eelo_account_type) val accountManager = AccountManager.get(context) + val canNotify = canNotify(context) AccountUtils.getMainAccounts(context) .forEach { - if (eAccountType == it.type && !isLoggedWithOpenId(it, accountManager)) { + val needReLogin = eAccountType == it.type && !isLoggedWithOpenId(it, accountManager) + + if ( canNotify && needReLogin) { notifySwitchToOpenId(it, context) } else { // sync intervals are checked in App.onCreate() val accountSettings = AccountSettings(context, it) @@ -54,7 +61,6 @@ class BootCompletedReceiver: BroadcastReceiver() { } private fun notifySwitchToOpenId(account: Account, context: Context) { - val tag = "Switch to openID" val title = context.getString(R.string.notification_account_title, account.name) val contentText = context.getString(R.string.notification_switch_to_openId_text) val intent = generateReLoginIntent(context) @@ -68,15 +74,30 @@ class BootCompletedReceiver: BroadcastReceiver() { .build() val notificationManager = NotificationManagerCompat.from(context) - notificationManager.notifyIfPossible(tag, NOTIFY_SWITCH_TO_OPENID, notification) + notificationManager.notifyIfPossible(NOTIFICATION_TAG, NOTIFY_SWITCH_TO_OPENID, notification) } private fun generateReLoginIntent(context: Context): PendingIntent { val reLoginIntent = Intent(context, ReLoginWithOidcService::class.java) - return PendingIntent.getService(context, + return PendingIntent.getService(context, 0, reLoginIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) } + + private fun canNotify(context: Context): Boolean { + if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { + val permission = POST_NOTIFICATIONS + if (context.checkSelfPermission(permission) != PERMISSION_GRANTED) { + Logger.log.warning("Missing notification permission") + return false + } + } + return true + } + + companion object { + private const val NOTIFICATION_TAG = "SWITCH_TO_OPENID" + } } -- GitLab From 518d49b44d6abeec46f1f579db7e6c34969a8d92 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 2 Dec 2024 10:26:06 +0100 Subject: [PATCH 05/17] refactor: replace intent to start login Activity with a pending Intent --- .../kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index 0e5073253..1ecb56800 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -17,6 +17,7 @@ package at.bitfire.davdroid import android.accounts.AccountManager import android.accounts.AccountManagerFuture +import android.app.PendingIntent import android.app.Service import android.content.Context import android.content.Intent @@ -24,7 +25,6 @@ import android.os.Build.VERSION import android.os.Build.VERSION_CODES import android.os.Bundle import android.os.IBinder -import androidx.core.content.ContextCompat import at.bitfire.davdroid.log.Logger class ReLoginWithOidcService: Service() { @@ -61,7 +61,10 @@ class ReLoginWithOidcService: Service() { val intent = getIntentFromFuture(future) intent?.let { - ContextCompat.startActivity(context, it, null) + PendingIntent.getActivity(context, + 0, + it, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) } }, null) -- GitLab From 07a5b25d054b37296634bdf10a7adbfd38001642 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 2 Dec 2024 12:46:36 +0100 Subject: [PATCH 06/17] refactor: Run code of BootCompleteReceiver in a coroutine as suggested by codeRabbit --- .../receiver/BootCompletedReceiver.kt | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index 8da28d845..86e6fa1eb 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -24,6 +24,10 @@ import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.NotificationUtils.CHANNEL_GENERAL import at.bitfire.davdroid.ui.NotificationUtils.NOTIFY_SWITCH_TO_OPENID import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.launch /** * There are circumstances when Android drops automatic sync of accounts and resets them @@ -34,24 +38,36 @@ import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible */ class BootCompletedReceiver: BroadcastReceiver() { + private val job = SupervisorJob() + private val scope = CoroutineScope(job + Dispatchers.IO) + override fun onReceive(context: Context, intent: Intent) { Logger.log.info("Device has been rebooted; checking sync intervals etc.") - val eAccountType = context.getString(R.string.eelo_account_type) - val accountManager = AccountManager.get(context) - val canNotify = canNotify(context) + val pendingResult = goAsync() + scope.launch { + try { + val eAccountType = context.getString(R.string.eelo_account_type) + val accountManager = AccountManager.get(context) + val canNotify = canNotify(context) - AccountUtils.getMainAccounts(context) - .forEach { - val needReLogin = eAccountType == it.type && !isLoggedWithOpenId(it, accountManager) + AccountUtils.getMainAccounts(context) + .forEach { + val needReLogin = + eAccountType == it.type && !isLoggedWithOpenId(it, accountManager) - if ( canNotify && needReLogin) { - notifySwitchToOpenId(it, context) - } else { // sync intervals are checked in App.onCreate() - val accountSettings = AccountSettings(context, it) - accountSettings.initSync() - } + if (canNotify && needReLogin) { + notifySwitchToOpenId(it, context) + } else { // sync intervals are checked in App.onCreate() + val accountSettings = AccountSettings(context, it) + accountSettings.initSync() + } + } + } finally { + pendingResult.finish() + job.cancel() } + } } private fun isLoggedWithOpenId(account: Account, accountManager: AccountManager): Boolean { -- GitLab From aef2c959a7a6c8c19019fa736ef216b62b3d714e Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 2 Dec 2024 12:52:43 +0100 Subject: [PATCH 07/17] refactor: add stopSelf() call in ReLoginWithOidcService.kt --- .../main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index 1ecb56800..dea635dc0 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -38,6 +38,8 @@ class ReLoginWithOidcService: Service() { if (accountRemoved) { openEAccountLoginForm(accountManager, eAccountType, context) + } else { + stopSelf() } return START_STICKY @@ -66,6 +68,7 @@ class ReLoginWithOidcService: Service() { it, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) } + stopSelf() }, null) } -- GitLab From 49a3561335aa3f9d79bc67fa183032dc8f7bc6f4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 2 Dec 2024 14:12:36 +0100 Subject: [PATCH 08/17] fix: add missing flag for login Activity's intent --- .../kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index dea635dc0..9b0cc2ee3 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -62,7 +62,12 @@ class ReLoginWithOidcService: Service() { { future -> val intent = getIntentFromFuture(future) + intent?.let { + it.apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } + PendingIntent.getActivity(context, 0, it, -- GitLab From 833f3b14a8afdbb6ad7a220cdb4caf14dd4951f3 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 2 Dec 2024 14:59:44 +0100 Subject: [PATCH 09/17] fix: start the pending intent --- .../davdroid/ReLoginWithOidcService.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index 9b0cc2ee3..a8ccf8a53 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -59,20 +59,29 @@ class ReLoginWithOidcService: Service() { null, null, null, - { future -> + { future -> val intent = getIntentFromFuture(future) + if (intent == null) { + stopSelf() + return@addAccount + } - intent?.let { - it.apply { + intent.apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } - - PendingIntent.getActivity(context, - 0, - it, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + + val pendingIntent = PendingIntent.getActivity(context, + 0, + intent, + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) + + try { + pendingIntent.send() + } catch (exception: Exception){ + Logger.log.warning("ReLogin: Can't start login activity: ${exception.message}") } + stopSelf() }, null) -- GitLab From ca81aee025f90bddd5a0a9ddf74574eaf190d231 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 2 Dec 2024 15:39:27 +0100 Subject: [PATCH 10/17] fix: try to keep login form in recent app list as long as the account is not added --- .../main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt index a8ccf8a53..03d049e10 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt @@ -68,7 +68,7 @@ class ReLoginWithOidcService: Service() { } intent.apply { - flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT } val pendingIntent = PendingIntent.getActivity(context, -- GitLab From 1ab1c25f556d215ea4d6178de52506b7b159ae6b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 3 Dec 2024 09:58:47 +0100 Subject: [PATCH 11/17] feat: try to call authenticator fragment without OS's accountManager --- app/src/main/AndroidManifest.xml | 2 + .../davdroid/ReLoginWithOidcActivity.kt | 56 +++++++++++++++++++ .../receiver/BootCompletedReceiver.kt | 7 ++- 3 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae77090bf..2098e375f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -189,6 +189,8 @@ + . + */ +package at.bitfire.davdroid + +import android.accounts.AccountManager +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import at.bitfire.davdroid.log.Logger +import at.bitfire.davdroid.ui.setup.EeloAuthenticatorFragment + +class ReLoginWithOidcActivity: AppCompatActivity() { + + private lateinit var eAccountType: String + private lateinit var accountManager: AccountManager + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + eAccountType = applicationContext.getString(R.string.eelo_account_type) + accountManager = AccountManager.get(this) + setContentView(R.layout.activity_create_account) + } + + override fun onStart() { + super.onStart() + if (removeAccount()) { + supportFragmentManager.beginTransaction().apply { + replace(R.id.content, EeloAuthenticatorFragment()) + commit() + } + } else { + Logger.log.info("Vincent: Cannot remove account") + } + } + + private fun removeAccount(): Boolean { + Logger.log.info("Vincent: try to remove account") + val account = accountManager.getAccountsByType(eAccountType).firstOrNull() + + return account?.let { + accountManager.removeAccountExplicitly(it) + } ?: false + } +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index 86e6fa1eb..f3cca0409 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -16,6 +16,7 @@ import android.os.Build.VERSION import android.os.Build.VERSION_CODES import androidx.core.app.NotificationManagerCompat import at.bitfire.davdroid.R +import at.bitfire.davdroid.ReLoginWithOidcActivity import at.bitfire.davdroid.ReLoginWithOidcService import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings @@ -94,9 +95,11 @@ class BootCompletedReceiver: BroadcastReceiver() { } private fun generateReLoginIntent(context: Context): PendingIntent { - val reLoginIntent = Intent(context, ReLoginWithOidcService::class.java) + val reLoginIntent = Intent(context, ReLoginWithOidcActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + } - return PendingIntent.getService(context, + return PendingIntent.getActivity(context, 0, reLoginIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) -- GitLab From 1546be24327319aeb1fcd577f387a7097c5a4027 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 3 Dec 2024 10:15:21 +0100 Subject: [PATCH 12/17] chore: remove ReLoginWithOidcService & add Hilt annotation --- app/src/main/AndroidManifest.xml | 3 - .../davdroid/ReLoginWithOidcActivity.kt | 2 + .../davdroid/ReLoginWithOidcService.kt | 109 ------------------ .../receiver/BootCompletedReceiver.kt | 1 - 4 files changed, 2 insertions(+), 113 deletions(-) delete mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2098e375f..912982be6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -192,9 +192,6 @@ - diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt index 3462be775..92eb9ccf1 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt @@ -20,7 +20,9 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.ui.setup.EeloAuthenticatorFragment +import dagger.hilt.android.AndroidEntryPoint +@AndroidEntryPoint class ReLoginWithOidcActivity: AppCompatActivity() { private lateinit var eAccountType: String diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt deleted file mode 100644 index 03d049e10..000000000 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcService.kt +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright MURENA SAS 2024 - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package at.bitfire.davdroid - -import android.accounts.AccountManager -import android.accounts.AccountManagerFuture -import android.app.PendingIntent -import android.app.Service -import android.content.Context -import android.content.Intent -import android.os.Build.VERSION -import android.os.Build.VERSION_CODES -import android.os.Bundle -import android.os.IBinder -import at.bitfire.davdroid.log.Logger -class ReLoginWithOidcService: Service() { - - override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { - val context = this.applicationContext - - val accountManager = AccountManager.get(context) - val eAccountType = context.getString(R.string.eelo_account_type) - - val accountRemoved = removeAccount(eAccountType, accountManager) - - if (accountRemoved) { - openEAccountLoginForm(accountManager, eAccountType, context) - } else { - stopSelf() - } - - return START_STICKY - } - - private fun removeAccount(accountType: String, accountManager: AccountManager): Boolean { - val account = accountManager.getAccountsByType(accountType).firstOrNull() - - return account?.let { - accountManager.removeAccountExplicitly(it) - } ?: false - } - - private fun openEAccountLoginForm(accountManager: AccountManager, eAccountType: String, context: Context) { - accountManager.addAccount(eAccountType, - null, - null, - null, - null, - { future -> - val intent = getIntentFromFuture(future) - - if (intent == null) { - stopSelf() - return@addAccount - } - - intent.apply { - flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT - } - - val pendingIntent = PendingIntent.getActivity(context, - 0, - intent, - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT) - - try { - pendingIntent.send() - } catch (exception: Exception){ - Logger.log.warning("ReLogin: Can't start login activity: ${exception.message}") - } - - stopSelf() - }, - null) - } - - private fun getIntentFromFuture(future: AccountManagerFuture): Intent? { - val bundle = future.result - - return try { - if (VERSION.SDK_INT >= VERSION_CODES.TIRAMISU) { - bundle?.getParcelable(AccountManager.KEY_INTENT, Intent::class.java) - } else { - @Suppress("DEPRECATION") - bundle?.getParcelable(AccountManager.KEY_INTENT) - } - } catch (exception: Exception) { - Logger.log.warning("${exception.javaClass}: can't add account: ${exception.message}") - null - } - } - - override fun onBind(intent: Intent?): IBinder? { - return null - } -} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index f3cca0409..9289bc73b 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -17,7 +17,6 @@ import android.os.Build.VERSION_CODES import androidx.core.app.NotificationManagerCompat import at.bitfire.davdroid.R import at.bitfire.davdroid.ReLoginWithOidcActivity -import at.bitfire.davdroid.ReLoginWithOidcService import at.bitfire.davdroid.log.Logger import at.bitfire.davdroid.settings.AccountSettings import at.bitfire.davdroid.syncadapter.AccountUtils -- GitLab From a73756760539e3466b1c16f4127ed5c6deaa9113 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 3 Dec 2024 17:15:37 +0100 Subject: [PATCH 13/17] refactor: move stuff from onStart to onCreate to follow the logic of LoginActivity --- .../kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt index 92eb9ccf1..3c628f332 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt @@ -32,11 +32,7 @@ class ReLoginWithOidcActivity: AppCompatActivity() { super.onCreate(savedInstanceState) eAccountType = applicationContext.getString(R.string.eelo_account_type) accountManager = AccountManager.get(this) - setContentView(R.layout.activity_create_account) - } - override fun onStart() { - super.onStart() if (removeAccount()) { supportFragmentManager.beginTransaction().apply { replace(R.id.content, EeloAuthenticatorFragment()) -- GitLab From a939636502878ea650fe709480dc532f9bb08236 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 4 Dec 2024 09:47:38 +0100 Subject: [PATCH 14/17] Revert "refactor: move stuff from onStart to onCreate to follow the logic of LoginActivity" This reverts commit a73756760539e3466b1c16f4127ed5c6deaa9113. --- .../kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt index 3c628f332..92eb9ccf1 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt @@ -32,7 +32,11 @@ class ReLoginWithOidcActivity: AppCompatActivity() { super.onCreate(savedInstanceState) eAccountType = applicationContext.getString(R.string.eelo_account_type) accountManager = AccountManager.get(this) + setContentView(R.layout.activity_create_account) + } + override fun onStart() { + super.onStart() if (removeAccount()) { supportFragmentManager.beginTransaction().apply { replace(R.id.content, EeloAuthenticatorFragment()) -- GitLab From fa39d7c3b9b7c69089aaa38608172f35eeb186c4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 4 Dec 2024 10:06:39 +0100 Subject: [PATCH 15/17] refactor: small changes --- .../kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt index 92eb9ccf1..fa77d6e99 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ReLoginWithOidcActivity.kt @@ -38,12 +38,12 @@ class ReLoginWithOidcActivity: AppCompatActivity() { override fun onStart() { super.onStart() if (removeAccount()) { - supportFragmentManager.beginTransaction().apply { - replace(R.id.content, EeloAuthenticatorFragment()) - commit() - } + supportFragmentManager.beginTransaction() + .replace(android.R.id.content, EeloAuthenticatorFragment()) + .commit() } else { Logger.log.info("Vincent: Cannot remove account") + finishAndRemoveTask() } } -- GitLab From a291158b3ddf80a2949e55364016938409872496 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 4 Dec 2024 10:40:43 +0100 Subject: [PATCH 16/17] fix: try to add intent data --- .../at/bitfire/davdroid/receiver/BootCompletedReceiver.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt index 9289bc73b..30f329986 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/receiver/BootCompletedReceiver.kt @@ -24,6 +24,8 @@ import at.bitfire.davdroid.ui.NotificationUtils import at.bitfire.davdroid.ui.NotificationUtils.CHANNEL_GENERAL import at.bitfire.davdroid.ui.NotificationUtils.NOTIFY_SWITCH_TO_OPENID import at.bitfire.davdroid.ui.NotificationUtils.notifyIfPossible +import at.bitfire.davdroid.ui.setup.LoginActivity.Companion.ACCOUNT_TYPE +import at.bitfire.davdroid.ui.setup.LoginActivity.Companion.EXTRA_USERNAME import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -79,7 +81,7 @@ class BootCompletedReceiver: BroadcastReceiver() { private fun notifySwitchToOpenId(account: Account, context: Context) { val title = context.getString(R.string.notification_account_title, account.name) val contentText = context.getString(R.string.notification_switch_to_openId_text) - val intent = generateReLoginIntent(context) + val intent = generateReLoginIntent(account, context) val notification = NotificationUtils.newBuilder(context, CHANNEL_GENERAL) .setContentTitle(title) @@ -93,9 +95,11 @@ class BootCompletedReceiver: BroadcastReceiver() { notificationManager.notifyIfPossible(NOTIFICATION_TAG, NOTIFY_SWITCH_TO_OPENID, notification) } - private fun generateReLoginIntent(context: Context): PendingIntent { + private fun generateReLoginIntent(account: Account, context: Context): PendingIntent { val reLoginIntent = Intent(context, ReLoginWithOidcActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK + putExtra(ACCOUNT_TYPE, account.type) + putExtra(EXTRA_USERNAME, account.name) } return PendingIntent.getActivity(context, -- GitLab From b52f00beb77494f7f970424bad6656bfc1df2697 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 4 Dec 2024 11:09:43 +0100 Subject: [PATCH 17/17] fix: try to prevent login form to reopen after login --- app/src/main/AndroidManifest.xml | 3 ++- .../at/bitfire/davdroid/receiver/BootCompletedReceiver.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 912982be6..4628744db 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -190,7 +190,8 @@ + android:exported="false" + android:parentActivityName=".ui.AccountsActivity"/>