Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compose Navigation: start migration with AccountsActivity #1211

Open
wants to merge 19 commits into
base: main-ose
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down
24 changes: 18 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,35 @@

<activity android:name=".ui.intro.IntroActivity" />
<activity
android:name=".ui.AccountsActivity"
android:name=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:scheme="davx5" android:host="destination"/>
</intent-filter>
</activity>

<!--
Left here for external apps / legacy shortcuts. Should be removed in the future.
Automatically redirects to MainActivity.
-->
<activity android:name=".ui.AccountsActivity" android:exported="true" />

<activity
android:name=".ui.AboutActivity"
android:label="@string/navigation_drawer_about"
android:parentActivityName=".ui.AccountsActivity"/>
android:parentActivityName=".ui.MainActivity"/>

<activity
android:name=".ui.AppSettingsActivity"
android:label="@string/app_settings"
android:parentActivityName=".ui.AccountsActivity"
android:parentActivityName=".ui.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.APPLICATION_PREFERENCES"/>
Expand Down Expand Up @@ -106,7 +118,7 @@

<activity
android:name=".ui.setup.LoginActivity"
android:parentActivityName=".ui.AccountsActivity"
android:parentActivityName=".ui.MainActivity"
android:windowSoftInputMode="adjustResize"
android:exported="true">
<intent-filter>
Expand Down Expand Up @@ -134,7 +146,7 @@

