Repo created
This commit is contained in:
parent
75dc487a7a
commit
39c29d175b
6317 changed files with 388324 additions and 2 deletions
17
feature/account/common/build.gradle.kts
Normal file
17
feature/account/common/build.gradle.kts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
plugins {
|
||||
id(ThunderbirdPlugins.Library.androidCompose)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "app.k9mail.feature.account.common"
|
||||
resourcePrefix = "account_common_"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.core.ui.compose.designsystem)
|
||||
implementation(projects.core.common)
|
||||
|
||||
implementation(projects.mail.common)
|
||||
|
||||
testImplementation(projects.core.ui.compose.testing)
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import app.k9mail.core.ui.compose.common.annotation.PreviewDevices
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
|
||||
@Composable
|
||||
@PreviewDevices
|
||||
internal fun AccountTopAppBarPreview() {
|
||||
PreviewWithThemes {
|
||||
AccountTopAppBar(
|
||||
title = "Title",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun AppTitleTopHeaderPreview() {
|
||||
PreviewWithThemes {
|
||||
AppTitleTopHeader(title = "Title")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextTitleMedium
|
||||
|
||||
@Composable
|
||||
@Preview(showBackground = true)
|
||||
internal fun ContentListViewPreview() {
|
||||
PreviewWithThemes {
|
||||
ContentListView {
|
||||
item {
|
||||
TextTitleMedium("Item 1")
|
||||
}
|
||||
item {
|
||||
TextTitleMedium("Item 2")
|
||||
}
|
||||
item {
|
||||
TextTitleMedium("Item 3")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import app.k9mail.core.ui.compose.common.annotation.PreviewDevices
|
||||
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
|
||||
|
||||
@Composable
|
||||
@PreviewDevices
|
||||
internal fun WizardNavigationBarPreview() {
|
||||
PreviewWithThemes {
|
||||
WizardNavigationBar(
|
||||
onNextClick = {},
|
||||
onBackClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewDevices
|
||||
internal fun WizardNavigationBarDisabledPreview() {
|
||||
PreviewWithThemes {
|
||||
WizardNavigationBar(
|
||||
onNextClick = {},
|
||||
onBackClick = {},
|
||||
state = WizardNavigationBarState(
|
||||
isNextEnabled = false,
|
||||
isBackEnabled = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewDevices
|
||||
internal fun WizardNavigationBarHideNextPreview() {
|
||||
PreviewWithThemes {
|
||||
WizardNavigationBar(
|
||||
onNextClick = {},
|
||||
onBackClick = {},
|
||||
state = WizardNavigationBarState(
|
||||
showNext = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@PreviewDevices
|
||||
internal fun WizardNavigationBarHideBackPreview() {
|
||||
PreviewWithThemes {
|
||||
WizardNavigationBar(
|
||||
onNextClick = {},
|
||||
onBackClick = {},
|
||||
state = WizardNavigationBarState(
|
||||
showBack = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
package app.k9mail.feature.account.common.ui.fake
|
||||
|
||||
import app.k9mail.feature.account.common.domain.AccountDomainContract
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountState
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
|
||||
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
|
||||
import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class FakeAccountStateRepository : AccountDomainContract.AccountStateRepository {
|
||||
|
||||
override fun getState(): AccountState = AccountState(
|
||||
emailAddress = "test@example.com",
|
||||
incomingServerSettings = ServerSettings(
|
||||
type = "imap",
|
||||
host = "imap.example.com",
|
||||
port = 993,
|
||||
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "test",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
),
|
||||
outgoingServerSettings = ServerSettings(
|
||||
type = "smtp",
|
||||
host = "smtp.example.com",
|
||||
port = 465,
|
||||
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
|
||||
authenticationType = AuthType.PLAIN,
|
||||
username = "test",
|
||||
password = "password",
|
||||
clientCertificateAlias = null,
|
||||
),
|
||||
)
|
||||
|
||||
override fun setState(accountState: AccountState) = Unit
|
||||
|
||||
override fun setEmailAddress(emailAddress: String) = Unit
|
||||
|
||||
override fun setIncomingServerSettings(serverSettings: ServerSettings) = Unit
|
||||
|
||||
override fun setOutgoingServerSettings(serverSettings: ServerSettings) = Unit
|
||||
|
||||
override fun setAuthorizationState(authorizationState: AuthorizationState) = Unit
|
||||
|
||||
override fun setSpecialFolderSettings(specialFolderSettings: SpecialFolderSettings) = Unit
|
||||
|
||||
override fun setDisplayOptions(displayOptions: AccountDisplayOptions) = Unit
|
||||
|
||||
override fun setSyncOptions(syncOptions: AccountSyncOptions) = Unit
|
||||
|
||||
override fun clear() = Unit
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package app.k9mail.feature.account.common
|
||||
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountState
|
||||
|
||||
interface AccountCommonExternalContract {
|
||||
|
||||
fun interface AccountStateLoader {
|
||||
suspend fun loadAccountState(accountUuid: String): AccountState?
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package app.k9mail.feature.account.common
|
||||
|
||||
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
|
||||
import app.k9mail.feature.account.common.domain.AccountDomainContract
|
||||
import com.fsck.k9.mail.oauth.AuthStateStorage
|
||||
import net.thunderbird.core.common.coreCommonModule
|
||||
import org.koin.core.module.Module
|
||||
import org.koin.dsl.binds
|
||||
import org.koin.dsl.module
|
||||
|
||||
val featureAccountCommonModule: Module = module {
|
||||
includes(coreCommonModule)
|
||||
|
||||
single {
|
||||
InMemoryAccountStateRepository()
|
||||
}.binds(arrayOf(AccountDomainContract.AccountStateRepository::class, AuthStateStorage::class))
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package app.k9mail.feature.account.common.data
|
||||
|
||||
import app.k9mail.feature.account.common.domain.AccountDomainContract
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountState
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
|
||||
import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
import com.fsck.k9.mail.oauth.AuthStateStorage
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class InMemoryAccountStateRepository(
|
||||
private var state: AccountState = AccountState(),
|
||||
) : AccountDomainContract.AccountStateRepository, AuthStateStorage {
|
||||
|
||||
override fun getState(): AccountState {
|
||||
return state
|
||||
}
|
||||
|
||||
override fun setState(accountState: AccountState) {
|
||||
state = accountState
|
||||
}
|
||||
|
||||
override fun setEmailAddress(emailAddress: String) {
|
||||
state = state.copy(emailAddress = emailAddress)
|
||||
}
|
||||
|
||||
override fun setIncomingServerSettings(serverSettings: ServerSettings) {
|
||||
state = state.copy(incomingServerSettings = serverSettings)
|
||||
}
|
||||
|
||||
override fun setOutgoingServerSettings(serverSettings: ServerSettings) {
|
||||
state = state.copy(outgoingServerSettings = serverSettings)
|
||||
}
|
||||
|
||||
override fun setAuthorizationState(authorizationState: AuthorizationState) {
|
||||
state = state.copy(authorizationState = authorizationState)
|
||||
}
|
||||
|
||||
override fun setSpecialFolderSettings(specialFolderSettings: SpecialFolderSettings) {
|
||||
state = state.copy(specialFolderSettings = specialFolderSettings)
|
||||
}
|
||||
|
||||
override fun setDisplayOptions(displayOptions: AccountDisplayOptions) {
|
||||
state = state.copy(displayOptions = displayOptions)
|
||||
}
|
||||
|
||||
override fun setSyncOptions(syncOptions: AccountSyncOptions) {
|
||||
state = state.copy(syncOptions = syncOptions)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
state = AccountState()
|
||||
}
|
||||
|
||||
override fun getAuthorizationState(): String? {
|
||||
return state.authorizationState?.value
|
||||
}
|
||||
|
||||
override fun updateAuthorizationState(authorizationState: String?) {
|
||||
state = state.copy(authorizationState = AuthorizationState(authorizationState))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
package app.k9mail.feature.account.common.domain
|
||||
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountState
|
||||
import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions
|
||||
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
|
||||
import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
|
||||
interface AccountDomainContract {
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
interface AccountStateRepository {
|
||||
fun getState(): AccountState
|
||||
|
||||
fun setState(accountState: AccountState)
|
||||
|
||||
fun setEmailAddress(emailAddress: String)
|
||||
|
||||
fun setIncomingServerSettings(serverSettings: ServerSettings)
|
||||
|
||||
fun setOutgoingServerSettings(serverSettings: ServerSettings)
|
||||
|
||||
fun setAuthorizationState(authorizationState: AuthorizationState)
|
||||
|
||||
fun setSpecialFolderSettings(specialFolderSettings: SpecialFolderSettings)
|
||||
|
||||
fun setDisplayOptions(displayOptions: AccountDisplayOptions)
|
||||
|
||||
fun setSyncOptions(syncOptions: AccountSyncOptions)
|
||||
|
||||
fun clear()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
|
||||
data class Account(
|
||||
val uuid: String,
|
||||
val emailAddress: String,
|
||||
val incomingServerSettings: ServerSettings,
|
||||
val outgoingServerSettings: ServerSettings,
|
||||
val authorizationState: String?,
|
||||
val specialFolderSettings: SpecialFolderSettings?,
|
||||
val options: AccountOptions,
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class AccountDisplayOptions(
|
||||
val accountName: String,
|
||||
val displayName: String,
|
||||
val emailSignature: String?,
|
||||
)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class AccountOptions(
|
||||
val accountName: String,
|
||||
val displayName: String,
|
||||
val emailSignature: String?,
|
||||
val checkFrequencyInMinutes: Int,
|
||||
val messageDisplayCount: Int,
|
||||
val showNotification: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import com.fsck.k9.mail.ServerSettings
|
||||
|
||||
data class AccountState(
|
||||
val uuid: String? = null,
|
||||
val emailAddress: String? = null,
|
||||
val incomingServerSettings: ServerSettings? = null,
|
||||
val outgoingServerSettings: ServerSettings? = null,
|
||||
val authorizationState: AuthorizationState? = null,
|
||||
val specialFolderSettings: SpecialFolderSettings? = null,
|
||||
val displayOptions: AccountDisplayOptions? = null,
|
||||
val syncOptions: AccountSyncOptions? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class AccountSyncOptions(
|
||||
val checkFrequencyInMinutes: Int,
|
||||
val messageDisplayCount: Int,
|
||||
val showNotification: Boolean,
|
||||
)
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import com.fsck.k9.mail.AuthType
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class AuthenticationType(
|
||||
val isUsernameRequired: Boolean,
|
||||
val isPasswordRequired: Boolean,
|
||||
) {
|
||||
None(
|
||||
isUsernameRequired = false,
|
||||
isPasswordRequired = false,
|
||||
),
|
||||
PasswordCleartext(
|
||||
isUsernameRequired = true,
|
||||
isPasswordRequired = true,
|
||||
),
|
||||
PasswordEncrypted(
|
||||
isUsernameRequired = true,
|
||||
isPasswordRequired = true,
|
||||
),
|
||||
ClientCertificate(
|
||||
isUsernameRequired = true,
|
||||
isPasswordRequired = false,
|
||||
),
|
||||
OAuth2(
|
||||
isUsernameRequired = true,
|
||||
isPasswordRequired = false,
|
||||
),
|
||||
;
|
||||
|
||||
companion object {
|
||||
val DEFAULT = PasswordCleartext
|
||||
fun all() = entries.toImmutableList()
|
||||
|
||||
fun outgoing() = all()
|
||||
}
|
||||
}
|
||||
|
||||
fun AuthenticationType.toAuthType(): AuthType {
|
||||
return when (this) {
|
||||
AuthenticationType.None -> AuthType.NONE
|
||||
AuthenticationType.PasswordCleartext -> AuthType.PLAIN
|
||||
AuthenticationType.PasswordEncrypted -> AuthType.CRAM_MD5
|
||||
AuthenticationType.ClientCertificate -> AuthType.EXTERNAL
|
||||
AuthenticationType.OAuth2 -> AuthType.XOAUTH2
|
||||
}
|
||||
}
|
||||
|
||||
fun AuthType.toAuthenticationType(): AuthenticationType {
|
||||
return when (this) {
|
||||
AuthType.PLAIN -> AuthenticationType.PasswordCleartext
|
||||
AuthType.CRAM_MD5 -> AuthenticationType.PasswordEncrypted
|
||||
AuthType.EXTERNAL -> AuthenticationType.ClientCertificate
|
||||
AuthType.XOAUTH2 -> AuthenticationType.OAuth2
|
||||
AuthType.NONE -> AuthenticationType.None
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class AuthorizationState(
|
||||
val value: String? = null,
|
||||
)
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity.None
|
||||
import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity.StartTLS
|
||||
import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity.TLS
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class ConnectionSecurity {
|
||||
None,
|
||||
StartTLS,
|
||||
TLS,
|
||||
;
|
||||
|
||||
companion object {
|
||||
val DEFAULT = TLS
|
||||
fun all() = entries.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun ConnectionSecurity.toMailConnectionSecurity(): MailConnectionSecurity {
|
||||
return when (this) {
|
||||
None -> MailConnectionSecurity.NONE
|
||||
StartTLS -> MailConnectionSecurity.STARTTLS_REQUIRED
|
||||
TLS -> MailConnectionSecurity.SSL_TLS_REQUIRED
|
||||
}
|
||||
}
|
||||
|
||||
fun MailConnectionSecurity.toConnectionSecurity(): ConnectionSecurity {
|
||||
return when (this) {
|
||||
MailConnectionSecurity.NONE -> None
|
||||
MailConnectionSecurity.STARTTLS_REQUIRED -> StartTLS
|
||||
MailConnectionSecurity.SSL_TLS_REQUIRED -> TLS
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun ConnectionSecurity.toSmtpDefaultPort(): Long {
|
||||
return when (this) {
|
||||
None -> 587
|
||||
StartTLS -> 587
|
||||
TLS -> 465
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun ConnectionSecurity.toImapDefaultPort(): Long {
|
||||
return when (this) {
|
||||
None -> 143
|
||||
StartTLS -> 143
|
||||
TLS -> 993
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
fun ConnectionSecurity.toPop3DefaultPort(): Long {
|
||||
return when (this) {
|
||||
None -> 110
|
||||
StartTLS -> 110
|
||||
TLS -> 995
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class IncomingProtocolType(
|
||||
val defaultName: String,
|
||||
val defaultConnectionSecurity: ConnectionSecurity,
|
||||
) {
|
||||
IMAP("imap", ConnectionSecurity.TLS),
|
||||
POP3("pop3", ConnectionSecurity.TLS),
|
||||
;
|
||||
|
||||
companion object {
|
||||
val DEFAULT = IMAP
|
||||
|
||||
fun all() = entries.toImmutableList()
|
||||
|
||||
fun fromName(name: String): IncomingProtocolType {
|
||||
return entries.find { it.defaultName == name } ?: throw IllegalArgumentException("Unknown protocol: $name")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IncomingProtocolType.toDefaultPort(connectionSecurity: ConnectionSecurity): Long {
|
||||
return when (this) {
|
||||
IncomingProtocolType.IMAP -> connectionSecurity.toImapDefaultPort()
|
||||
IncomingProtocolType.POP3 -> connectionSecurity.toPop3DefaultPort()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
/**
|
||||
* Enum representing the mode a user is interacting with an account or setting.
|
||||
*/
|
||||
enum class InteractionMode {
|
||||
Create,
|
||||
Edit,
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
typealias MailConnectionSecurity = com.fsck.k9.mail.ConnectionSecurity
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
|
||||
enum class OutgoingProtocolType(
|
||||
val defaultName: String,
|
||||
val defaultConnectionSecurity: ConnectionSecurity,
|
||||
) {
|
||||
SMTP("smtp", ConnectionSecurity.TLS),
|
||||
;
|
||||
|
||||
companion object {
|
||||
val DEFAULT = SMTP
|
||||
|
||||
fun all() = entries.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
fun OutgoingProtocolType.toDefaultPort(connectionSecurity: ConnectionSecurity): Long {
|
||||
return when (this) {
|
||||
OutgoingProtocolType.SMTP -> connectionSecurity.toSmtpDefaultPort()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
import com.fsck.k9.mail.folders.RemoteFolder
|
||||
|
||||
sealed interface SpecialFolderOption {
|
||||
data class None(
|
||||
val isAutomatic: Boolean = false,
|
||||
) : SpecialFolderOption
|
||||
|
||||
data class Regular(
|
||||
val remoteFolder: RemoteFolder,
|
||||
) : SpecialFolderOption
|
||||
|
||||
data class Special(
|
||||
val isAutomatic: Boolean = false,
|
||||
val remoteFolder: RemoteFolder,
|
||||
) : SpecialFolderOption
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class SpecialFolderOptions(
|
||||
val archiveSpecialFolderOptions: List<SpecialFolderOption>,
|
||||
val draftsSpecialFolderOptions: List<SpecialFolderOption>,
|
||||
val sentSpecialFolderOptions: List<SpecialFolderOption>,
|
||||
val spamSpecialFolderOptions: List<SpecialFolderOption>,
|
||||
val trashSpecialFolderOptions: List<SpecialFolderOption>,
|
||||
)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class SpecialFolderSettings(
|
||||
val archiveSpecialFolderOption: SpecialFolderOption,
|
||||
val draftsSpecialFolderOption: SpecialFolderOption,
|
||||
val sentSpecialFolderOption: SpecialFolderOption,
|
||||
val spamSpecialFolderOption: SpecialFolderOption,
|
||||
val trashSpecialFolderOption: SpecialFolderOption,
|
||||
)
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package app.k9mail.feature.account.common.domain.input
|
||||
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationError
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationResult
|
||||
|
||||
class BooleanInputField(
|
||||
override val value: Boolean? = null,
|
||||
override val error: ValidationError? = null,
|
||||
override val isValid: Boolean = false,
|
||||
) : InputField<Boolean?> {
|
||||
override fun updateValue(value: Boolean?): BooleanInputField {
|
||||
return BooleanInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateError(error: ValidationError?): BooleanInputField {
|
||||
return BooleanInputField(
|
||||
value = value,
|
||||
error = error,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateValidity(isValid: Boolean): BooleanInputField {
|
||||
if (isValid == this.isValid) return this
|
||||
|
||||
return BooleanInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = isValid,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateFromValidationResult(result: ValidationResult): BooleanInputField {
|
||||
return when (result) {
|
||||
is ValidationResult.Success -> BooleanInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = true,
|
||||
)
|
||||
|
||||
is ValidationResult.Failure -> BooleanInputField(
|
||||
value = value,
|
||||
error = result.error,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as BooleanInputField
|
||||
|
||||
if (value != other.value) return false
|
||||
if (error != other.error) return false
|
||||
return isValid == other.isValid
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = value?.hashCode() ?: 0
|
||||
result = 31 * result + (error?.hashCode() ?: 0)
|
||||
result = 31 * result + isValid.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "BooleanInputField(value=$value, error=$error, isValid=$isValid)"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
package app.k9mail.feature.account.common.domain.input
|
||||
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationError
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationResult
|
||||
|
||||
/**
|
||||
* InputField is an interface defining the state of an input field.
|
||||
*
|
||||
* @param T The type of the value the input field holds.
|
||||
*/
|
||||
interface InputField<T> {
|
||||
val value: T
|
||||
val error: ValidationError?
|
||||
val isValid: Boolean
|
||||
|
||||
/**
|
||||
* Updates the current value of the input field.
|
||||
*
|
||||
* @param value The new value to be set for the input field.
|
||||
* @return a new InputField instance with the updated value.
|
||||
*/
|
||||
fun updateValue(value: T): InputField<T>
|
||||
|
||||
/**
|
||||
* Updates the current error of the input field.
|
||||
*
|
||||
* @param error The new error to be set for the input field.
|
||||
*/
|
||||
fun updateError(error: ValidationError?): InputField<T>
|
||||
|
||||
/**
|
||||
* Updates the current validity of the input field.
|
||||
*
|
||||
* @param isValid The new validity to be set for the input field.
|
||||
*/
|
||||
fun updateValidity(isValid: Boolean): InputField<T>
|
||||
|
||||
/**
|
||||
* Checks if the input field currently has an error.
|
||||
*
|
||||
* @return a Boolean indicating whether the input field has an error.
|
||||
*/
|
||||
fun hasError(): Boolean {
|
||||
return error != null
|
||||
}
|
||||
|
||||
fun updateFromValidationResult(result: ValidationResult): InputField<T>
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package app.k9mail.feature.account.common.domain.input
|
||||
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationError
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationResult
|
||||
|
||||
class NumberInputField(
|
||||
override val value: Long? = null,
|
||||
override val error: ValidationError? = null,
|
||||
override val isValid: Boolean = false,
|
||||
) : InputField<Long?> {
|
||||
|
||||
override fun updateValue(value: Long?): NumberInputField {
|
||||
return NumberInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateError(error: ValidationError?): NumberInputField {
|
||||
return NumberInputField(
|
||||
value = value,
|
||||
error = error,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateValidity(isValid: Boolean): NumberInputField {
|
||||
if (isValid == this.isValid) return this
|
||||
|
||||
return NumberInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = isValid,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateFromValidationResult(result: ValidationResult): NumberInputField {
|
||||
return when (result) {
|
||||
is ValidationResult.Success -> NumberInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = true,
|
||||
)
|
||||
|
||||
is ValidationResult.Failure -> NumberInputField(
|
||||
value = value,
|
||||
error = result.error,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as NumberInputField
|
||||
|
||||
if (value != other.value) return false
|
||||
if (error != other.error) return false
|
||||
return isValid == other.isValid
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = value?.hashCode() ?: 0
|
||||
result = 31 * result + (error?.hashCode() ?: 0)
|
||||
result = 31 * result + isValid.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "NumberInputField(value=$value, error=$error, isValid=$isValid)"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
package app.k9mail.feature.account.common.domain.input
|
||||
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationError
|
||||
import net.thunderbird.core.common.domain.usecase.validation.ValidationResult
|
||||
|
||||
class StringInputField(
|
||||
override val value: String = "",
|
||||
override val error: ValidationError? = null,
|
||||
override val isValid: Boolean = false,
|
||||
) : InputField<String> {
|
||||
|
||||
override fun updateValue(value: String): StringInputField {
|
||||
return StringInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateError(error: ValidationError?): StringInputField {
|
||||
return StringInputField(
|
||||
value = value,
|
||||
error = error,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateValidity(isValid: Boolean): StringInputField {
|
||||
if (isValid == this.isValid) return this
|
||||
|
||||
return StringInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = isValid,
|
||||
)
|
||||
}
|
||||
|
||||
override fun updateFromValidationResult(result: ValidationResult): StringInputField {
|
||||
return when (result) {
|
||||
is ValidationResult.Success -> StringInputField(
|
||||
value = value,
|
||||
error = null,
|
||||
isValid = true,
|
||||
)
|
||||
|
||||
is ValidationResult.Failure -> StringInputField(
|
||||
value = value,
|
||||
error = result.error,
|
||||
isValid = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as StringInputField
|
||||
|
||||
if (value != other.value) return false
|
||||
if (error != other.error) return false
|
||||
return isValid == other.isValid
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = value.hashCode()
|
||||
result = 31 * result + (error?.hashCode() ?: 0)
|
||||
result = 31 * result + isValid.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "StringInputField(value='$value', error=$error, isValid=$isValid)"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBar
|
||||
|
||||
/**
|
||||
* Top app bar for the account screens.
|
||||
*/
|
||||
@Composable
|
||||
fun AccountTopAppBar(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = title,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.text.TextDisplayMediumAutoResize
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
private const val TITLE_ICON_SIZE_DP = 56
|
||||
|
||||
@Composable
|
||||
fun AppTitleTopHeader(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
ResponsiveWidthContainer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
top = MainTheme.spacings.quadruple,
|
||||
bottom = MainTheme.spacings.default,
|
||||
)
|
||||
.then(modifier),
|
||||
) { contentPadding ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = MainTheme.spacings.half,
|
||||
end = MainTheme.spacings.quadruple,
|
||||
)
|
||||
.padding(contentPadding)
|
||||
.then(modifier),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = MainTheme.images.logo),
|
||||
modifier = Modifier
|
||||
.padding(all = MainTheme.spacings.default)
|
||||
.padding(end = MainTheme.spacings.default)
|
||||
.size(TITLE_ICON_SIZE_DP.dp),
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
TextDisplayMediumAutoResize(text = title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
@Composable
|
||||
fun ContentListView(
|
||||
modifier: Modifier = Modifier,
|
||||
contentPadding: PaddingValues = PaddingValues(),
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
verticalArrangement: Arrangement.Vertical = Arrangement.spacedBy(MainTheme.spacings.default),
|
||||
items: LazyListScope.() -> Unit,
|
||||
) {
|
||||
ResponsiveWidthContainer(
|
||||
modifier = Modifier
|
||||
.padding(contentPadding)
|
||||
.fillMaxWidth()
|
||||
.then(modifier),
|
||||
) { contentPadding ->
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.imePadding(),
|
||||
contentPadding = contentPadding,
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
verticalArrangement = verticalArrangement,
|
||||
) {
|
||||
items()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import app.k9mail.feature.account.common.domain.entity.InteractionMode
|
||||
|
||||
/**
|
||||
* Interface for screens that can be used in different interaction modes.
|
||||
*/
|
||||
interface WithInteractionMode {
|
||||
val mode: InteractionMode
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
object WizardConstants {
|
||||
const val CONTINUE_NEXT_DELAY = 500L
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonFilled
|
||||
import app.k9mail.core.ui.compose.designsystem.atom.button.ButtonOutlined
|
||||
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
import app.k9mail.feature.account.common.R
|
||||
import net.thunderbird.core.ui.compose.common.modifier.testTagAsResourceId
|
||||
|
||||
@Composable
|
||||
fun WizardNavigationBar(
|
||||
onNextClick: () -> Unit,
|
||||
onBackClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
nextButtonText: String = stringResource(id = R.string.account_common_button_next),
|
||||
backButtonText: String = stringResource(id = R.string.account_common_button_back),
|
||||
state: WizardNavigationBarState = WizardNavigationBarState(),
|
||||
) {
|
||||
ResponsiveWidthContainer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.then(modifier),
|
||||
) { contentPadding ->
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = MainTheme.spacings.quadruple,
|
||||
top = MainTheme.spacings.default,
|
||||
end = MainTheme.spacings.quadruple,
|
||||
bottom = MainTheme.spacings.double,
|
||||
)
|
||||
.padding(contentPadding)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = getHorizontalArrangement(state),
|
||||
) {
|
||||
if (state.showBack) {
|
||||
ButtonOutlined(
|
||||
text = backButtonText,
|
||||
onClick = onBackClick,
|
||||
enabled = state.isBackEnabled,
|
||||
modifier = Modifier.testTagAsResourceId("account_setup_back_button"),
|
||||
)
|
||||
}
|
||||
if (state.showNext) {
|
||||
ButtonFilled(
|
||||
text = nextButtonText,
|
||||
onClick = onNextClick,
|
||||
enabled = state.isNextEnabled,
|
||||
modifier = Modifier.testTagAsResourceId("account_setup_next_button"),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHorizontalArrangement(state: WizardNavigationBarState): Arrangement.Horizontal {
|
||||
return if (state.showNext && state.showBack) {
|
||||
Arrangement.SpaceBetween
|
||||
} else if (state.showNext) {
|
||||
Arrangement.End
|
||||
} else {
|
||||
Arrangement.Start
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
package app.k9mail.feature.account.common.ui
|
||||
|
||||
data class WizardNavigationBarState(
|
||||
val isNextEnabled: Boolean = true,
|
||||
val showNext: Boolean = true,
|
||||
val isBackEnabled: Boolean = true,
|
||||
val showBack: Boolean = true,
|
||||
)
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package app.k9mail.feature.account.common.ui.item
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyItemScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
|
||||
|
||||
@Composable
|
||||
fun LazyItemScope.ErrorItem(
|
||||
title: String,
|
||||
modifier: Modifier = Modifier,
|
||||
message: String? = null,
|
||||
onRetry: () -> Unit = { },
|
||||
) {
|
||||
ListItem(
|
||||
modifier = modifier,
|
||||
) {
|
||||
ErrorView(
|
||||
title = title,
|
||||
message = message,
|
||||
onRetry = onRetry,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package app.k9mail.feature.account.common.ui.item
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import app.k9mail.core.ui.compose.theme2.MainTheme
|
||||
|
||||
@Composable
|
||||
fun defaultHeadlineItemPadding() = PaddingValues(
|
||||
start = MainTheme.spacings.quadruple,
|
||||
top = MainTheme.spacings.triple,
|
||||
end = MainTheme.spacings.quadruple,
|
||||
bottom = MainTheme.spacings.default,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun defaultItemPadding() = PaddingValues(
|
||||
horizontal = MainTheme.spacings.quadruple,
|
||||
vertical = MainTheme.spacings.zero,
|
||||
)
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package app.k9mail.feature.account.common.ui.item
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyItemScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun LazyItemScope.ListItem(
|
||||
modifier: Modifier = Modifier,
|
||||
contentPaddingValues: PaddingValues = defaultItemPadding(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(contentPaddingValues)
|
||||
.animateItem()
|
||||
.fillMaxWidth()
|
||||
.then(modifier),
|
||||
) {
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package app.k9mail.feature.account.common.ui.item
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyItemScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
|
||||
|
||||
@Composable
|
||||
fun LazyItemScope.LoadingItem(
|
||||
modifier: Modifier = Modifier,
|
||||
message: String? = null,
|
||||
) {
|
||||
ListItem(
|
||||
modifier = modifier,
|
||||
) {
|
||||
LoadingView(
|
||||
message = message,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">قام الخادم بإرجاع الرسالة التالية:\n%s</string>
|
||||
<string name="account_common_button_next">التالي</string>
|
||||
<string name="account_common_button_back">السابق</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Növbəti</string>
|
||||
<string name="account_common_button_back">Geri</string>
|
||||
<string name="account_common_error_server_message">Server aşağıdakı məlumatı verdi: \n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Далей</string>
|
||||
<string name="account_common_button_back">Назад</string>
|
||||
<string name="account_common_error_server_message">Сервер вярнуў наступнае паведамленне:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Напред</string>
|
||||
<string name="account_common_button_back">Назад</string>
|
||||
<string name="account_common_error_server_message">Сървърът връща следното съобщение\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_back">পিছনে</string>
|
||||
<string name="account_common_error_server_message">সার্ভার এই বার্তাটি পাঠিয়েছে:\n%s</string>
|
||||
<string name="account_common_button_next">পরবর্তী</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Da heul</string>
|
||||
<string name="account_common_button_back">Kent</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Sljedeći</string>
|
||||
<string name="account_common_button_back">Nazad</string>
|
||||
<string name="account_common_error_server_message">Server je odgovorio ovom porukom:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Següent</string>
|
||||
<string name="account_common_button_back">Enrere</string>
|
||||
<string name="account_common_error_server_message">El servidor ha retornat en següent missatge:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Seguente</string>
|
||||
<string name="account_common_button_back">Ritornu</string>
|
||||
<string name="account_common_error_server_message">U servitore hà mandatu quessu messaghju :
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Další</string>
|
||||
<string name="account_common_button_back">Zpět</string>
|
||||
<string name="account_common_error_server_message">Server vrátil následující zprávu:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Nesaf</string>
|
||||
<string name="account_common_button_back">Nôl</string>
|
||||
<string name="account_common_error_server_message">Dychwelodd y gweinydd y negae ganlynol:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Næste</string>
|
||||
<string name="account_common_button_back">Tilbage</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Weiter</string>
|
||||
<string name="account_common_button_back">Zurück</string>
|
||||
<string name="account_common_error_server_message">Der Server hat die folgende Nachricht zurückgegeben:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Επόμενο</string>
|
||||
<string name="account_common_button_back">Πίσω</string>
|
||||
<string name="account_common_error_server_message">Ο διακομιστής επέστρεψε το ακόλουθο μήνυμα:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Next</string>
|
||||
<string name="account_common_button_back">Back</string>
|
||||
<string name="account_common_error_server_message">The server returned the following message:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">La servilo sendis la jenan mesaĝon:
|
||||
\n%s</string>
|
||||
<string name="account_common_button_next">Sekven</string>
|
||||
<string name="account_common_button_back">Reen</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Siguiente</string>
|
||||
<string name="account_common_button_back">Atrás</string>
|
||||
<string name="account_common_error_server_message">El servidor devolvió el siguiente mensaje:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Edasi</string>
|
||||
<string name="account_common_button_back">Tagasi</string>
|
||||
<string name="account_common_error_server_message">Serveri vastuses sisaldus selline sõnum:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Hurrengoa</string>
|
||||
<string name="account_common_button_back">Aurrekoa</string>
|
||||
<string name="account_common_error_server_message">Zerbitzariak mezu hau itzuli du:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">بعدی</string>
|
||||
<string name="account_common_button_back">بازگشت</string>
|
||||
<string name="account_common_error_server_message">کارساز، پیام زیر را برگردانده است:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Seuraava</string>
|
||||
<string name="account_common_button_back">Takaisin</string>
|
||||
<string name="account_common_error_server_message">Palvelin palautti seuraavan viestin:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Suivant</string>
|
||||
<string name="account_common_button_back">Précédent</string>
|
||||
<string name="account_common_error_server_message">Le message suivant a été retourné par le serveur :\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Folgjende</string>
|
||||
<string name="account_common_button_back">Tebek</string>
|
||||
<string name="account_common_error_server_message">De server joech it folgjende berjocht werom:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Ar aghaidh</string>
|
||||
<string name="account_common_button_back">Ar ais</string>
|
||||
<string name="account_common_error_server_message">Chuir an freastalaí an teachtaireacht seo a leanas ar ais:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">Thill am frithealaiche an teachdaireachd a leanas:\n%s</string>
|
||||
<string name="account_common_button_next">Air adhart</string>
|
||||
<string name="account_common_button_back">Air ais</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">seguinte</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">अगला</string>
|
||||
<string name="account_common_button_back">पिछला</string>
|
||||
<string name="account_common_error_server_message">सेवक ने निम्न संदेश लौटायाः\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Sljedeće</string>
|
||||
<string name="account_common_button_back">Natrag</string>
|
||||
<string name="account_common_error_server_message">Poslužitelj je vratio sljedeću poruku:\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Következő</string>
|
||||
<string name="account_common_button_back">Vissza</string>
|
||||
<string name="account_common_error_server_message">A kiszolgáló a következő üzenetet küldte vissza:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">Peladen mengembalikan pesan berikut ini:\n%s</string>
|
||||
<string name="account_common_button_next">Berikutnya</string>
|
||||
<string name="account_common_button_back">Kembali</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Næsta</string>
|
||||
<string name="account_common_button_back">Til baka</string>
|
||||
<string name="account_common_error_server_message">Póstþjónninn svaraði með eftirfarandi skilaboðum:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Successivo</string>
|
||||
<string name="account_common_button_back">Precedente</string>
|
||||
<string name="account_common_error_server_message">Il server ha restituito il seguente messaggio:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">השרת החזיר את השגיאה הבאה:
|
||||
\n%s</string>
|
||||
<string name="account_common_button_next">הבא</string>
|
||||
<string name="account_common_button_back">הקודם</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">次へ</string>
|
||||
<string name="account_common_button_back">戻る</string>
|
||||
<string name="account_common_error_server_message">サーバーから以下のメッセージが返答されました:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Uḍfir</string>
|
||||
<string name="account_common_button_back">Uɣal</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Келесі</string>
|
||||
<string name="account_common_button_back">Артқа</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">다음</string>
|
||||
<string name="account_common_button_back">이전</string>
|
||||
<string name="account_common_error_server_message">서버가 다음의 메시지를 보냈습니다:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">Serverio grąžintas pranešimas:
|
||||
\n%s</string>
|
||||
<string name="account_common_button_next">Toliau</string>
|
||||
<string name="account_common_button_back">Atgal</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Neste</string>
|
||||
<string name="account_common_button_back">Tilbake</string>
|
||||
<string name="account_common_error_server_message">Tjeneren svarte følgende:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Volgende</string>
|
||||
<string name="account_common_button_back">Terug</string>
|
||||
<string name="account_common_error_server_message">De server gaf het volgende bericht terug: \n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">Serveren returnerte følgjande melding:\n%s</string>
|
||||
<string name="account_common_button_next">Neste</string>
|
||||
<string name="account_common_button_back">Tilbake</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Dalej</string>
|
||||
<string name="account_common_button_back">Cofnij</string>
|
||||
<string name="account_common_error_server_message">Serwer zwrócił następujący komunikat:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Avançar</string>
|
||||
<string name="account_common_button_back">Voltar</string>
|
||||
<string name="account_common_error_server_message">O servidor retornou a seguinte mensagem:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Seguinte</string>
|
||||
<string name="account_common_button_back">Anterior</string>
|
||||
<string name="account_common_error_server_message">O servidor devolveu a seguinte mensagem:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Înainte</string>
|
||||
<string name="account_common_button_back">Înapoi</string>
|
||||
<string name="account_common_error_server_message">Serverul a returnat următorul mesaj:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Далее</string>
|
||||
<string name="account_common_button_back">Назад</string>
|
||||
<string name="account_common_error_server_message">Сервер вернул следующее сообщение:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Ďalej</string>
|
||||
<string name="account_common_button_back">Späť</string>
|
||||
<string name="account_common_error_server_message">Server vrátil nasledujúcu správu:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Naslednji</string>
|
||||
<string name="account_common_button_back">Prejšnji</string>
|
||||
<string name="account_common_error_server_message">Strežnik je vrnil naslednje sporočilo:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Pasuesi</string>
|
||||
<string name="account_common_button_back">Mbrapsht</string>
|
||||
<string name="account_common_error_server_message">Shërbyesi ktheu mesazhin vijues: \n%s</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_error_server_message">Сервер је вратио следећу поруку:
|
||||
\n%s</string>
|
||||
<string name="account_common_button_next">Следеће</string>
|
||||
<string name="account_common_button_back">Назад</string>
|
||||
</resources>
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="account_common_button_next">Nästa</string>
|
||||
<string name="account_common_button_back">Tillbaka</string>
|
||||
<string name="account_common_error_server_message">Servern returnerade följande meddelande:
|
||||
\n%s</string>
|
||||
</resources>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue