From 777b419a602c1c9ae6e96886cda7860484f4e180 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 15:17:17 +0100 Subject: [PATCH 01/12] Renamed `AccountsActivity` to `MainActivity` Signed-off-by: Arnau Mora --- app/src/main/AndroidManifest.xml | 12 ++++++------ .../kotlin/at/bitfire/davdroid/db/AppDatabase.kt | 7 ++++--- .../sync/account/AddressBookAuthenticatorService.kt | 4 ++-- .../ui/{AccountsActivity.kt => MainActivity.kt} | 2 +- .../main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt | 2 +- app/src/main/res/xml/sync_prefs.xml | 2 +- 6 files changed, 15 insertions(+), 14 deletions(-) rename app/src/main/kotlin/at/bitfire/davdroid/ui/{AccountsActivity.kt => MainActivity.kt} (97%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 173b80fc0..4e2202fb7 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -62,7 +62,7 @@ @@ -73,12 +73,12 @@ + android:parentActivityName=".ui.MainActivity"/> @@ -106,7 +106,7 @@ @@ -134,7 +134,7 @@ + android:parentActivityName=".ui.MainActivity" /> ?, options: Bundle?): Bundle { - val intent = Intent(context, AccountsActivity::class.java) + val intent = Intent(context, MainActivity::class.java) intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response) return bundleOf(AccountManager.KEY_INTENT to intent) } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt similarity index 97% rename from app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsActivity.kt rename to app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index ad6752eb2..7a37b2fc1 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -16,7 +16,7 @@ import javax.inject.Inject @AndroidEntryPoint -class AccountsActivity: AppCompatActivity() { +class MainActivity: AppCompatActivity() { @Inject lateinit var accountsDrawerHandler: AccountsDrawerHandler diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt index 4b1038a79..72d97128a 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt @@ -86,7 +86,7 @@ object UiUtils { ShortcutInfo.Builder(context, SHORTCUT_SYNC_ALL) .setIcon(Icon.createWithResource(context, R.drawable.ic_sync_shortcut)) .setShortLabel(context.getString(R.string.accounts_sync_all)) - .setIntent(Intent(Intent.ACTION_SYNC, null, context, AccountsActivity::class.java)) + .setIntent(Intent(Intent.ACTION_SYNC, null, context, MainActivity::class.java)) .build() ) } catch(e: Exception) { diff --git a/app/src/main/res/xml/sync_prefs.xml b/app/src/main/res/xml/sync_prefs.xml index 6e693253d..d14c48a24 100644 --- a/app/src/main/res/xml/sync_prefs.xml +++ b/app/src/main/res/xml/sync_prefs.xml @@ -12,7 +12,7 @@ From ddf881a504d5193561f70b63500b4d4a8f1efbf7 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 17:29:55 +0100 Subject: [PATCH 02/12] Added navigation Signed-off-by: Arnau Mora --- app/build.gradle.kts | 3 ++ .../at/bitfire/davdroid/ui/AccountsScreen.kt | 41 ++++++++++++++++ .../at/bitfire/davdroid/ui/MainActivity.kt | 48 ++++++++----------- .../bitfire/davdroid/ui/navigation/Routes.kt | 6 +++ gradle/libs.versions.toml | 5 ++ 5 files changed, 74 insertions(+), 29 deletions(-) create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e56c50081..795cdd520 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,6 +8,7 @@ plugins { alias(libs.plugins.compose.compiler) alias(libs.plugins.hilt) alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.serialization) alias(libs.plugins.ksp) } @@ -163,6 +164,7 @@ dependencies { implementation(platform(libs.compose.bom)) implementation(libs.compose.material3) implementation(libs.compose.materialIconsExtended) + implementation(libs.compose.navigation) implementation(libs.compose.runtime.livedata) debugImplementation(libs.compose.ui.tooling) implementation(libs.compose.ui.toolingPreview) @@ -189,6 +191,7 @@ dependencies { @Suppress("RedundantSuppression") implementation(libs.dnsjava) implementation(libs.guava) + implementation(libs.kotlinx.serialization) implementation(libs.mikepenz.aboutLibraries) implementation(libs.nsk90.kstatemachine) implementation(libs.okhttp.base) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt index 2b0e878f2..b40251586 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt @@ -2,11 +2,13 @@ package at.bitfire.davdroid.ui import android.Manifest import android.accounts.Account +import android.app.Activity import android.content.Intent import android.net.Uri import android.os.Build import android.provider.Settings import androidx.activity.compose.BackHandler +import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -67,17 +69,56 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavBackStackEntry +import androidx.navigation.toRoute import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R +import at.bitfire.davdroid.ui.account.AccountActivity import at.bitfire.davdroid.ui.account.AccountProgress import at.bitfire.davdroid.ui.composable.ActionCard import at.bitfire.davdroid.ui.composable.ProgressBar +import at.bitfire.davdroid.ui.intro.IntroActivity +import at.bitfire.davdroid.ui.navigation.Routes +import at.bitfire.davdroid.ui.setup.LoginActivity import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.isGranted import com.google.accompanist.permissions.rememberPermissionState import kotlinx.coroutines.delay import kotlinx.coroutines.launch +@Composable +fun AccountsScreen(backStackEntry: NavBackStackEntry, accountsDrawerHandler: AccountsDrawerHandler) { + val route = backStackEntry.toRoute() + val context = LocalContext.current + val activity = context as? Activity + + val introActivityLauncher = rememberLauncherForActivityResult(IntroActivity.Contract) { cancelled -> + if (cancelled) activity?.finish() + } + + AccountsScreen( + initialSyncAccounts = route.syncAccounts, + onShowAppIntro = { + introActivityLauncher.launch(null) + }, + accountsDrawerHandler = accountsDrawerHandler, + onAddAccount = { + // eventually this will become a navigation + context.startActivity(Intent(context, LoginActivity::class.java)) + }, + onShowAccount = { account -> + // eventually this will become a navigation + val intent = Intent(context, AccountActivity::class.java) + intent.putExtra(AccountActivity.EXTRA_ACCOUNT, account) + context.startActivity(intent) + }, + onManagePermissions = { + // eventually this will become a navigation + context.startActivity(Intent(context, PermissionsActivity::class.java)) + } + ) +} + @Composable fun AccountsScreen( initialSyncAccounts: Boolean, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index 7a37b2fc1..d82bef8c0 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -8,9 +8,10 @@ import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import at.bitfire.davdroid.ui.account.AccountActivity -import at.bitfire.davdroid.ui.intro.IntroActivity -import at.bitfire.davdroid.ui.setup.LoginActivity +import androidx.navigation.compose.NavHost +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import at.bitfire.davdroid.ui.navigation.Routes import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -21,38 +22,27 @@ class MainActivity: AppCompatActivity() { @Inject lateinit var accountsDrawerHandler: AccountsDrawerHandler - private val introActivityLauncher = registerForActivityResult(IntroActivity.Contract) { cancelled -> - if (cancelled) - finish() - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - // handle "Sync all" intent from launcher shortcut - val syncAccounts = intent.action == Intent.ACTION_SYNC - setContent { - AccountsScreen( - initialSyncAccounts = syncAccounts, - onShowAppIntro = { - introActivityLauncher.launch(null) - }, - accountsDrawerHandler = accountsDrawerHandler, - onAddAccount = { - startActivity(Intent(this, LoginActivity::class.java)) - }, - onShowAccount = { account -> - val intent = Intent(this, AccountActivity::class.java) - intent.putExtra(AccountActivity.EXTRA_ACCOUNT, account) - startActivity(intent) - }, - onManagePermissions = { - startActivity(Intent(this, PermissionsActivity::class.java)) - } - ) + NavHost( + navController = rememberNavController(), + startDestination = accountsFromIntent() + ) { + composable { AccountsScreen(it, accountsDrawerHandler) } + } } } + /** + * Initializes the accounts route from the current intent data. + * Checks whether the action is [Intent.ACTION_SYNC]. + */ + private fun accountsFromIntent() = Routes.Accounts( + // handle "Sync all" intent from launcher shortcut + syncAccounts = intent.action == Intent.ACTION_SYNC + ) + } \ No newline at end of file diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt new file mode 100644 index 000000000..a2a0cb226 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt @@ -0,0 +1,6 @@ +package at.bitfire.davdroid.ui.navigation + +object Routes { + @Serializable + data class Accounts(val syncAccounts: Boolean) +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d271f8780..93205fd69 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,6 +24,7 @@ bitfire-ical4android = "c1ed0a0570" bitfire-vcard4android = "ae5d609f92" compose-accompanist = "0.37.0" compose-bom = "2024.12.01" +compose-navigation = "2.8.5" dnsjava = "3.6.0" glance = "1.1.1" guava = "33.4.0-android" @@ -31,6 +32,7 @@ hilt = "2.54" # keep in sync with ksp version kotlin = "2.1.0" kotlinx-coroutines = "1.10.1" +kotlinx-serialization = "1.7.3" # see https://github.com/google/ksp/releases for version numbers ksp = "2.1.0-1.0.29" mikepenz-aboutLibraries = "11.2.3" @@ -72,6 +74,7 @@ compose-accompanist-permissions = { module = "com.google.accompanist:accompanist compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" } compose-material3 = { group = "androidx.compose.material3", name = "material3" } compose-materialIconsExtended = { module = "androidx.compose.material:material-icons-extended" } +compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "compose-navigation" } compose-runtime-livedata = { module = "androidx.compose.runtime:runtime-livedata" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } compose-ui-toolingPreview = { module = "androidx.compose.ui:ui-tooling-preview" } @@ -85,6 +88,7 @@ hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", vers junit = { module = "junit:junit", version = "4.13.2" } kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" } kotlinx-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } +kotlinx-serialization = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } mikepenz-aboutLibraries = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "mikepenz-aboutLibraries" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } mockk-android = { module = "io.mockk:mockk-android", version.ref = "mockk" } @@ -106,5 +110,6 @@ android-application = { id = "com.android.application", version.ref = "android-a compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } mikepenz-aboutLibraries = { id = "com.mikepenz.aboutlibraries.plugin", version.ref = "mikepenz-aboutLibraries" } From a02559ca9a05ec72b5cb3f7c19dff2a621c91052 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 17:32:56 +0100 Subject: [PATCH 03/12] Fixed import Signed-off-by: Arnau Mora --- app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt index a2a0cb226..8c1aebec4 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt @@ -1,5 +1,7 @@ package at.bitfire.davdroid.ui.navigation +import kotlinx.serialization.Serializable + object Routes { @Serializable data class Accounts(val syncAccounts: Boolean) From a544e53267995fd132e461d5eb36c9a8f4ca6121 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 17:33:57 +0100 Subject: [PATCH 04/12] Created `LocalNavController` Signed-off-by: Arnau Mora --- .../at/bitfire/davdroid/ui/MainActivity.kt | 16 +++++++++++----- .../davdroid/ui/composition/NavController.kt | 6 ++++++ 2 files changed, 17 insertions(+), 5 deletions(-) create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index d82bef8c0..b8bce58db 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -8,9 +8,11 @@ import android.content.Intent import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity +import androidx.compose.runtime.CompositionLocalProvider import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import at.bitfire.davdroid.ui.composition.LocalNavController import at.bitfire.davdroid.ui.navigation.Routes import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -27,11 +29,15 @@ class MainActivity: AppCompatActivity() { super.onCreate(savedInstanceState) setContent { - NavHost( - navController = rememberNavController(), - startDestination = accountsFromIntent() - ) { - composable { AccountsScreen(it, accountsDrawerHandler) } + val navController = rememberNavController() + + CompositionLocalProvider(LocalNavController provides navController) { + NavHost( + navController = rememberNavController(), + startDestination = accountsFromIntent() + ) { + composable { AccountsScreen(it, accountsDrawerHandler) } + } } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt new file mode 100644 index 000000000..5f09fa567 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt @@ -0,0 +1,6 @@ +package at.bitfire.davdroid.ui.composition + +import androidx.compose.runtime.compositionLocalOf +import androidx.navigation.NavController + +val LocalNavController = compositionLocalOf { error("No NavController attached.") } From b8b38b600afad8c23c3fba7396c5ed53ad2b11f6 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 17:34:19 +0100 Subject: [PATCH 05/12] Typo Signed-off-by: Arnau Mora --- app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index b8bce58db..fa3a8e549 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -33,7 +33,7 @@ class MainActivity: AppCompatActivity() { CompositionLocalProvider(LocalNavController provides navController) { NavHost( - navController = rememberNavController(), + navController = navController, startDestination = accountsFromIntent() ) { composable { AccountsScreen(it, accountsDrawerHandler) } From 0d9be985471f2a62a988051463bc6449beb7bba4 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 18:07:40 +0100 Subject: [PATCH 06/12] Migrated IntroActivity Signed-off-by: Arnau Mora --- app/src/main/AndroidManifest.xml | 1 - .../at/bitfire/davdroid/ui/AccountsScreen.kt | 18 +++-- .../at/bitfire/davdroid/ui/MainActivity.kt | 2 + .../bitfire/davdroid/ui/PermissionsScreen.kt | 4 +- .../at/bitfire/davdroid/ui/TasksScreen.kt | 4 +- .../intro/BatteryOptimizationsPageContent.kt | 4 +- .../davdroid/ui/intro/IntroActivity.kt | 72 ------------------- .../bitfire/davdroid/ui/intro/IntroScreen.kt | 32 +++++++++ .../davdroid/ui/intro/OpenSourcePage.kt | 4 +- .../bitfire/davdroid/ui/navigation/Routes.kt | 3 + 10 files changed, 56 insertions(+), 88 deletions(-) delete mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4e2202fb7..7038827c0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -60,7 +60,6 @@ - diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt index b40251586..4982d3463 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt @@ -8,7 +8,6 @@ import android.net.Uri import android.os.Build import android.provider.Settings import androidx.activity.compose.BackHandler -import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -77,7 +76,8 @@ import at.bitfire.davdroid.ui.account.AccountActivity import at.bitfire.davdroid.ui.account.AccountProgress import at.bitfire.davdroid.ui.composable.ActionCard import at.bitfire.davdroid.ui.composable.ProgressBar -import at.bitfire.davdroid.ui.intro.IntroActivity +import at.bitfire.davdroid.ui.composition.LocalNavController +import at.bitfire.davdroid.ui.intro.INTRO_CANCELLED import at.bitfire.davdroid.ui.navigation.Routes import at.bitfire.davdroid.ui.setup.LoginActivity import com.google.accompanist.permissions.ExperimentalPermissionsApi @@ -89,18 +89,22 @@ import kotlinx.coroutines.launch @Composable fun AccountsScreen(backStackEntry: NavBackStackEntry, accountsDrawerHandler: AccountsDrawerHandler) { val route = backStackEntry.toRoute() + val navController = LocalNavController.current val context = LocalContext.current val activity = context as? Activity - val introActivityLauncher = rememberLauncherForActivityResult(IntroActivity.Contract) { cancelled -> - if (cancelled) activity?.finish() + LaunchedEffect(Unit) { + navController.currentBackStackEntry + ?.savedStateHandle + ?.getStateFlow(INTRO_CANCELLED, false) + ?.collect { cancelled -> + if (cancelled) activity?.finish() + } } AccountsScreen( initialSyncAccounts = route.syncAccounts, - onShowAppIntro = { - introActivityLauncher.launch(null) - }, + onShowAppIntro = { navController.navigate(Routes.Intro) }, accountsDrawerHandler = accountsDrawerHandler, onAddAccount = { // eventually this will become a navigation diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index fa3a8e549..a6d9609f3 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -13,6 +13,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import at.bitfire.davdroid.ui.composition.LocalNavController +import at.bitfire.davdroid.ui.intro.IntroScreen import at.bitfire.davdroid.ui.navigation.Routes import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject @@ -37,6 +38,7 @@ class MainActivity: AppCompatActivity() { startDestination = accountsFromIntent() ) { composable { AccountsScreen(it, accountsDrawerHandler) } + composable { IntroScreen() } } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsScreen.kt index 52e23faae..5db503861 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/PermissionsScreen.kt @@ -30,9 +30,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.viewmodel.compose.viewModel import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.composable.CardWithImage @@ -76,7 +76,7 @@ fun PermissionsScreen( @Composable fun PermissionsScreen( modifier: Modifier = Modifier, - model: PermissionsModel = viewModel() + model: PermissionsModel = hiltViewModel() ) { // check permissions when the lifecycle owner (for instance Activity) is resumed val lifecycle = LocalLifecycleOwner.current.lifecycle diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksScreen.kt index 38153b847..61f984b1f 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/TasksScreen.kt @@ -36,8 +36,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.core.text.HtmlCompat +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString @@ -73,7 +73,7 @@ fun TasksScreen(onNavUp: () -> Unit) { @Composable fun TasksCard( - model: TasksModel = viewModel() + model: TasksModel = hiltViewModel() ) { val context = LocalContext.current val coroutineScope = rememberCoroutineScope() diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageContent.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageContent.kt index 95eec124e..f785b1d1a 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageContent.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/BatteryOptimizationsPageContent.kt @@ -26,8 +26,8 @@ import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.Constants import at.bitfire.davdroid.Constants.withStatParams @@ -37,7 +37,7 @@ import java.util.Locale @Composable fun BatteryOptimizationsPageContent( - model: BatteryOptimizationsPageModel = viewModel() + model: BatteryOptimizationsPageModel = hiltViewModel() ) { val ignoreBatteryOptimizationsResultLauncher = rememberLauncherForActivityResult( BatteryOptimizationsPage.IgnoreBatteryOptimizationsContract diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroActivity.kt deleted file mode 100644 index 97d919f73..000000000 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroActivity.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details. - */ - -package at.bitfire.davdroid.ui.intro - -import android.app.Activity -import android.content.Context -import android.content.Intent -import android.os.Bundle -import androidx.activity.compose.BackHandler -import androidx.activity.compose.setContent -import androidx.activity.result.contract.ActivityResultContract -import androidx.activity.viewModels -import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.runtime.rememberCoroutineScope -import at.bitfire.davdroid.ui.AppTheme -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch - -@AndroidEntryPoint -class IntroActivity : AppCompatActivity() { - - val model by viewModels() - - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val pages = model.pages - - setContent { - AppTheme { - val scope = rememberCoroutineScope() - val pagerState = rememberPagerState { pages.size } - - BackHandler { - if (pagerState.settledPage == 0) { - setResult(Activity.RESULT_CANCELED) - finish() - } else scope.launch { - pagerState.animateScrollToPage(pagerState.settledPage - 1) - } - } - - IntroScreen( - pages = pages, - pagerState = pagerState, - onDonePressed = { - setResult(Activity.RESULT_OK) - finish() - } - ) - } - } - } - - - /** - * For launching the [IntroActivity]. Result is `true` when the user cancelled the intro. - */ - object Contract: ActivityResultContract() { - override fun createIntent(context: Context, input: Unit?): Intent = - Intent(context, IntroActivity::class.java) - - override fun parseResult(resultCode: Int, intent: Intent?): Boolean { - return resultCode == Activity.RESULT_CANCELED - } - } - -} \ No newline at end of file diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroScreen.kt index 4d4137a0f..467f86829 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/IntroScreen.kt @@ -1,5 +1,6 @@ package at.bitfire.davdroid.ui.intro +import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedContent import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.Canvas @@ -49,11 +50,42 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import at.bitfire.davdroid.R import at.bitfire.davdroid.ui.AppTheme import at.bitfire.davdroid.ui.M3ColorScheme +import at.bitfire.davdroid.ui.composition.LocalNavController import kotlinx.coroutines.launch +const val INTRO_CANCELLED = "cancelled" + +@Composable +fun IntroScreen( + model: IntroModel = hiltViewModel() +) { + val navController = LocalNavController.current + + val scope = rememberCoroutineScope() + val pagerState = rememberPagerState { model.pages.size } + + BackHandler { + if (pagerState.settledPage == 0) { + navController.previousBackStackEntry + ?.savedStateHandle + ?.set(INTRO_CANCELLED, true) + navController.popBackStack() + } else scope.launch { + pagerState.animateScrollToPage(pagerState.settledPage - 1) + } + } + + IntroScreen( + pages = model.pages, + pagerState = pagerState, + onDonePressed = { navController.popBackStack() } + ) +} + @Composable fun IntroScreen( pages: List, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourcePage.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourcePage.kt index 642830595..5d76b520d 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourcePage.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/intro/OpenSourcePage.kt @@ -26,9 +26,9 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.ViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import at.bitfire.davdroid.Constants import at.bitfire.davdroid.Constants.withStatParams import at.bitfire.davdroid.R @@ -54,7 +54,7 @@ class OpenSourcePage @Inject constructor( } @Composable - private fun Page(model: Model = viewModel()) { + private fun Page(model: Model = hiltViewModel()) { val dontShow by model.dontShow.collectAsStateWithLifecycle(false) OpenSourcePage( dontShow = dontShow, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt index 8c1aebec4..30c262e62 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt @@ -5,4 +5,7 @@ import kotlinx.serialization.Serializable object Routes { @Serializable data class Accounts(val syncAccounts: Boolean) + + @Serializable + data object Intro } From b2785bc296853a0785a93113d5cc7b312bb2159a Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 18:18:52 +0100 Subject: [PATCH 07/12] Migrated AboutActivity Signed-off-by: Arnau Mora --- app/src/main/AndroidManifest.xml | 5 - .../at/bitfire/davdroid/ui/AboutActivity.kt | 358 ------------------ .../at/bitfire/davdroid/ui/MainActivity.kt | 2 + .../bitfire/davdroid/ui/about/AboutModel.kt | 68 ++++ .../bitfire/davdroid/ui/about/AboutScreen.kt | 250 ++++++++++++ .../ui/about/AppLicenseInfoProvider.kt | 8 + .../ui/about/AppLicenseInfoProviderModule.kt | 13 + .../bitfire/davdroid/ui/navigation/Routes.kt | 3 + .../at/bitfire/davdroid/OseFlavorModule.kt | 4 +- .../ui/OpenSourceLicenseInfoProvider.kt | 9 +- 10 files changed, 351 insertions(+), 369 deletions(-) delete mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/AboutActivity.kt create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProvider.kt create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7038827c0..275ad1368 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,11 +69,6 @@ - - () - - @Inject - lateinit var licenseInfoProvider: Optional - - - @OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - setContent { - AppTheme { - val uriHandler = LocalUriHandler.current - - Scaffold( - topBar = { - TopAppBar( - navigationIcon = { - IconButton(onClick = { onSupportNavigateUp() }) { - Icon( - Icons.AutoMirrored.Default.ArrowBack, - contentDescription = stringResource(R.string.navigate_up) - ) - } - }, - title = { - Text(stringResource(R.string.navigation_drawer_about)) - }, - actions = { - IconButton(onClick = { - uriHandler.openUri(Constants.HOMEPAGE_URL - .buildUpon() - .withStatParams("AboutActivity") - .build().toString()) - }) { - Icon( - Icons.Default.Home, - contentDescription = stringResource(R.string.navigation_drawer_website) - ) - } - } - ) - } - ) { paddingValues -> - Column(Modifier.padding(paddingValues)) { - val scope = rememberCoroutineScope() - val state = rememberPagerState(pageCount = { 3 }) - - TabRow(state.currentPage) { - Tab(state.currentPage == 0, onClick = { - scope.launch { state.scrollToPage(0) } - }) { - Text( - stringResource(R.string.app_name), - modifier = Modifier.padding(8.dp) - ) - } - Tab(state.currentPage == 1, onClick = { - scope.launch { state.scrollToPage(1) } - }) { - Text( - stringResource(R.string.about_translations), - modifier = Modifier.padding(8.dp) - ) - } - Tab(state.currentPage == 2, onClick = { - scope.launch { state.scrollToPage(2) } - }) { - Text( - stringResource(R.string.about_libraries), - modifier = Modifier.padding(8.dp) - ) - } - } - - HorizontalPager( - state, - modifier = Modifier - .fillMaxWidth() - .weight(1f), - verticalAlignment = Alignment.Top - ) { index -> - when (index) { - 0 -> AboutApp(licenseInfoProvider = licenseInfoProvider.getOrNull()) - 1 -> { - val translations = model.translations.observeAsState(emptyList()) - TranslatorsGallery(translations.value) - } - - 2 -> LibrariesContainer(Modifier.fillMaxSize(), - itemContentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp), - itemSpacing = 8.dp, - librariesBlock = { ctx -> - Libs.Builder() - .withJson(ctx, R.raw.aboutlibraries) - .build() - }) - } - } - } - } - } - } - } - - - @HiltViewModel - class Model @Inject constructor( - @ApplicationContext val context: Context, - private val logger: Logger - ): ViewModel() { - - data class Translation( - val language: String, - val translators: Set - ) - - val translations = MutableLiveData>() - - init { - viewModelScope.launch(Dispatchers.IO) { - loadTranslations() - } - } - - private fun loadTranslations() { - try { - context.resources.assets.open("translators.json").use { stream -> - val jsonTranslations = JSONObject(stream.readBytes().decodeToString()) - val result = LinkedList() - for (langCode in jsonTranslations.keys()) { - val jsonTranslators = jsonTranslations.getJSONArray(langCode) - val translators = Array(jsonTranslators.length()) { idx -> - jsonTranslators.getString(idx) - } - - val langTag = langCode.replace('_', '-') - val language = Locale.forLanguageTag(langTag).displayName - result += Translation(language, translators.toSet()) - } - - // sort translations by localized language name - val collator = Collator.getInstance() - result.sortWith { o1, o2 -> - collator.compare(o1.language, o2.language) - } - - translations.postValue(result) - } - } catch (e: Exception) { - logger.log(Level.WARNING, "Couldn't load translators", e) - } - } - - } - - - interface AppLicenseInfoProvider { - @Composable - fun LicenseInfo() - } - - @Module - @InstallIn(ActivityComponent::class) - interface AppLicenseInfoProviderModule { - @BindsOptionalOf - fun appLicenseInfoProvider(): AppLicenseInfoProvider - } - -} - - -@Composable -fun AboutApp(licenseInfoProvider: AboutActivity.AppLicenseInfoProvider? = null) { - Column( - modifier = Modifier - .padding(8.dp) - .fillMaxWidth() - .verticalScroll(rememberScrollState())) { - Image( - UiUtils.adaptiveIconPainterResource(R.mipmap.ic_launcher), - contentDescription = stringResource(R.string.app_name), - modifier = Modifier - .size(128.dp) - .align(Alignment.CenterHorizontally) - ) - Text( - stringResource(R.string.app_name), - style = MaterialTheme.typography.headlineMedium, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp) - ) - - Text( - stringResource(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE), - style = MaterialTheme.typography.bodyLarge, - textAlign = TextAlign.Center, - modifier = Modifier.fillMaxWidth() - ) - - Text( - stringResource(R.string.about_copyright), - style = MaterialTheme.typography.bodyLarge, - textAlign = TextAlign.Center, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) - ) - - Text( - stringResource(R.string.about_license_info_no_warranty), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier - .fillMaxWidth() - .padding(top = 8.dp) - ) - - PixelBoxes( - arrayOf(Color(0xFFFCF434), Color.White, Color(0xFF9C59D1), Color.Black), - modifier = Modifier - .align(Alignment.CenterHorizontally) - .padding(16.dp) - ) - - licenseInfoProvider?.LicenseInfo() - } -} - -@Composable -@Preview -fun AboutApp_Preview() { - AboutApp(licenseInfoProvider = object : AboutActivity.AppLicenseInfoProvider { - @Composable - override fun LicenseInfo() { - Text("Some flavored License Info") - } - }) -} - - -@Composable -fun TranslatorsGallery( - translations: List -) { - val collator = Collator.getInstance() - LazyColumn(Modifier.padding(8.dp)) { - items(translations) { translation -> - Text( - translation.language, - style = MaterialTheme.typography.headlineMedium, - modifier = Modifier.padding(vertical = 4.dp) - ) - Text( - translation.translators - .sortedWith { a, b -> collator.compare(a, b) } - .joinToString(" · "), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(bottom = 16.dp) - ) - } - } -} - -@Composable -@Preview -fun TranslatorsGallery_Sample() { - TranslatorsGallery(listOf( - AboutActivity.Model.Translation("Some Language", setOf("User 1", "User 2")), - AboutActivity.Model.Translation("Another Language", setOf("User 3", "User 4")) - )) -} \ No newline at end of file diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index a6d9609f3..3f8385403 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController +import at.bitfire.davdroid.ui.about.AboutScreen import at.bitfire.davdroid.ui.composition.LocalNavController import at.bitfire.davdroid.ui.intro.IntroScreen import at.bitfire.davdroid.ui.navigation.Routes @@ -39,6 +40,7 @@ class MainActivity: AppCompatActivity() { ) { composable { AccountsScreen(it, accountsDrawerHandler) } composable { IntroScreen() } + composable { AboutScreen() } } } } diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt new file mode 100644 index 000000000..c3b892ea8 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt @@ -0,0 +1,68 @@ +package at.bitfire.davdroid.ui.about + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import org.json.JSONObject +import java.text.Collator +import java.util.LinkedList +import java.util.Locale +import java.util.logging.Level +import java.util.logging.Logger +import javax.inject.Inject + +@HiltViewModel +class AboutModel @Inject constructor( + @ApplicationContext val context: Context, + private val logger: Logger, + val licenseInfoProvider: Lazy +): ViewModel() { + + data class Translation( + val language: String, + val translators: Set + ) + + val translations = MutableLiveData>() + + init { + viewModelScope.launch(Dispatchers.IO) { + loadTranslations() + } + } + + private fun loadTranslations() { + try { + context.resources.assets.open("translators.json").use { stream -> + val jsonTranslations = JSONObject(stream.readBytes().decodeToString()) + val result = LinkedList() + for (langCode in jsonTranslations.keys()) { + val jsonTranslators = jsonTranslations.getJSONArray(langCode) + val translators = Array(jsonTranslators.length()) { idx -> + jsonTranslators.getString(idx) + } + + val langTag = langCode.replace('_', '-') + val language = Locale.forLanguageTag(langTag).displayName + result += Translation(language, translators.toSet()) + } + + // sort translations by localized language name + val collator = Collator.getInstance() + result.sortWith { o1, o2 -> + collator.compare(o1.language, o2.language) + } + + translations.postValue(result) + } + } catch (e: Exception) { + logger.log(Level.WARNING, "Couldn't load translators", e) + } + } + +} \ No newline at end of file diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt new file mode 100644 index 000000000..0ee24ca99 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt @@ -0,0 +1,250 @@ +package at.bitfire.davdroid.ui.about + +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.filled.Home +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Tab +import androidx.compose.material3.TabRow +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import at.bitfire.davdroid.BuildConfig +import at.bitfire.davdroid.Constants +import at.bitfire.davdroid.Constants.withStatParams +import at.bitfire.davdroid.R +import at.bitfire.davdroid.ui.UiUtils +import at.bitfire.davdroid.ui.composable.PixelBoxes +import at.bitfire.davdroid.ui.composition.LocalNavController +import com.mikepenz.aboutlibraries.Libs +import com.mikepenz.aboutlibraries.ui.compose.m3.LibrariesContainer +import com.mikepenz.aboutlibraries.util.withJson +import kotlinx.coroutines.launch +import java.text.Collator + +@Composable +@OptIn(ExperimentalMaterial3Api::class) +fun AboutScreen(model: AboutModel = hiltViewModel()) { + val uriHandler = LocalUriHandler.current + val navController = LocalNavController.current + + Scaffold( + topBar = { + TopAppBar( + navigationIcon = { + IconButton(onClick = { navController.popBackStack() }) { + Icon( + Icons.AutoMirrored.Default.ArrowBack, + contentDescription = stringResource(R.string.navigate_up) + ) + } + }, + title = { + Text(stringResource(R.string.navigation_drawer_about)) + }, + actions = { + IconButton(onClick = { + uriHandler.openUri( + Constants.HOMEPAGE_URL + .buildUpon() + .withStatParams("AboutActivity") + .build().toString()) + }) { + Icon( + Icons.Default.Home, + contentDescription = stringResource(R.string.navigation_drawer_website) + ) + } + } + ) + } + ) { paddingValues -> + Column(Modifier.padding(paddingValues)) { + val scope = rememberCoroutineScope() + val state = rememberPagerState(pageCount = { 3 }) + + TabRow(state.currentPage) { + Tab(state.currentPage == 0, onClick = { + scope.launch { state.scrollToPage(0) } + }) { + Text( + stringResource(R.string.app_name), + modifier = Modifier.padding(8.dp) + ) + } + Tab(state.currentPage == 1, onClick = { + scope.launch { state.scrollToPage(1) } + }) { + Text( + stringResource(R.string.about_translations), + modifier = Modifier.padding(8.dp) + ) + } + Tab(state.currentPage == 2, onClick = { + scope.launch { state.scrollToPage(2) } + }) { + Text( + stringResource(R.string.about_libraries), + modifier = Modifier.padding(8.dp) + ) + } + } + + HorizontalPager( + state, + modifier = Modifier + .fillMaxWidth() + .weight(1f), + verticalAlignment = Alignment.Top + ) { index -> + when (index) { + 0 -> AboutApp(licenseInfoProvider = model.licenseInfoProvider.value) + 1 -> { + val translations = model.translations.observeAsState(emptyList()) + TranslatorsGallery(translations.value) + } + + 2 -> LibrariesContainer( + Modifier.fillMaxSize(), + itemContentPadding = PaddingValues(horizontal = 8.dp, vertical = 8.dp), + itemSpacing = 8.dp, + librariesBlock = { ctx -> + Libs.Builder() + .withJson(ctx, R.raw.aboutlibraries) + .build() + }) + } + } + } + } +} + +@Composable +fun AboutApp(licenseInfoProvider: AppLicenseInfoProvider? = null) { + Column( + modifier = Modifier + .padding(8.dp) + .fillMaxWidth() + .verticalScroll(rememberScrollState())) { + Image( + UiUtils.adaptiveIconPainterResource(R.mipmap.ic_launcher), + contentDescription = stringResource(R.string.app_name), + modifier = Modifier + .size(128.dp) + .align(Alignment.CenterHorizontally) + ) + Text( + stringResource(R.string.app_name), + style = MaterialTheme.typography.headlineMedium, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp) + ) + + Text( + stringResource(R.string.about_version, BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE), + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + + Text( + stringResource(R.string.about_copyright), + style = MaterialTheme.typography.bodyLarge, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) + + Text( + stringResource(R.string.about_license_info_no_warranty), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier + .fillMaxWidth() + .padding(top = 8.dp) + ) + + PixelBoxes( + arrayOf(Color(0xFFFCF434), Color.White, Color(0xFF9C59D1), Color.Black), + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(16.dp) + ) + + licenseInfoProvider?.LicenseInfo() + } +} + +@Composable +@Preview +fun AboutApp_Preview() { + AboutApp(licenseInfoProvider = object : AppLicenseInfoProvider { + @Composable + override fun LicenseInfo() { + Text("Some flavored License Info") + } + }) +} + + +@Composable +fun TranslatorsGallery( + translations: List +) { + val collator = Collator.getInstance() + LazyColumn(Modifier.padding(8.dp)) { + items(translations) { translation -> + Text( + translation.language, + style = MaterialTheme.typography.headlineMedium, + modifier = Modifier.padding(vertical = 4.dp) + ) + Text( + translation.translators + .sortedWith { a, b -> collator.compare(a, b) } + .joinToString(" · "), + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier.padding(bottom = 16.dp) + ) + } + } +} + +@Composable +@Preview +fun TranslatorsGallery_Sample() { + TranslatorsGallery(listOf( + AboutModel.Translation("Some Language", setOf("User 1", "User 2")), + AboutModel.Translation("Another Language", setOf("User 3", "User 4")) + )) +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProvider.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProvider.kt new file mode 100644 index 000000000..9380c5a62 --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProvider.kt @@ -0,0 +1,8 @@ +package at.bitfire.davdroid.ui.about + +import androidx.compose.runtime.Composable + +interface AppLicenseInfoProvider { + @Composable + fun LicenseInfo() +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt new file mode 100644 index 000000000..b64104c2d --- /dev/null +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt @@ -0,0 +1,13 @@ +package at.bitfire.davdroid.ui.about + +import dagger.BindsOptionalOf +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ActivityComponent + +@Module +@InstallIn(ActivityComponent::class) +interface AppLicenseInfoProviderModule { + @BindsOptionalOf + fun appLicenseInfoProvider(): AppLicenseInfoProvider +} diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt index 30c262e62..b217cd463 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/Routes.kt @@ -8,4 +8,7 @@ object Routes { @Serializable data object Intro + + @Serializable + data object About } diff --git a/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt b/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt index 3efcf73f0..28e1c6a19 100644 --- a/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt +++ b/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt @@ -4,10 +4,10 @@ package at.bitfire.davdroid -import at.bitfire.davdroid.ui.AboutActivity import at.bitfire.davdroid.ui.AccountsDrawerHandler import at.bitfire.davdroid.ui.OpenSourceLicenseInfoProvider import at.bitfire.davdroid.ui.OseAccountsDrawerHandler +import at.bitfire.davdroid.ui.about.AppLicenseInfoProvider import at.bitfire.davdroid.ui.intro.IntroPageFactory import at.bitfire.davdroid.ui.setup.LoginTypesProvider import at.bitfire.davdroid.ui.setup.StandardLoginTypesProvider @@ -34,7 +34,7 @@ interface OseFlavorModules { @InstallIn(ViewModelComponent::class) interface ForViewModels { @Binds - fun appLicenseInfoProvider(impl: OpenSourceLicenseInfoProvider): AboutActivity.AppLicenseInfoProvider + fun appLicenseInfoProvider(impl: OpenSourceLicenseInfoProvider): AppLicenseInfoProvider @Binds fun loginTypesProvider(impl: StandardLoginTypesProvider): LoginTypesProvider diff --git a/app/src/ose/kotlin/at/bitfire/davdroid/ui/OpenSourceLicenseInfoProvider.kt b/app/src/ose/kotlin/at/bitfire/davdroid/ui/OpenSourceLicenseInfoProvider.kt index a076d63c7..065fc12a2 100644 --- a/app/src/ose/kotlin/at/bitfire/davdroid/ui/OpenSourceLicenseInfoProvider.kt +++ b/app/src/ose/kotlin/at/bitfire/davdroid/ui/OpenSourceLicenseInfoProvider.kt @@ -14,17 +14,18 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.tooling.preview.Preview import androidx.core.text.HtmlCompat +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope -import androidx.lifecycle.viewmodel.compose.viewModel import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString +import at.bitfire.davdroid.ui.about.AppLicenseInfoProvider import com.google.common.io.CharStreams import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import javax.inject.Inject -class OpenSourceLicenseInfoProvider @Inject constructor(): AboutActivity.AppLicenseInfoProvider { +class OpenSourceLicenseInfoProvider @Inject constructor(): AppLicenseInfoProvider { @Composable override fun LicenseInfo() { @@ -33,7 +34,7 @@ class OpenSourceLicenseInfoProvider @Inject constructor(): AboutActivity.AppLice @Composable fun LicenseInfoGpl( - model: Model = viewModel() + model: Model = hiltViewModel() ) { model.gpl?.let { OpenSourceLicenseInfo(it.toAnnotatedString()) } } From bdae74189b3f8d26e8e45434cdfb25e7579a4144 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 18:23:47 +0100 Subject: [PATCH 08/12] Moved `AccountsDrawerHandler` to model Signed-off-by: Arnau Mora --- .../main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt | 3 ++- .../main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt | 6 ++---- app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt | 7 +------ app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt | 6 +++--- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt index 309a7dc78..a21f6ce12 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt @@ -50,7 +50,8 @@ class AccountsModel @AssistedInject constructor( private val db: AppDatabase, introPageFactory: IntroPageFactory, private val logger: Logger, - private val syncWorkerManager: SyncWorkerManager + private val syncWorkerManager: SyncWorkerManager, + val accountsDrawerHandler: AccountsDrawerHandler ): ViewModel() { @AssistedFactory diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt index b40251586..d49d252d0 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt @@ -87,7 +87,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable -fun AccountsScreen(backStackEntry: NavBackStackEntry, accountsDrawerHandler: AccountsDrawerHandler) { +fun AccountsScreen(backStackEntry: NavBackStackEntry) { val route = backStackEntry.toRoute() val context = LocalContext.current val activity = context as? Activity @@ -101,7 +101,6 @@ fun AccountsScreen(backStackEntry: NavBackStackEntry, accountsDrawerHandler: Acc onShowAppIntro = { introActivityLauncher.launch(null) }, - accountsDrawerHandler = accountsDrawerHandler, onAddAccount = { // eventually this will become a navigation context.startActivity(Intent(context, LoginActivity::class.java)) @@ -123,7 +122,6 @@ fun AccountsScreen(backStackEntry: NavBackStackEntry, accountsDrawerHandler: Acc fun AccountsScreen( initialSyncAccounts: Boolean, onShowAppIntro: () -> Unit, - accountsDrawerHandler: AccountsDrawerHandler, onAddAccount: () -> Unit, onShowAccount: (Account) -> Unit, onManagePermissions: () -> Unit, @@ -148,7 +146,7 @@ fun AccountsScreen( } AccountsScreen( - accountsDrawerHandler = accountsDrawerHandler, + accountsDrawerHandler = model.accountsDrawerHandler, accounts = accounts, showSyncAll = showSyncAll, onSyncAll = { model.syncAllAccounts() }, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index fa3a8e549..3304bc327 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -15,16 +15,11 @@ import androidx.navigation.compose.rememberNavController import at.bitfire.davdroid.ui.composition.LocalNavController import at.bitfire.davdroid.ui.navigation.Routes import dagger.hilt.android.AndroidEntryPoint -import javax.inject.Inject @AndroidEntryPoint class MainActivity: AppCompatActivity() { - @Inject - lateinit var accountsDrawerHandler: AccountsDrawerHandler - - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -36,7 +31,7 @@ class MainActivity: AppCompatActivity() { navController = navController, startDestination = accountsFromIntent() ) { - composable { AccountsScreen(it, accountsDrawerHandler) } + composable { AccountsScreen(it) } } } } diff --git a/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt b/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt index 3efcf73f0..6c20518ba 100644 --- a/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt +++ b/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt @@ -23,9 +23,6 @@ interface OseFlavorModules { @Module @InstallIn(ActivityComponent::class) interface ForActivities { - @Binds - fun accountsDrawerHandler(impl: OseAccountsDrawerHandler): AccountsDrawerHandler - @Binds fun loginTypesProvider(impl: StandardLoginTypesProvider): LoginTypesProvider } @@ -33,6 +30,9 @@ interface OseFlavorModules { @Module @InstallIn(ViewModelComponent::class) interface ForViewModels { + @Binds + fun accountsDrawerHandler(impl: OseAccountsDrawerHandler): AccountsDrawerHandler + @Binds fun appLicenseInfoProvider(impl: OpenSourceLicenseInfoProvider): AboutActivity.AppLicenseInfoProvider From 52fde0c9f708d1009a80feac563b62165bb94a3e Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Wed, 1 Jan 2025 18:28:50 +0100 Subject: [PATCH 09/12] Completed migration Signed-off-by: Arnau Mora --- .../kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt | 5 ++++- .../main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt | 2 +- .../main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt | 2 +- .../davdroid/ui/about/AppLicenseInfoProviderModule.kt | 4 ++-- 4 files changed, 8 insertions(+), 5 deletions(-) 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 4a081dcfb..5c529b0a5 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsDrawerHandler.kt @@ -53,6 +53,8 @@ import androidx.compose.ui.unit.dp import androidx.core.net.toUri import at.bitfire.davdroid.BuildConfig import at.bitfire.davdroid.R +import at.bitfire.davdroid.ui.composition.LocalNavController +import at.bitfire.davdroid.ui.navigation.Routes import at.bitfire.davdroid.ui.webdav.WebdavMountsActivity import kotlinx.coroutines.launch import java.net.URI @@ -104,6 +106,7 @@ abstract class AccountsDrawerHandler { open fun ImportantEntries( snackbarHostState: SnackbarHostState ) { + val navController = LocalNavController.current val context = LocalContext.current val isBeta = LocalInspectionMode.current || @@ -116,7 +119,7 @@ abstract class AccountsDrawerHandler { icon = Icons.Default.Info, title = stringResource(R.string.navigation_drawer_about), onClick = { - context.startActivity(Intent(context, AboutActivity::class.java)) + navController.navigate(Routes.About) } ) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt index c3b892ea8..96050f42c 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutModel.kt @@ -20,7 +20,7 @@ import javax.inject.Inject class AboutModel @Inject constructor( @ApplicationContext val context: Context, private val logger: Logger, - val licenseInfoProvider: Lazy + val licenseInfoProvider: AppLicenseInfoProvider? ): ViewModel() { data class Translation( diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt index 0ee24ca99..72f1649c9 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AboutScreen.kt @@ -126,7 +126,7 @@ fun AboutScreen(model: AboutModel = hiltViewModel()) { verticalAlignment = Alignment.Top ) { index -> when (index) { - 0 -> AboutApp(licenseInfoProvider = model.licenseInfoProvider.value) + 0 -> AboutApp(licenseInfoProvider = model.licenseInfoProvider) 1 -> { val translations = model.translations.observeAsState(emptyList()) TranslatorsGallery(translations.value) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt index b64104c2d..4bb35c8b0 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/about/AppLicenseInfoProviderModule.kt @@ -3,10 +3,10 @@ package at.bitfire.davdroid.ui.about import dagger.BindsOptionalOf import dagger.Module import dagger.hilt.InstallIn -import dagger.hilt.android.components.ActivityComponent +import dagger.hilt.android.components.ViewModelComponent @Module -@InstallIn(ActivityComponent::class) +@InstallIn(ViewModelComponent::class) interface AppLicenseInfoProviderModule { @BindsOptionalOf fun appLicenseInfoProvider(): AppLicenseInfoProvider From 282f1d1db6b942ae496ee335b9ad6f9bd8c18433 Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Thu, 2 Jan 2025 14:52:52 +0100 Subject: [PATCH 10/12] Moved NavController Signed-off-by: Arnau Mora --- app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt | 2 +- .../davdroid/ui/{composition => navigation}/NavController.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename app/src/main/kotlin/at/bitfire/davdroid/ui/{composition => navigation}/NavController.kt (81%) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index 3304bc327..81581a5bf 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -12,7 +12,7 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController -import at.bitfire.davdroid.ui.composition.LocalNavController +import at.bitfire.davdroid.ui.navigation.LocalNavController import at.bitfire.davdroid.ui.navigation.Routes import dagger.hilt.android.AndroidEntryPoint diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/NavController.kt similarity index 81% rename from app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt rename to app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/NavController.kt index 5f09fa567..71991c542 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/composition/NavController.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/navigation/NavController.kt @@ -1,4 +1,4 @@ -package at.bitfire.davdroid.ui.composition +package at.bitfire.davdroid.ui.navigation import androidx.compose.runtime.compositionLocalOf import androidx.navigation.NavController From 0e59334e68944f674e24a34f776a274d06015c8c Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Sat, 11 Jan 2025 15:46:18 +0100 Subject: [PATCH 11/12] Moved `AccountsDrawerHandler` back to activity Signed-off-by: Arnau Mora --- .../main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt | 3 +-- .../main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt | 9 +++++++-- .../main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt | 6 +++++- .../ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt | 6 +++--- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt index cf2ef2a84..dc718ac99 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsModel.kt @@ -54,8 +54,7 @@ class AccountsModel @AssistedInject constructor( private val db: AppDatabase, introPageFactory: IntroPageFactory, private val logger: Logger, - private val syncWorkerManager: SyncWorkerManager, - val accountsDrawerHandler: AccountsDrawerHandler + private val syncWorkerManager: SyncWorkerManager ): ViewModel() { @AssistedFactory diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt index 38190a01d..e6d665875 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt @@ -91,7 +91,10 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch @Composable -fun AccountsScreen(backStackEntry: NavBackStackEntry) { +fun AccountsScreen( + backStackEntry: NavBackStackEntry, + accountsDrawerHandler: AccountsDrawerHandler +) { val route = backStackEntry.toRoute() val context = LocalContext.current val activity = context as? Activity @@ -101,6 +104,7 @@ fun AccountsScreen(backStackEntry: NavBackStackEntry) { } AccountsScreen( + accountsDrawerHandler = accountsDrawerHandler, initialSyncAccounts = route.syncAccounts, onShowAppIntro = { introActivityLauncher.launch(null) @@ -124,6 +128,7 @@ fun AccountsScreen(backStackEntry: NavBackStackEntry) { @Composable fun AccountsScreen( + accountsDrawerHandler: AccountsDrawerHandler, initialSyncAccounts: Boolean, onShowAppIntro: () -> Unit, onAddAccount: () -> Unit, @@ -150,7 +155,7 @@ fun AccountsScreen( } AccountsScreen( - accountsDrawerHandler = model.accountsDrawerHandler, + accountsDrawerHandler = accountsDrawerHandler, accounts = accounts, showSyncAll = showSyncAll, onSyncAll = { model.syncAllAccounts() }, diff --git a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt index 81581a5bf..bf767c491 100644 --- a/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt +++ b/app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt @@ -15,11 +15,15 @@ import androidx.navigation.compose.rememberNavController import at.bitfire.davdroid.ui.navigation.LocalNavController import at.bitfire.davdroid.ui.navigation.Routes import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject @AndroidEntryPoint class MainActivity: AppCompatActivity() { + @Inject + lateinit var accountsDrawerHandler: AccountsDrawerHandler + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -31,7 +35,7 @@ class MainActivity: AppCompatActivity() { navController = navController, startDestination = accountsFromIntent() ) { - composable { AccountsScreen(it) } + composable { AccountsScreen(it, accountsDrawerHandler) } } } } diff --git a/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt b/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt index 6c20518ba..3efcf73f0 100644 --- a/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt +++ b/app/src/ose/kotlin/at/bitfire/davdroid/OseFlavorModule.kt @@ -23,6 +23,9 @@ interface OseFlavorModules { @Module @InstallIn(ActivityComponent::class) interface ForActivities { + @Binds + fun accountsDrawerHandler(impl: OseAccountsDrawerHandler): AccountsDrawerHandler + @Binds fun loginTypesProvider(impl: StandardLoginTypesProvider): LoginTypesProvider } @@ -30,9 +33,6 @@ interface OseFlavorModules { @Module @InstallIn(ViewModelComponent::class) interface ForViewModels { - @Binds - fun accountsDrawerHandler(impl: OseAccountsDrawerHandler): AccountsDrawerHandler - @Binds fun appLicenseInfoProvider(impl: OpenSourceLicenseInfoProvider): AboutActivity.AppLicenseInfoProvider From 597a3d293ec7d543913d923811d942fb3cdc6c1e Mon Sep 17 00:00:00 2001 From: Arnau Mora Date: Sat, 11 Jan 2025 15:50:16 +0100 Subject: [PATCH 12/12] Added redirection from `AccountsActivity` to `MainActivity` Signed-off-by: Arnau Mora --- app/src/main/AndroidManifest.xml | 8 +++++++ .../bitfire/davdroid/ui/AccountsActivity.kt | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsActivity.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4e2202fb7..9add29a5c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -70,6 +70,14 @@ + + + +