Merge pull request #7037 from thundernest/add_feature_launcher

Add feature launcher
This commit is contained in:
Wolf-Martell Montwé 2023-07-03 14:32:34 +02:00 committed by GitHub
commit 0c1921539f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
21 changed files with 231 additions and 68 deletions

View file

@ -5,7 +5,6 @@ import app.k9mail.feature.account.setup.AccountSetupExternalContract
import app.k9mail.feature.account.setup.featureAccountSetupModule
import app.k9mail.feature.preview.account.AccountCreator
import app.k9mail.feature.preview.account.AccountOwnerNameProvider
import app.k9mail.feature.preview.account.AccountSetupFinishedLauncher
import app.k9mail.feature.preview.auth.AndroidKeyStoreDirectoryProvider
import app.k9mail.feature.preview.auth.AppOAuthConfigurationFactory
import app.k9mail.feature.preview.auth.DefaultTrustedSocketFactory
@ -13,27 +12,15 @@ import com.fsck.k9.mail.ssl.KeyStoreDirectoryProvider
import com.fsck.k9.mail.ssl.LocalKeyStore
import com.fsck.k9.mail.ssl.TrustManagerFactory
import com.fsck.k9.mail.ssl.TrustedSocketFactory
import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext
import org.koin.core.module.Module
import org.koin.dsl.module
val accountModule: Module = module {
factory<AccountSetupExternalContract.AccountOwnerNameProvider> { AccountOwnerNameProvider() }
factory<AccountSetupExternalContract.AccountCreator> { AccountCreator() }
factory<AccountSetupExternalContract.AccountSetupFinishedLauncher> {
AccountSetupFinishedLauncher(
context = androidContext(),
)
}
}
val featureModule: Module = module {
// TODO move to network module
single<OkHttpClient> {
OkHttpClient()
}
single<OAuthConfigurationFactory> { AppOAuthConfigurationFactory() }
factory<KeyStoreDirectoryProvider> { AndroidKeyStoreDirectoryProvider(context = get()) }

View file

@ -1,13 +0,0 @@
package app.k9mail.feature.preview.account
import android.content.Context
import android.widget.Toast
import app.k9mail.feature.account.setup.AccountSetupExternalContract
class AccountSetupFinishedLauncher(
private val context: Context,
) : AccountSetupExternalContract.AccountSetupFinishedLauncher {
override suspend fun launch(accountUuid: String) {
Toast.makeText(context, "AccountSetupFinishedLauncher.launch($accountUuid)", Toast.LENGTH_SHORT).show()
}
}

View file

@ -17,6 +17,9 @@ dependencies {
implementation(projects.backend.pop3)
debugImplementation(projects.backend.demo)
implementation(projects.feature.launcher)
// TODO remove account setup dependency
implementation(projects.feature.account.setup)
implementation(libs.androidx.appcompat)

View file

@ -8,6 +8,7 @@ import com.fsck.k9.backends.backendsModule
import com.fsck.k9.controller.ControllerExtension
import com.fsck.k9.crypto.EncryptionExtractor
import com.fsck.k9.crypto.openpgp.OpenPgpEncryptionExtractor
import com.fsck.k9.feature.featureModule
import com.fsck.k9.notification.notificationModule
import com.fsck.k9.preferences.K9StoragePersister
import com.fsck.k9.preferences.StoragePersister
@ -44,4 +45,5 @@ val appModules = listOf(
backendsModule,
storageModule,
newAccountModule,
featureModule,
)

View file

@ -2,7 +2,6 @@ package com.fsck.k9.account
import app.k9mail.feature.account.setup.AccountSetupExternalContract
import org.koin.android.ext.koin.androidApplication
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val newAccountModule = module {
@ -20,11 +19,4 @@ val newAccountModule = module {
context = androidApplication(),
)
}
factory<AccountSetupExternalContract.AccountSetupFinishedLauncher> {
AccountSetupFinishedLauncher(
context = androidContext(),
preferences = get(),
)
}
}

View file

@ -1,27 +0,0 @@
package com.fsck.k9.account
import android.content.Context
import app.k9mail.feature.account.setup.AccountSetupExternalContract
import com.fsck.k9.Preferences
import com.fsck.k9.activity.MessageList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class AccountSetupFinishedLauncher(
private val context: Context,
private val preferences: Preferences,
private val coroutineDispatcher: CoroutineDispatcher = Dispatchers.IO,
) : AccountSetupExternalContract.AccountSetupFinishedLauncher {
override suspend fun launch(accountUuid: String) {
val account = withContext(coroutineDispatcher) {
preferences.getAccount(accountUuid)
}
if (account == null) {
MessageList.launch(context)
} else {
MessageList.launch(context, account)
}
}
}

View file

@ -0,0 +1,21 @@
package com.fsck.k9.feature
import android.content.Context
import android.content.Intent
import app.k9mail.feature.launcher.FeatureLauncherExternalContract
import com.fsck.k9.activity.MessageList
class AccountSetupFinishedLauncher(
private val context: Context,
) : FeatureLauncherExternalContract.AccountSetupFinishedLauncher {
override fun launch(accountUuid: String) {
val intent = Intent(context, MessageList::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
putExtra(MessageList.EXTRA_ACCOUNT, accountUuid)
}
context.startActivity(intent)
}
}

View file

@ -0,0 +1,13 @@
package com.fsck.k9.feature
import app.k9mail.feature.launcher.FeatureLauncherExternalContract
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.module
val featureModule = module {
factory<FeatureLauncherExternalContract.AccountSetupFinishedLauncher> {
AccountSetupFinishedLauncher(
context = androidContext(),
)
}
}

View file

@ -10,9 +10,11 @@ dependencies {
implementation(projects.feature.autodiscovery.providersxml)
implementation(projects.mail.common)
implementation(projects.uiUtils.toolbarBottomSheet)
implementation(projects.feature.launcher)
// TODO: Remove AccountOauth dependency
implementation(projects.feature.account.oauth)
// Remove AccountSetupIncoming's dependency on these
compileOnly(projects.mail.protocols.imap)
implementation(projects.plugins.openpgpApiLib.openpgpApi)

View file

@ -2,6 +2,7 @@ package com.fsck.k9
import app.k9mail.autodiscovery.providersxml.autodiscoveryProvidersXmlModule
import app.k9mail.feature.account.oauth.featureAccountOAuthModule
import app.k9mail.feature.launcher.di.featureLauncherModule
import com.fsck.k9.account.accountModule
import com.fsck.k9.activity.activityModule
import com.fsck.k9.contacts.contactsModule
@ -42,4 +43,5 @@ val uiModules = listOf(
messageDetailsUiModule,
messageViewUiModule,
identityUiModule,
featureLauncherModule,
)

View file

@ -28,6 +28,7 @@ import androidx.fragment.app.commit
import androidx.fragment.app.commitNow
import app.k9mail.core.android.common.contact.CachingRepository
import app.k9mail.core.android.common.contact.ContactRepository
import app.k9mail.feature.launcher.FeatureLauncherActivity
import com.fsck.k9.Account
import com.fsck.k9.K9
import com.fsck.k9.K9.SplitViewMode
@ -153,7 +154,12 @@ open class MessageList :
deleteIncompleteAccounts(accounts)
val hasAccountSetup = accounts.any { it.isFinishedSetup }
if (!hasAccountSetup) {
OnboardingActivity.launch(this)
val useNewOnboarding = false
if (useNewOnboarding) {
FeatureLauncherActivity.launchOnboarding(this)
} else {
OnboardingActivity.launch(this)
}
finish()
return
}
@ -1392,7 +1398,7 @@ open class MessageList :
private const val ACTION_SHORTCUT = "shortcut"
private const val EXTRA_SPECIAL_FOLDER = "special_folder"
private const val EXTRA_ACCOUNT = "account_uuid"
const val EXTRA_ACCOUNT = "account_uuid"
private const val EXTRA_MESSAGE_REFERENCE = "message_reference"
private const val EXTRA_MESSAGE_VIEW_ONLY = "message_view_only"

View file

@ -16,8 +16,4 @@ interface AccountSetupExternalContract {
fun interface AccountOwnerNameProvider {
suspend fun getOwnerName(): String?
}
fun interface AccountSetupFinishedLauncher {
suspend fun launch(accountUuid: String)
}
}

View file

@ -23,11 +23,17 @@ import app.k9mail.feature.account.setup.ui.outgoing.AccountOutgoingConfigViewMod
import com.fsck.k9.mail.store.imap.ImapServerSettingsValidator
import com.fsck.k9.mail.store.pop3.Pop3ServerSettingsValidator
import com.fsck.k9.mail.transport.smtp.SmtpServerSettingsValidator
import okhttp3.OkHttpClient
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.core.module.Module
import org.koin.dsl.module
val featureAccountSetupModule: Module = module {
single<OkHttpClient> {
OkHttpClient()
}
single<AutoDiscoveryService> {
RealAutoDiscoveryService(
okHttpClient = get(),

View file

@ -0,0 +1,25 @@
plugins {
id(ThunderbirdPlugins.Library.androidCompose)
}
android {
namespace = "app.k9mail.feature.launcher"
resourcePrefix = "launcher_"
buildTypes {
debug {
manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
}
release {
manifestPlaceholders["appAuthRedirectScheme"] = "FIXME: override this in your app project"
}
}
}
dependencies {
implementation(projects.core.ui.compose.designsystem)
implementation(projects.feature.onboarding)
implementation(projects.feature.account.setup)
testImplementation(projects.core.ui.compose.testing)
}

View file

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:supportsRtl="true">
<activity
android:name=".FeatureLauncherActivity"
android:windowSoftInputMode="adjustResize"
/>
</application>
</manifest>

View file

@ -0,0 +1,49 @@
package app.k9mail.feature.launcher
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.view.WindowCompat
import app.k9mail.feature.account.setup.navigation.NAVIGATION_ROUTE_ACCOUNT_SETUP
import app.k9mail.feature.launcher.ui.FeatureLauncherApp
import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING
class FeatureLauncherActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
val destination = intent.getStringExtra(EXTRA_DESTINATION)
setContent {
FeatureLauncherApp(startDestination = destination)
}
}
companion object {
private const val EXTRA_DESTINATION = "destination"
private const val DESTINATION_ONBOARDING = NAVIGATION_ROUTE_ONBOARDING
private const val DESTINATION_SETUP_ACCOUNT = NAVIGATION_ROUTE_ACCOUNT_SETUP
@JvmStatic
fun launchOnboarding(context: Activity) {
val intent = Intent(context, FeatureLauncherActivity::class.java).apply {
putExtra(EXTRA_DESTINATION, DESTINATION_ONBOARDING)
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
context.startActivity(intent)
}
@JvmStatic
fun launchSetupAccount(context: Activity) {
val intent = Intent(context, FeatureLauncherActivity::class.java).apply {
putExtra(EXTRA_DESTINATION, DESTINATION_SETUP_ACCOUNT)
}
context.startActivity(intent)
}
}
}

View file

@ -0,0 +1,8 @@
package app.k9mail.feature.launcher
interface FeatureLauncherExternalContract {
fun interface AccountSetupFinishedLauncher {
fun launch(accountUuid: String)
}
}

View file

@ -0,0 +1,10 @@
package app.k9mail.feature.launcher.di
import app.k9mail.feature.account.setup.featureAccountSetupModule
import org.koin.dsl.module
val featureLauncherModule = module {
includes(
featureAccountSetupModule,
)
}

View file

@ -0,0 +1,35 @@
package app.k9mail.feature.launcher.navigation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import app.k9mail.feature.account.setup.navigation.accountSetupRoute
import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup
import app.k9mail.feature.launcher.FeatureLauncherExternalContract.AccountSetupFinishedLauncher
import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING
import app.k9mail.feature.onboarding.navigation.onboardingRoute
import org.koin.compose.koinInject
@Composable
fun FeatureLauncherNavHost(
navController: NavHostController,
startDestination: String?,
modifier: Modifier = Modifier,
accountSetupFinishedLauncher: AccountSetupFinishedLauncher = koinInject(),
) {
NavHost(
navController = navController,
startDestination = startDestination ?: NAVIGATION_ROUTE_ONBOARDING,
modifier = modifier,
) {
onboardingRoute(
onStart = { navController.navigateToAccountSetup() },
onImport = { /* TODO */ },
)
accountSetupRoute(
onBack = navController::popBackStack,
onFinish = { accountSetupFinishedLauncher.launch(it) },
)
}
}

View file

@ -0,0 +1,32 @@
package app.k9mail.feature.launcher.ui
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.navigation.compose.rememberNavController
import app.k9mail.core.ui.compose.designsystem.atom.Background
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.feature.launcher.navigation.FeatureLauncherNavHost
@Composable
fun FeatureLauncherApp(
startDestination: String?,
modifier: Modifier = Modifier,
) {
val navController = rememberNavController()
K9Theme {
Background(
modifier = Modifier
.fillMaxSize()
.safeDrawingPadding()
.then(modifier),
) {
FeatureLauncherNavHost(
navController = navController,
startDestination = startDestination,
)
}
}
}

View file

@ -39,6 +39,7 @@ include(
)
include(
":feature:launcher",
":feature:account:setup",
":feature:account:oauth",
":feature:onboarding",