From ee231d9693f16ab7cf50789299010bd540ca1160 Mon Sep 17 00:00:00 2001 From: althafvly Date: Tue, 5 Aug 2025 22:18:31 +0530 Subject: [PATCH 01/10] AM: Add navigation bar changes in Account Manager --- .../davdroid/ui/AccountsDrawerHandler.kt | 25 +++++++++++++++ .../davdroid/ui/OseAccountsDrawerHandler.kt | 6 ++++ .../ui/PrivacyPolicyActivity.kt | 32 +++++++++++++++++++ .../e/accountmanager/utils/AppConstants.kt | 24 ++++++++++++++ .../e/accountmanager/utils/WebViewUtils.kt | 32 +++++++++++++++++++ app/src/main/res/values/e_strings.xml | 4 +++ app/src/ose/AndroidManifest.xml | 23 +++++++++++++ 7 files changed, 146 insertions(+) create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt index 911446220..39f63b2e0 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt @@ -5,6 +5,7 @@ package at.bitfire.davdroid.ui import android.content.ActivityNotFoundException +import android.content.ComponentName import android.content.Context import android.content.Intent import androidx.annotation.StringRes @@ -21,8 +22,10 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.EditCalendar import androidx.compose.material.icons.filled.Feedback import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.Policy import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Storage import androidx.compose.material3.HorizontalDivider @@ -54,6 +57,8 @@ import androidx.core.net.toUri import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.webdav.WebdavMountsActivity +import foundation.e.accountmanager.ui.PrivacyPolicyActivity +import foundation.e.accountmanager.utils.AppConstants import kotlinx.coroutines.launch import java.net.URI @@ -119,6 +124,14 @@ abstract class AccountsDrawerHandler { } ) + MenuEntry( + icon = Icons.Default.Policy, + title = stringResource(R.string.privacy_policy_title_nav), + onClick = { + context.startActivity(Intent(context, PrivacyPolicyActivity::class.java)) + } + ) + if (isBeta) MenuEntry( icon = Icons.Default.Feedback, @@ -157,6 +170,18 @@ abstract class AccountsDrawerHandler { context.startActivity(Intent(context, WebdavMountsActivity::class.java)) } ) + MenuEntry( + icon = Icons.Default.EditCalendar, + title = stringResource(R.string.navigation_drawer_open_webcalmanager), + onClick = { + val intent = Intent(Intent.ACTION_MAIN) + intent.component = ComponentName( + AppConstants.WEBCAL_MANAGER_PACKAGE, + AppConstants.WEBCAL_MANAGER_ACTIVITY + ) + context.startActivity(intent) + } + ) } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/OseAccountsDrawerHandler.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/OseAccountsDrawerHandler.kt index 3a2ad7745..c91142a76 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/OseAccountsDrawerHandler.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/OseAccountsDrawerHandler.kt @@ -37,6 +37,7 @@ open class OseAccountsDrawerHandler @Inject constructor(): AccountsDrawerHandler ImportantEntries(snackbarHostState) // News + /* MenuHeading(R.string.navigation_drawer_news_updates) MenuEntry( icon = painterResource(R.drawable.mastodon), @@ -45,11 +46,13 @@ open class OseAccountsDrawerHandler @Inject constructor(): AccountsDrawerHandler uriHandler.openUri(Social.fediverseUrl.toString()) } ) + */ // Tools Tools() // Support the project + /* MenuHeading(R.string.navigation_drawer_support_project) Contribute(onContribute = { uriHandler.openUri( @@ -66,9 +69,11 @@ open class OseAccountsDrawerHandler @Inject constructor(): AccountsDrawerHandler uriHandler.openUri(Social.discussionsUrl.toString()) } ) + */ // External links + /* MenuHeading(R.string.navigation_drawer_external_links) MenuEntry( icon = Icons.Default.Home, @@ -125,6 +130,7 @@ open class OseAccountsDrawerHandler @Inject constructor(): AccountsDrawerHandler ) } ) + */ } @Composable diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt new file mode 100644 index 000000000..014c9b0f4 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 eFoundation + * + * 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 foundation.e.accountmanager.ui + +import android.os.Bundle +import androidx.activity.ComponentActivity +import foundation.e.accountmanager.utils.AppConstants +import foundation.e.accountmanager.utils.WebViewUtils + + +class PrivacyPolicyActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + WebViewUtils.openCustomTab(this, AppConstants.PRIVACY_POLICY_URL) + finishAfterTransition() // Finish the activity after launching the custom tab + } +} diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt new file mode 100644 index 000000000..ece0cba61 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2025 eFoundation + * + * 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 foundation.e.accountmanager.utils + +object AppConstants { + const val WEBCAL_MANAGER_PACKAGE = "foundation.e.webcalendarmanager" + const val WEBCAL_MANAGER_ACTIVITY = "at.bitfire.icsdroid.ui.views.CalendarListActivity" + const val PRIVACY_POLICY_URL = "https://e.foundation/legal-notice-privacy/#account-manager" +} diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt new file mode 100644 index 000000000..3d23a82a5 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt @@ -0,0 +1,32 @@ +/* + * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + */ + +package foundation.e.accountmanager.utils + +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import androidx.browser.customtabs.CustomTabsIntent + +object WebViewUtils { + fun openCustomTab(context: Context, url: String) { + val packageManager = context.packageManager + val resolveInfo = packageManager.queryIntentActivities( + Intent(Intent.ACTION_VIEW, Uri.parse(url)), + PackageManager.MATCH_DEFAULT_ONLY + ) + + if (resolveInfo.isNotEmpty()) { + val customTabsIntent = CustomTabsIntent.Builder() + .setShowTitle(true).build() + customTabsIntent.intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + customTabsIntent.launchUrl(context, Uri.parse(url)) + } else { + // Fallback to default browser + val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url)) + context.startActivity(intent) + } + } +} diff --git a/app/src/main/res/values/e_strings.xml b/app/src/main/res/values/e_strings.xml index 1356803cb..5c277d717 100644 --- a/app/src/main/res/values/e_strings.xml +++ b/app/src/main/res/values/e_strings.xml @@ -29,4 +29,8 @@ Use a specific server Collapse Expand + + "Account Manager's Privacy Policy" + "Privacy Policy" + Web Calendar Manager diff --git a/app/src/ose/AndroidManifest.xml b/app/src/ose/AndroidManifest.xml index f645872f4..51764bec1 100644 --- a/app/src/ose/AndroidManifest.xml +++ b/app/src/ose/AndroidManifest.xml @@ -227,6 +227,29 @@ android:resource="@xml/e_sync_e_notes" /> + + + + + + + + + + + -- GitLab From 39b0bd201e5cb705a0f7c3650120aa344b6ed31a Mon Sep 17 00:00:00 2001 From: althafvly Date: Mon, 4 Aug 2025 20:19:35 +0530 Subject: [PATCH 02/10] AM: Catch exceptions during cancellation --- .../RefreshCollectionsWorker.kt | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt b/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt index 5d48e1da4..1cf93cea8 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/servicedetection/RefreshCollectionsWorker.kt @@ -37,8 +37,11 @@ import dagger.assisted.AssistedInject import foundation.e.accountmanager.AccountTypes import kotlinx.coroutines.flow.map import kotlinx.coroutines.runInterruptible +import java.io.IOException +import java.io.InterruptedIOException import java.util.logging.Level import java.util.logging.Logger +import kotlin.coroutines.cancellation.CancellationException /** * Refreshes list of home sets and their respective collections of a service type (CardDAV or CalDAV). @@ -195,18 +198,30 @@ class RefreshCollectionsWorker @AssistedInject constructor( settingsIntent ) return Result.failure() - } catch(e: Exception) { - logger.log(Level.SEVERE, "Couldn't refresh collection list", e) + } catch (e: Exception) { + when (e) { + is CancellationException -> throw e - val debugIntent = DebugInfoActivity.IntentBuilder(applicationContext) - .withCause(e) - .withAccount(account) - .build() - notifyRefreshError( - applicationContext.getString(R.string.refresh_collections_worker_refresh_couldnt_refresh), - debugIntent - ) - return Result.failure() + is IOException -> { + logger.log(Level.WARNING, "I/O issue while refreshing collection list", e) + return Result.failure() + } + + else -> { + logger.log(Level.SEVERE, "Couldn't refresh collection list", e) + + val debugIntent = DebugInfoActivity.IntentBuilder(applicationContext) + .withCause(e) + .withAccount(account) + .build() + + notifyRefreshError( + applicationContext.getString(R.string.refresh_collections_worker_refresh_couldnt_refresh), + debugIntent + ) + return Result.failure() + } + } } // update push registrations -- GitLab From 8911868c368b8c14455fff9351eea64886059083 Mon Sep 17 00:00:00 2001 From: althafvly Date: Wed, 6 Aug 2025 20:35:34 +0530 Subject: [PATCH 03/10] AM: Trigger sync after adding new items --- app/build.gradle.kts | 2 +- .../davdroid/repository/AccountRepository.kt | 3 + .../at/bitfire/davdroid/sync/SyncManager.kt | 11 ++- .../ui/account/AccountSettingsModel.kt | 2 + .../e/accountmanager/sync/OneTimeSyncModel.kt | 96 +++++++++++++++++++ .../sync/OneTimeSyncModelEntryPoint.kt | 28 ++++++ .../sync/SyncBroadcastReceiver.kt | 32 +++++++ .../e/accountmanager/utils/AccountHelper.kt | 22 +++++ .../e/accountmanager/utils/AppConstants.kt | 2 + app/src/ose/AndroidManifest.xml | 4 + 10 files changed, 197 insertions(+), 5 deletions(-) create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt create mode 100644 app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 208a90be9..cd13cb940 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -30,7 +30,7 @@ android { setProperty("archivesBaseName", "davx5-ose-$versionName") - minSdk = 24 // Android 7.0 + minSdk = 31 // Android 12 - canScheduleExactAlarms minimum requirement targetSdk = 36 // Android 16 buildConfigField("boolean", "customCertsUI", "true") diff --git a/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt b/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt index 282a3baba..0420512b9 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/repository/AccountRepository.kt @@ -108,6 +108,9 @@ class AccountRepository @Inject constructor( // set up automatic sync (processes inserted services) automaticSyncManager.get().updateAutomaticSync(account) + // Schedule sync + AccountHelper.scheduleSyncWithDelay(context) + } catch(e: InvalidAccountException) { logger.log(Level.SEVERE, "Couldn't access account settings", e) return null diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncManager.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncManager.kt index f2ae8898a..722f955a4 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncManager.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/SyncManager.kt @@ -734,11 +734,10 @@ abstract class SyncManager, out CollectionType: L */ private fun handleException(e: Throwable, local: LocalResource<*>?, remote: HttpUrl?) { var message: String - var isNetworkAvailable = true + val isNetworkAvailable = SystemUtils.isNetworkAvailable(context) when (e) { is IOException -> { logger.log(Level.WARNING, "I/O error", e) - isNetworkAvailable = SystemUtils.isNetworkAvailable(context) if (isNetworkAvailable) { syncResult.numIoExceptions++ } @@ -747,13 +746,17 @@ abstract class SyncManager, out CollectionType: L is UnauthorizedException -> { logger.log(Level.SEVERE, "Not authorized anymore", e) - syncResult.numAuthExceptions++ + if (isNetworkAvailable) { + syncResult.numAuthExceptions++ + } message = context.getString(R.string.sync_error_authentication_failed) } is HttpException, is DavException -> { logger.log(Level.SEVERE, "HTTP/DAV exception", e) - syncResult.numHttpExceptions++ + if (isNetworkAvailable) { + syncResult.numHttpExceptions++ + } message = context.getString(R.string.sync_error_http_dav, e.localizedMessage) } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt index 539d202d6..84146d0c8 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/account/AccountSettingsModel.kt @@ -26,6 +26,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.accountmanager.utils.AccountHelper import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow @@ -234,6 +235,7 @@ class AccountSettingsModel @AssistedInject constructor( fun updateCredentials(credentials: Credentials) = CoroutineScope(defaultDispatcher).launch { accountSettings.credentials(credentials) + AccountHelper.syncMailAccounts(context) reload() } diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt new file mode 100644 index 000000000..bedc61939 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2025 eFoundation + * + * 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 foundation.e.accountmanager.sync + +import android.accounts.AccountManager +import android.content.Context +import androidx.work.WorkManager +import at.bitfire.davdroid.repository.AccountRepository +import at.bitfire.davdroid.repository.DavServiceRepository +import at.bitfire.davdroid.servicedetection.RefreshCollectionsWorker +import at.bitfire.davdroid.settings.AccountSettings.Companion.KEY_SETTINGS_VERSION +import at.bitfire.davdroid.sync.worker.SyncWorkerManager +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import javax.inject.Inject + +class OneTimeSyncModel @Inject constructor( + private val accountRepository: AccountRepository, + private val serviceRepository: DavServiceRepository, + @ApplicationContext val context: Context, + private val syncWorkerManager: SyncWorkerManager +) { + + fun requestSync() { + CoroutineScope(Dispatchers.Default).launch { + val workManager = WorkManager.getInstance(context) + val accountManager = AccountManager.get(context) + val workNames = mutableListOf() + val allAccounts = accountRepository.getAll() + val startTime = System.currentTimeMillis() + + for (service in serviceRepository.getAll()) { + val account = retry(maxRetries = 3, delayMs = 2000) { + allAccounts.find { it.name == service.accountName } + } + + var version: String? = null + retry(maxRetries = 3, delayMs = 2000) { + account?.let { + version = accountManager.getUserData(it, KEY_SETTINGS_VERSION) + } + } + + if (version != null) { + val (name, _) = RefreshCollectionsWorker.enqueue(context, service.id) + workNames.add(name) + } + } + + for (name in workNames) { + waitForUniqueWorkToFinish(workManager, name) + } + + val elapsed = System.currentTimeMillis() - startTime + if (elapsed < 3_000) delay(3_000 - elapsed) + + for (account in allAccounts) { + syncWorkerManager.enqueueOneTimeAllAuthorities(account, manual = true) + } + } + } + + private suspend fun retry(maxRetries: Int, delayMs: Long, block: suspend () -> T?): T? { + repeat(maxRetries - 1) { + block()?.let { return it } + delay(delayMs) + } + return block() + } + + private suspend fun waitForUniqueWorkToFinish(workManager: WorkManager, uniqueWorkName: String) { + while (true) { + val infos = workManager.getWorkInfosForUniqueWork(uniqueWorkName).get() + if (infos.all { it.state.isFinished }) break + delay(500) + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt new file mode 100644 index 000000000..3f1977df7 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2025 eFoundation + * + * 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 foundation.e.accountmanager.sync + +import dagger.hilt.EntryPoint +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent + +@EntryPoint +@InstallIn(SingletonComponent::class) +interface OneTimeSyncModelEntryPoint { + fun syncModel(): OneTimeSyncModel +} diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt new file mode 100644 index 000000000..ef98b03b3 --- /dev/null +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2025 eFoundation + * + * 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 foundation.e.accountmanager.sync + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.util.Log +import dagger.hilt.android.EntryPointAccessors + +class SyncBroadcastReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent?) { + val entryPoint = EntryPointAccessors.fromApplication(context) + val syncModel = entryPoint.syncModel() + syncModel.requestSync() + } +} diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt index 9f93a6eb7..c00b3bf5f 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt @@ -19,10 +19,15 @@ package foundation.e.accountmanager.utils import android.accounts.Account import android.accounts.AccountManager +import android.app.AlarmManager +import android.app.PendingIntent import android.content.ComponentName import android.content.Context import android.content.Intent +import android.os.Build import foundation.e.accountmanager.AccountTypes +import foundation.e.accountmanager.sync.SyncBroadcastReceiver +import java.util.concurrent.TimeUnit object AccountHelper { private const val MAIL_PACKAGE = "foundation.e.mail" @@ -52,4 +57,21 @@ object AccountHelper { intent.action = ACTION_PREFIX + "create" context.sendBroadcast(intent) } + + fun scheduleSyncWithDelay(context: Context) { + val intent = Intent(context, SyncBroadcastReceiver::class.java) + val pendingIntent = PendingIntent.getBroadcast( + context, + 0, + intent, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + + val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager + val triggerTime = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(AppConstants.SYNC_DELAY_SECONDS) + + if (alarmManager.canScheduleExactAlarms()) { + alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerTime, pendingIntent) + } + } } diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt index ece0cba61..bbb450996 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt @@ -21,4 +21,6 @@ object AppConstants { const val WEBCAL_MANAGER_PACKAGE = "foundation.e.webcalendarmanager" const val WEBCAL_MANAGER_ACTIVITY = "at.bitfire.icsdroid.ui.views.CalendarListActivity" const val PRIVACY_POLICY_URL = "https://e.foundation/legal-notice-privacy/#account-manager" + + const val SYNC_DELAY_SECONDS = 5L } diff --git a/app/src/ose/AndroidManifest.xml b/app/src/ose/AndroidManifest.xml index 51764bec1..4289641fa 100644 --- a/app/src/ose/AndroidManifest.xml +++ b/app/src/ose/AndroidManifest.xml @@ -9,6 +9,7 @@ + @@ -250,6 +251,9 @@ android:value="205" /> + + -- GitLab From 835c2fa15d963c1d2468bac7f873e08ca9e40be6 Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 14 Aug 2025 19:01:35 +0530 Subject: [PATCH 04/10] AM: Add ose build directory to .gitignore --- app/.gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/app/.gitignore b/app/.gitignore index 6eeec870d..ccccca9c4 100644 --- a/app/.gitignore +++ b/app/.gitignore @@ -1,2 +1,3 @@ build target +ose -- GitLab From 62e94edd388824a4006b8afdb43d3f7f6b3ffd9a Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 14 Aug 2025 19:02:14 +0530 Subject: [PATCH 05/10] AM: Remove app from launcher visibility --- app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6cf7d65d7..50d040950 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -78,7 +78,6 @@ android:exported="true"> - -- GitLab From e56d480c858bb9df0c331153a1923a802618375d Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 14 Aug 2025 19:14:42 +0530 Subject: [PATCH 06/10] AM: Disable FCM-based push notifications --- app/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index cd13cb940..951f8f182 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -272,7 +272,7 @@ dependencies { // See: https://codeberg.org/UnifiedPush/android-connector/src/commit/28cb0d622ed0a972996041ab9cc85b701abc48c6/connector/build.gradle#L56-L59 exclude(group = "com.google.crypto.tink", module = "tink") } - implementation(libs.unifiedpush.fcm) + //implementation(libs.unifiedpush.fcm) // force some versions for compatibility with our minSdk level (see version catalog for details) implementation(libs.commons.codec) -- GitLab From 447070b89cac09b57bf521f71793daabd4280123 Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 14 Aug 2025 20:14:29 +0530 Subject: [PATCH 07/10] AM: Build separate APKs for different ABIs --- app/build.gradle.kts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 951f8f182..70193ab13 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -179,6 +179,15 @@ android { } } } + + splits { + abi { + isEnable = true + reset() + include("armeabi-v7a", "arm64-v8a", "x86", "x86_64") + isUniversalApk = false + } + } } fun retrieveKey(keyName: String): String { -- GitLab From ef3c726e4ef8c88746fd80354bcc669d9f8c2c0d Mon Sep 17 00:00:00 2001 From: althafvly Date: Thu, 21 Aug 2025 18:55:44 +0530 Subject: [PATCH 08/10] AM: Fix sync always pending on Android 14+ See: https://github.com/bitfireAT/davx5-ose/issues/1458 --- .../davdroid/sync/adapter/SyncAdapterImpl.kt | 9 ---- .../sync/adapter/SyncFrameworkIntegration.kt | 50 +++++++++---------- 2 files changed, 23 insertions(+), 36 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt index 2b956065d..270457b56 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncAdapterImpl.kt @@ -117,15 +117,6 @@ class SyncAdapterImpl @Inject constructor( logger.fine("Starting OneTimeSyncWorker for $account $authority and waiting for it") val workerName = syncWorkerManager.enqueueOneTime(account, dataType = SyncDataType.Companion.fromAuthority(authority), fromUpload = upload) - // Android 14+ does not handle pending sync state correctly. - // As a defensive workaround, we can cancel specifically this still pending sync only - // See: https://github.com/bitfireAT/davx5-ose/issues/1458 - if (Build.VERSION.SDK_INT >= 34) { - logger.fine("Android 14+ bug: Canceling forever pending sync adapter framework sync request for " + - "account=$account authority=$authority upload=$upload") - syncFrameworkIntegration.cancelSync(account, authority, extras) - } - /* Because we are not allowed to observe worker state on a background thread, we can not use it to block the sync adapter. Instead we use a Flow to get notified when the sync has finished. */ diff --git a/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncFrameworkIntegration.kt b/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncFrameworkIntegration.kt index 539ea2bec..2e05e7bda 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncFrameworkIntegration.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/sync/adapter/SyncFrameworkIntegration.kt @@ -183,38 +183,34 @@ class SyncFrameworkIntegration @Inject constructor( * @return flow emitting true if any of the given data types has a sync pending, false otherwise */ @OptIn(ExperimentalCoroutinesApi::class) - fun isSyncPending(account: Account, dataTypes: Iterable): Flow = - if (Build.VERSION.SDK_INT >= 34) { - // On Android 14+ pending sync checks always return true (bug), so we don't need to check. - // See: https://github.com/bitfireAT/davx5-ose/issues/1458 - flowOf(false) - } else { - val authorities = dataTypes.flatMap { it.possibleAuthorities() } - - // Use address book accounts if needed - val accountsFlow = if (dataTypes.contains(SyncDataType.CONTACTS)) - localAddressBookStore.get().getAddressBookAccountsFlow(account) - else - flowOf(listOf(account)) - - // Observe sync pending state for the given accounts and authorities - accountsFlow.flatMapLatest { accounts -> - callbackFlow { - // Observe sync pending state - val listener = ContentResolver.addStatusChangeListener( - ContentResolver.SYNC_OBSERVER_TYPE_PENDING - ) { + fun isSyncPending(account: Account, dataTypes: Iterable): Flow { + val authorities = dataTypes.flatMap { it.possibleAuthorities() } + + val accountsFlow = if (dataTypes.contains(SyncDataType.CONTACTS)) + localAddressBookStore.get().getAddressBookAccountsFlow(account) + else + flowOf(listOf(account)) + + return accountsFlow.flatMapLatest { accounts -> + callbackFlow { + val listener = ContentResolver.addStatusChangeListener( + ContentResolver.SYNC_OBSERVER_TYPE_PENDING + ) { + runCatching { trySend(anyPendingSync(accounts, authorities)) } + } - // Emit initial value - trySend(anyPendingSync(accounts, authorities)) + // Emit initial value + runCatching { trySend(anyPendingSync(accounts, authorities)) } - // Clean up listener on close - awaitClose { ContentResolver.removeStatusChangeListener(listener) } + awaitClose { + ContentResolver.removeStatusChangeListener(listener) } - }.distinctUntilChanged() - } + } + }.distinctUntilChanged() + } + /** * Check if any of the given accounts and authorities have a sync pending. -- GitLab From cdc61e61730f538ff70bc09ce377c7afc0792366 Mon Sep 17 00:00:00 2001 From: althafvly Date: Wed, 27 Aug 2025 12:19:00 +0530 Subject: [PATCH 09/10] AM: Change past event time limit to 365 days --- .../main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt index d26e1a1a7..b114c024b 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/settings/AccountSettings.kt @@ -390,7 +390,7 @@ class AccountSettings @AssistedInject constructor( * - n>0: entries more than n days in the past won't be synchronized */ const val KEY_TIME_RANGE_PAST_DAYS = "time_range_past_days" - const val DEFAULT_TIME_RANGE_PAST_DAYS = 90 + const val DEFAULT_TIME_RANGE_PAST_DAYS = 365 /** * Whether a default alarm shall be assigned to received events/tasks which don't have an alarm. -- GitLab From a42e197865ba447d1375e121b290e35b66cee071 Mon Sep 17 00:00:00 2001 From: althafvly Date: Tue, 2 Sep 2025 15:47:26 +0530 Subject: [PATCH 10/10] AM: Fix copyright text --- .../foundation/e/accountmanager/AccountTypes.kt | 2 +- .../foundation/e/accountmanager/Authority.kt | 2 +- .../e/accountmanager/auth/AccountReceiver.kt | 2 +- .../e/accountmanager/network/OAuthMurena.kt | 2 +- .../e/accountmanager/pref/AuthStatePrefUtils.kt | 2 +- .../e/accountmanager/sync/OneTimeSyncModel.kt | 2 +- .../sync/OneTimeSyncModelEntryPoint.kt | 2 +- .../sync/SyncBroadcastReceiver.kt | 2 +- .../MurenaAccountAuthenticatorService.kt | 2 +- .../MurenaAddressBookAuthenticatorService.kt | 2 +- .../accountmanager/ui/PrivacyPolicyActivity.kt | 2 +- .../e/accountmanager/ui/components/ESwitch.kt | 2 +- .../ui/setup/MurenaAccountDetailsPageContent.kt | 2 +- .../e/accountmanager/ui/setup/MurenaLogin.kt | 2 +- .../accountmanager/ui/setup/MurenaLoginModel.kt | 2 +- .../ui/setup/MurenaLogoutActivity.kt | 2 +- .../e/accountmanager/utils/AccountHelper.kt | 2 +- .../e/accountmanager/utils/AppConstants.kt | 2 +- .../e/accountmanager/utils/SystemUtils.kt | 2 +- .../e/accountmanager/utils/WebViewUtils.kt | 17 +++++++++++++++-- 20 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt b/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt index 50e1ec65e..fdce88f3f 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/AccountTypes.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/Authority.kt b/app/src/main/kotlin/foundation/e/accountmanager/Authority.kt index 71ba6de4c..324ec3063 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/Authority.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/Authority.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/auth/AccountReceiver.kt b/app/src/main/kotlin/foundation/e/accountmanager/auth/AccountReceiver.kt index d212a6908..8603d6117 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/auth/AccountReceiver.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/auth/AccountReceiver.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/network/OAuthMurena.kt b/app/src/main/kotlin/foundation/e/accountmanager/network/OAuthMurena.kt index e053bb7dd..eb6273e1b 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/network/OAuthMurena.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/network/OAuthMurena.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/pref/AuthStatePrefUtils.kt b/app/src/main/kotlin/foundation/e/accountmanager/pref/AuthStatePrefUtils.kt index db5f3c71d..ff70c93c0 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/pref/AuthStatePrefUtils.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/pref/AuthStatePrefUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt index bedc61939..b065c6992 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt index 3f1977df7..b2dae8f5c 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/OneTimeSyncModelEntryPoint.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt index ef98b03b3..c39faba72 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/SyncBroadcastReceiver.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAccountAuthenticatorService.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAccountAuthenticatorService.kt index 731b8eaaa..ee35c4a69 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAccountAuthenticatorService.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAccountAuthenticatorService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAddressBookAuthenticatorService.kt b/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAddressBookAuthenticatorService.kt index 3527e4d3e..6ca351412 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAddressBookAuthenticatorService.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/sync/account/MurenaAddressBookAuthenticatorService.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt index 014c9b0f4..09894800e 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/PrivacyPolicyActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/components/ESwitch.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/components/ESwitch.kt index 6a678300c..26754c4c1 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/ui/components/ESwitch.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/components/ESwitch.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaAccountDetailsPageContent.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaAccountDetailsPageContent.kt index 11726a069..731bd3360 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaAccountDetailsPageContent.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaAccountDetailsPageContent.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogin.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogin.kt index 56ceec360..651d9a2fb 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogin.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogin.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLoginModel.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLoginModel.kt index dbe8ea322..5d59c33aa 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLoginModel.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLoginModel.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogoutActivity.kt b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogoutActivity.kt index 5d37d606b..64862d040 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogoutActivity.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/ui/setup/MurenaLogoutActivity.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt index c00b3bf5f..9ae52d232 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/AccountHelper.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt index bbb450996..99474967a 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/AppConstants.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt index 1739bf2bf..c211e6990 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/SystemUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2025 eFoundation + * Copyright (C) 2025 e Foundation * * 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 diff --git a/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt b/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt index 3d23a82a5..9a9f0b189 100644 --- a/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt +++ b/app/src/main/kotlin/foundation/e/accountmanager/utils/WebViewUtils.kt @@ -1,7 +1,20 @@ /* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. + * Copyright (C) 2025 e Foundation + * + * 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 foundation.e.accountmanager.utils import android.content.Context -- GitLab