<activity
android:name=".ui.account.AccountActivity"
android:parentActivityName=".ui.AccountsActivity"
android:parentActivityName=".ui.MainActivity"
android:exported="true">
</activity>
<activity
Expand All @@ -156,7 +168,7 @@
<activity
android:name=".ui.webdav.WebdavMountsActivity"
android:exported="true"
android:parentActivityName=".ui.AccountsActivity" />
android:parentActivityName=".ui.MainActivity" />
<activity
android:name=".ui.webdav.AddWebdavMountActivity"
android:parentActivityName=".ui.webdav.WebdavMountsActivity"
Expand Down
7 changes: 4 additions & 3 deletions app/src/main/kotlin/at/bitfire/davdroid/db/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import at.bitfire.davdroid.R
import at.bitfire.davdroid.TextTable
import at.bitfire.davdroid.db.migration.*
import at.bitfire.davdroid.ui.AccountsActivity
import at.bitfire.davdroid.db.migration.AutoMigration12
import at.bitfire.davdroid.db.migration.AutoMigration16
import at.bitfire.davdroid.ui.MainActivity
import at.bitfire.davdroid.ui.NotificationRegistry
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -74,7 +75,7 @@ abstract class AppDatabase: RoomDatabase() {
.addCallback(object: Callback() {
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
notificationRegistry.notifyIfPossible(NotificationRegistry.NOTIFY_DATABASE_CORRUPTED) {
val launcherIntent = Intent(context, AccountsActivity::class.java)
val launcherIntent = Intent(context, MainActivity::class.java)
NotificationCompat.Builder(context, notificationRegistry.CHANNEL_GENERAL)
.setSmallIcon(R.drawable.ic_warning_notify)
.setContentTitle(context.getString(R.string.database_destructive_migration_title))
Expand Down
53 changes: 14 additions & 39 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsActivity.kt
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
ArnyminerZ marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -6,53 +6,28 @@ package at.bitfire.davdroid.ui

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 dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import androidx.core.net.toUri
import at.bitfire.davdroid.ui.navigation.Destination


@AndroidEntryPoint
/**
* Legacy activity, previously used to host [AccountsScreen].
* Needed for backwards compatibility with external apps / shortcuts that call the activity directly.
*/
class AccountsActivity: 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
val uri = Destination.Accounts.PATH.toUri().buildUpon()
val syncOnLaunch = intent.action == Intent.ACTION_SYNC
if (syncOnLaunch)
uri.appendQueryParameter("syncAccounts", "true")

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))
}
)
}
MainActivity.legacyRedirect(
activity = this,
uri = uri.build()
)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import java.util.logging.Logger
class AccountsModel @AssistedInject constructor(
@Assisted val syncAccountsOnInit: Boolean,
private val accountRepository: AccountRepository,
internal val accountsDrawerHandler: AccountsDrawerHandler,
@ApplicationContext val context: Context,
private val db: AppDatabase,
introPageFactory: IntroPageFactory,
Expand Down
41 changes: 39 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/AccountsScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,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
Expand Down Expand Up @@ -73,20 +75,55 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.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(
initialSyncAccounts: Boolean
) {
val context = LocalContext.current
val activity = context as? Activity

val introActivityLauncher = rememberLauncherForActivityResult(IntroActivity.Contract) { cancelled ->
if (cancelled) activity?.finish()
}

AccountsScreen(
initialSyncAccounts = initialSyncAccounts,
onShowAppIntro = {
introActivityLauncher.launch(null)
},
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,
onShowAppIntro: () -> Unit,
accountsDrawerHandler: AccountsDrawerHandler,
onAddAccount: () -> Unit,
onShowAccount: (Account) -> Unit,
onManagePermissions: () -> Unit,
Expand All @@ -111,7 +148,7 @@ fun AccountsScreen(
}

AccountsScreen(
accountsDrawerHandler = accountsDrawerHandler,
accountsDrawerHandler = model.accountsDrawerHandler,
accounts = accounts,
showSyncAll = showSyncAll,
onSyncAll = { model.syncAllAccounts() },
Expand Down
60 changes: 60 additions & 0 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright © All Contributors. See LICENSE and AUTHORS in the root directory for details.
*/

package at.bitfire.davdroid.ui

import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.annotation.UiThread
import androidx.appcompat.app.AppCompatActivity
import at.bitfire.davdroid.ui.navigation.Destination
import at.bitfire.davdroid.ui.navigation.Navigation
import dagger.hilt.android.AndroidEntryPoint
import java.util.logging.Logger
import javax.inject.Inject

@AndroidEntryPoint
class MainActivity: AppCompatActivity() {

@Inject
lateinit var logger: Logger

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
Navigation(
deepLinkUri = intent.data,
logger = logger
)
}
}


companion object {

fun intentWithDestination(context: Context, uri: Uri) =
Intent(Intent.ACTION_VIEW, uri, context, MainActivity::class.java).apply {
// Create a new activity, do not allow going back.
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}

/**
* Starts [MainActivity] as a redirect of a legacy activity.
*
* @param activity The activity that is requesting the redirection.
* @param uri The URI to launch. Should have schema of [Destination.APP_BASE_URI].
*/
@UiThread
fun legacyRedirect(activity: Activity, uri: Uri) {
activity.startActivity(intentWithDestination(activity, uri))
}

}

}
10 changes: 8 additions & 2 deletions app/src/main/kotlin/at/bitfire/davdroid/ui/UiUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package at.bitfire.davdroid.ui

import android.content.Context
import android.content.Intent
import android.content.pm.ShortcutInfo
import android.content.pm.ShortcutManager
import android.graphics.Typeface
Expand Down Expand Up @@ -34,10 +33,12 @@ import androidx.compose.ui.text.style.TextDecoration
import androidx.core.content.getSystemService
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.toBitmap
import androidx.core.net.toUri
import androidx.core.text.getSpans
import at.bitfire.davdroid.R
import at.bitfire.davdroid.settings.Settings
import at.bitfire.davdroid.settings.SettingsManager
import at.bitfire.davdroid.ui.navigation.Destination
import dagger.hilt.EntryPoint
import dagger.hilt.InstallIn
import dagger.hilt.android.EntryPointAccessors
Expand Down Expand Up @@ -86,7 +87,12 @@ 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(
MainActivity.intentWithDestination(
context,
Destination.Accounts.PATH.toUri().buildUpon().appendQueryParameter("syncAccounts", "true").build()
)
)
.build()
)
} catch(e: Exception) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package at.bitfire.davdroid.ui.navigation

import androidx.navigation.navDeepLink
import kotlinx.serialization.Serializable

object Destination {

/**
* Base URI for internal (non-exposed) deep links (used for navigation).
*/
private const val APP_BASE_URI = "nav:"

@Serializable
data class Accounts(val syncAccounts: Boolean = false) {
companion object {
const val PATH = "$APP_BASE_URI/accounts"

val deepLinks = listOf(
navDeepLink<Accounts>(basePath = PATH)
)
}
}

}
Loading
Loading