1
0
Fork 0
mirror of https://github.com/anyproto/anytype-kotlin.git synced 2025-06-08 05:47:05 +09:00

AA-65 | Feature/auth wired to middleware

This commit is contained in:
Evgenii Kozlov 2019-10-11 16:19:47 +00:00
parent 523b65de0b
commit a944b40ed4
45 changed files with 540 additions and 136 deletions

View file

@ -1,10 +1,7 @@
package com.agileburo.anytype
import android.app.Application
import com.agileburo.anytype.core_utils.di.ContextModule
import com.agileburo.anytype.core_utils.di.CoreComponent
import com.agileburo.anytype.core_utils.di.CoreComponentProvider
import com.agileburo.anytype.core_utils.di.DaggerCoreComponent
import com.agileburo.anytype.core_utils.di.*
import com.agileburo.anytype.core_utils.tools.CrashlyticsTree
import com.agileburo.anytype.di.app.AppModule
import com.agileburo.anytype.di.app.ApplicationComponent
@ -24,6 +21,7 @@ class AndroidApplication : Application(), CoreComponentProvider {
DaggerCoreComponent
.builder()
.contextModule(ContextModule(this))
.dataModule(DataModule())
.build()
}

View file

@ -39,13 +39,13 @@
<fragment
android:id="@+id/create"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.CreateProfileFragment"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.CreateAccountFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<fragment
android:id="@+id/choose"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.ChooseProfileFragment"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.SelectAccountFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />

View file

@ -24,7 +24,7 @@
<fragment
android:id="@+id/createProfileScreen"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.CreateProfileFragment"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.CreateAccountFragment"
android:label="StartLoginFragment"
tools:layout="@layout/fragment_create_profile" />
@ -54,7 +54,7 @@
<fragment
android:id="@+id/chooseProfileScreen"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.ChooseProfileFragment"
android:name="com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.SelectAccountFragment"
android:label="ChooseProfileScreen"
tools:layout="@layout/fragment_choose_profile" />

View file

@ -1,11 +1,18 @@
package com.agileburo.anytype.core_utils.di
import android.content.Context
import android.content.SharedPreferences
import dagger.Component
import javax.inject.Singleton
@Singleton
@Component(modules = [ContextModule::class])
@Component(
modules = [
ContextModule::class,
DataModule::class
]
)
interface CoreComponent {
fun context(): Context
fun prefs(): SharedPreferences
}

View file

@ -0,0 +1,18 @@
package com.agileburo.anytype.core_utils.di
import android.content.Context
import android.content.SharedPreferences
import dagger.Module
import dagger.Provides
import javax.inject.Singleton
@Module
class DataModule {
@Provides
@Singleton
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
}
}

View file

@ -18,6 +18,7 @@ android {
dependencies {
implementation project(':core_utils')
implementation project(':middleware')
def applicationDependencies = rootProject.ext.mainApplication
def acceptanceTesting = rootProject.ext.acceptanceTesting

View file

@ -7,7 +7,7 @@ class AuthCacheDataStore(
override suspend fun isSignedIn() = authCache.isSignedIn()
override suspend fun saveMnemonic(mnemonic: String) {
return authCache.saveMnemonic(mnemonic)
authCache.saveMnemonic(mnemonic)
}
override suspend fun getMnemonic() = authCache.getMnemonic()

View file

@ -1,16 +1,23 @@
package com.agileburo.anytype.feature_login.ui.login.data
class AuthCacheImpl : AuthCache {
import android.content.SharedPreferences
class AuthCacheImpl(
private val prefs: SharedPreferences
) : AuthCache {
companion object {
const val MNEMONIC_KEY = "mnemonic"
}
override suspend fun isSignedIn(): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override suspend fun saveMnemonic(mnemonic: String) {
// TODO
prefs.edit().putString(MNEMONIC_KEY, mnemonic).apply()
}
override suspend fun getMnemonic(): String {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
override suspend fun getMnemonic() = prefs.getString(MNEMONIC_KEY, null)
?: throw IllegalStateException("Mnemonic is missing.")
}

View file

@ -2,27 +2,28 @@ package com.agileburo.anytype.feature_login.ui.login.data
import com.agileburo.anytype.feature_login.ui.login.domain.model.Wallet
import com.agileburo.anytype.feature_login.ui.login.domain.repository.AuthRepository
import kotlinx.coroutines.delay
import com.agileburo.anytype.middleware.interactor.Middleware
import timber.log.Timber
class AuthDataRepository(
private val cache: AuthCacheDataStore
private val cache: AuthCacheDataStore,
private val middleware: Middleware
) : AuthRepository {
override suspend fun isSignedIn() = cache.isSignedIn()
override suspend fun saveMnemonic(mnemonic: String) {
Timber.d("Saving mnemonic: $mnemonic")
cache.saveMnemonic(mnemonic)
}
override suspend fun getMnemonic() = cache.getMnemonic()
override suspend fun createWallet(path: String): Wallet {
return Wallet(mnemonic = "123467890")
return Wallet(mnemonic = middleware.createWallet(path).mnemonic)
}
override suspend fun recoverWallet(path: String, mnemonic: String) {
delay(2000)
if (mnemonic != "mock")
throw IllegalStateException("Invalid mnemonic phrase")
middleware.recoverWallet(path, mnemonic)
}
}

View file

@ -1,24 +1,61 @@
package com.agileburo.anytype.feature_login.ui.login.data
import com.agileburo.anytype.feature_login.ui.login.domain.model.Account
import com.agileburo.anytype.feature_login.ui.login.domain.model.Image
import com.agileburo.anytype.feature_login.ui.login.domain.repository.UserRepository
import kotlinx.coroutines.delay
import com.agileburo.anytype.middleware.Event
import com.agileburo.anytype.middleware.EventProxy
import com.agileburo.anytype.middleware.interactor.Middleware
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import timber.log.Timber
class UserDataRepository : UserRepository {
class UserDataRepository(
private val middleware: Middleware,
private val proxy: EventProxy
) : UserRepository {
override suspend fun getAccounts(): List<Account> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
override fun observeAccounts(): Flow<Account> {
return proxy
.flow()
.onEach { event ->
Timber.d("Event : $event")
}
.map { event ->
if (event is Event.AccountAdd) {
Account(
id = event.id,
name = event.name,
image = Image(
id = "Id",
size = Image.ImageSize.SMALL
)
)
} else
TODO()
}
}
override suspend fun createAccount(name: String) {
delay(3000)
middleware.createAccount(name)
}
override suspend fun getCurrentAccount(): Account {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
override suspend fun recoverAccount() {
middleware.recoverAccount()
}
override suspend fun selectAccount(id: String) {
delay(2000)
override suspend fun selectAccount(id: String, path: String): Account {
return middleware.selectAccount(id, path).let { response ->
Account(
id = response.id,
name = response.name
)
}
}
override suspend fun saveAccount(account: Account) {
// TODO
}
}

View file

@ -2,7 +2,7 @@ package com.agileburo.anytype.feature_login.ui.login.di
import com.agileburo.anytype.core_utils.di.scope.PerScreen
import com.agileburo.anytype.feature_login.ui.login.domain.common.Session
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateProfileViewModelFactory
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateAccountViewModelFactory
import dagger.Module
import dagger.Provides
@ -13,8 +13,8 @@ class CreateProfileModule {
@Provides
fun provideCreateProfileViewModelFactory(
session: Session
): CreateProfileViewModelFactory {
return CreateProfileViewModelFactory(
): CreateAccountViewModelFactory {
return CreateAccountViewModelFactory(
session = session
)
}

View file

@ -3,7 +3,7 @@ package com.agileburo.anytype.feature_login.ui.login.di
import com.agileburo.anytype.core_utils.common.ParametrizedProvider
import com.agileburo.anytype.core_utils.di.CoreComponent
import com.agileburo.anytype.core_utils.di.scope.PerScreen
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.CreateProfileFragment
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.CreateAccountFragment
import dagger.Subcomponent
@Subcomponent(modules = [CreateProfileModule::class])
@ -14,6 +14,6 @@ abstract class CreateProfileSubComponent {
override fun create(param: CoreComponent) = LoginFeatureComponent.get(param).plus(CreateProfileModule())
}
abstract fun inject(fragment: CreateProfileFragment)
abstract fun inject(fragment: CreateAccountFragment)
}

View file

@ -1,6 +1,7 @@
package com.agileburo.anytype.feature_login.ui.login.di
import android.content.Context
import android.content.SharedPreferences
import com.agileburo.anytype.core_utils.di.scope.PerFeature
import com.agileburo.anytype.feature_login.ui.login.data.AuthCacheDataStore
import com.agileburo.anytype.feature_login.ui.login.data.AuthCacheImpl
@ -11,6 +12,9 @@ import com.agileburo.anytype.feature_login.ui.login.domain.common.PathProvider
import com.agileburo.anytype.feature_login.ui.login.domain.common.Session
import com.agileburo.anytype.feature_login.ui.login.domain.repository.AuthRepository
import com.agileburo.anytype.feature_login.ui.login.domain.repository.UserRepository
import com.agileburo.anytype.middleware.EventProxy
import com.agileburo.anytype.middleware.interactor.Handler
import com.agileburo.anytype.middleware.interactor.Middleware
import dagger.Module
import dagger.Provides
@ -19,18 +23,28 @@ class LoginFeatureModule {
@PerFeature
@Provides
fun provideAuthRepository(): AuthRepository {
fun provideAuthRepository(
prefs: SharedPreferences,
middleware: Middleware
): AuthRepository {
return AuthDataRepository(
cache = AuthCacheDataStore(
authCache = AuthCacheImpl()
)
authCache = AuthCacheImpl(prefs)
),
middleware = middleware
)
}
@PerFeature
@Provides
fun provideUserRepository(): UserRepository {
return UserDataRepository()
fun provideUserRepository(
middleware: Middleware,
proxy: EventProxy
): UserRepository {
return UserDataRepository(
middleware = middleware,
proxy = proxy
)
}
@PerFeature
@ -45,4 +59,16 @@ class LoginFeatureModule {
return DefaultPathProvider(context)
}
@PerFeature
@Provides
fun provideMiddleware(): Middleware {
return Middleware()
}
@PerFeature
@Provides
fun provideEventProxy(): EventProxy {
return Handler()
}
}

View file

@ -1,7 +1,10 @@
package com.agileburo.anytype.feature_login.ui.login.di
import com.agileburo.anytype.core_utils.di.scope.PerScreen
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.ChooseProfileViewModelFactory
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.ObserveAccounts
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.RecoverAccount
import com.agileburo.anytype.feature_login.ui.login.domain.repository.UserRepository
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.SelectAccountViewModelFactory
import dagger.Module
import dagger.Provides
@ -10,8 +13,32 @@ class SelectProfileModule {
@PerScreen
@Provides
fun provideSelectProfileViewModelFactory(): ChooseProfileViewModelFactory {
return ChooseProfileViewModelFactory()
fun provideSelectProfileViewModelFactory(
recoverAccount: RecoverAccount,
observeAccounts: ObserveAccounts
): SelectAccountViewModelFactory {
return SelectAccountViewModelFactory(
recoverAccount = recoverAccount,
observeAccounts = observeAccounts
)
}
@PerScreen
@Provides
fun provideRecoverAccountUseCase(repository: UserRepository): RecoverAccount {
return RecoverAccount(
repository = repository
)
}
@PerScreen
@Provides
fun provideObserveAccountsUseCase(
repository: UserRepository
): ObserveAccounts {
return ObserveAccounts(
repository = repository
)
}
}

View file

@ -3,7 +3,7 @@ package com.agileburo.anytype.feature_login.ui.login.di
import com.agileburo.anytype.core_utils.common.ParametrizedProvider
import com.agileburo.anytype.core_utils.di.CoreComponent
import com.agileburo.anytype.core_utils.di.scope.PerScreen
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.ChooseProfileFragment
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.SelectAccountFragment
import dagger.Subcomponent
@Subcomponent(modules = [SelectProfileModule::class])
@ -13,5 +13,5 @@ abstract class SelectProfileSubComponent {
override fun create(param: CoreComponent) = LoginFeatureComponent.get(param).plus(SelectProfileModule())
}
abstract fun inject(fragment: ChooseProfileFragment)
abstract fun inject(fragment: SelectAccountFragment)
}

View file

@ -1,6 +1,7 @@
package com.agileburo.anytype.feature_login.ui.login.di
import com.agileburo.anytype.core_utils.di.scope.PerScreen
import com.agileburo.anytype.feature_login.ui.login.domain.common.PathProvider
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.SelectAccount
import com.agileburo.anytype.feature_login.ui.login.domain.repository.UserRepository
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.setup.SetupSelectedAccountViewModelFactory
@ -13,10 +14,12 @@ class SetupSelectedAccountModule {
@Provides
@PerScreen
fun provideSetupSelectedAccountViewModelFactory(
selectAccount: SelectAccount
selectAccount: SelectAccount,
pathProvider: PathProvider
): SetupSelectedAccountViewModelFactory {
return SetupSelectedAccountViewModelFactory(
selectAccount = selectAccount
selectAccount = selectAccount,
pathProvider = pathProvider
)
}

View file

@ -1,32 +1,13 @@
package com.agileburo.anytype.feature_login.ui.login.domain.interactor
import com.agileburo.anytype.feature_login.ui.login.domain.common.FlowUseCase
import com.agileburo.anytype.feature_login.ui.login.domain.model.Account
import com.agileburo.anytype.feature_login.ui.login.domain.model.Image
import kotlinx.coroutines.delay
import com.agileburo.anytype.feature_login.ui.login.domain.repository.UserRepository
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
class ObserveAccounts {
fun observe(): Flow<List<Account>> = flow {
delay(1000)
val first = Account(
id = "1",
name = "Ubu",
image = Image(
id = "1",
size = Image.ImageSize.SMALL
)
)
emit(listOf(first))
delay(1000)
val second = Account(
id = "1",
name = "Evgenii",
image = Image(
id = "1",
size = Image.ImageSize.SMALL
)
)
emit(listOf(first, second))
}
class ObserveAccounts(
private val repository: UserRepository
) : FlowUseCase<Account, Unit>() {
override fun stream(params: Unit): Flow<Account> = repository.observeAccounts()
}

View file

@ -0,0 +1,20 @@
package com.agileburo.anytype.feature_login.ui.login.domain.interactor
import com.agileburo.anytype.core_utils.common.Either
import com.agileburo.anytype.feature_login.ui.login.domain.common.BaseUseCase
import com.agileburo.anytype.feature_login.ui.login.domain.repository.UserRepository
class RecoverAccount(
private val repository: UserRepository
) : BaseUseCase<Unit, RecoverAccount.Params>() {
override suspend fun run(params: Params) = try {
repository.recoverAccount().let {
Either.Right(it)
}
} catch (e: Throwable) {
Either.Left(e)
}
class Params
}

View file

@ -10,13 +10,19 @@ class SelectAccount(
override suspend fun run(params: Params) = try {
userRepository.selectAccount(
id = params.id
).let {
id = params.id,
path = params.path
).let { account ->
userRepository.saveAccount(account)
}.let {
Either.Right(it)
}
} catch (e: Throwable) {
Either.Left(e)
}
class Params(val id: String)
class Params(
val id: String,
val path: String
)
}

View file

@ -3,5 +3,5 @@ package com.agileburo.anytype.feature_login.ui.login.domain.model
data class Account(
val id: String,
val name: String,
val image: Image
val image: Image? = null
)

View file

@ -1,10 +1,12 @@
package com.agileburo.anytype.feature_login.ui.login.domain.repository
import com.agileburo.anytype.feature_login.ui.login.domain.model.Account
import kotlinx.coroutines.flow.Flow
interface UserRepository {
suspend fun selectAccount(id: String)
suspend fun getCurrentAccount(): Account
suspend fun getAccounts(): List<Account>
suspend fun selectAccount(id: String, path: String): Account
suspend fun createAccount(name: String)
suspend fun recoverAccount()
suspend fun saveAccount(account: Account)
fun observeAccounts(): Flow<Account>
}

View file

@ -47,7 +47,7 @@ class KeychainLoginViewModel(
fnR = { proceedWithSavingMnemonic(chain) },
fnL = {
state.postValue(ViewState.Error(it.localizedMessage))
Timber.e(it, "Invalid mnemonic phrase")
Timber.e(it, "Error while recovering wallet with the following mnemonic: $chain")
}
)
}
@ -62,7 +62,7 @@ class KeychainLoginViewModel(
) { result ->
result.either(
fnR = { navigation.accept(NavigationCommand.ChooseProfileScreen) },
fnL = { Timber.e(it, "Error while saving mnemonic") }
fnL = { Timber.e(it, "Error while saving mnemonic: $mnemonic") }
)
}
}

View file

@ -1,8 +1,8 @@
package com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.common.ViewType
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.ChooseProfileAdapter.Companion.ADD_NEW_PROFILE
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.ChooseProfileAdapter.Companion.PROFILE
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.SelectAccountAdapter.Companion.ADD_NEW_PROFILE
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.profile.SelectAccountAdapter.Companion.PROFILE
sealed class ChooseProfileView : ViewType {

View file

@ -1,12 +0,0 @@
package com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class ChooseProfileViewModelFactory : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return ChooseProfileViewModel() as T
}
}

View file

@ -7,7 +7,7 @@ import com.agileburo.anytype.feature_login.ui.login.presentation.navigation.Supp
import com.jakewharton.rxrelay2.PublishRelay
import io.reactivex.Observable
class CreateProfileViewModel(
class CreateAccountViewModel(
val session: Session
) : ViewModel(), SupportNavigation {

View file

@ -4,13 +4,13 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.agileburo.anytype.feature_login.ui.login.domain.common.Session
class CreateProfileViewModelFactory(
class CreateAccountViewModelFactory(
private val session: Session
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return CreateProfileViewModel(
return CreateAccountViewModel(
session = session
) as T
}

View file

@ -3,15 +3,19 @@ package com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.ObserveAccounts
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.RecoverAccount
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.common.BaseViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.navigation.NavigationCommand
import com.agileburo.anytype.feature_login.ui.login.presentation.navigation.SupportNavigation
import com.jakewharton.rxrelay2.PublishRelay
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import timber.log.Timber
class ChooseProfileViewModel : BaseViewModel(), SupportNavigation {
class SelectAccountViewModel(
private val recoverAccount: RecoverAccount,
private val observeAccounts: ObserveAccounts
) : BaseViewModel(), SupportNavigation {
val state by lazy {
MutableLiveData<List<ChooseProfileView>>()
@ -21,21 +25,31 @@ class ChooseProfileViewModel : BaseViewModel(), SupportNavigation {
PublishRelay.create<NavigationCommand>().toSerialized()
}
private val observeAccounts = ObserveAccounts()
init {
startObservingAccounts()
startRecoveringAccounts()
}
private fun startRecoveringAccounts() {
recoverAccount.invoke(viewModelScope, RecoverAccount.Params()) {
Timber.d(it.toString())
}
}
private fun startObservingAccounts() {
viewModelScope.launch {
observeAccounts
.observe()
.map { profiles ->
profiles.map { profile ->
ChooseProfileView.ProfileView(
id = profile.id,
name = profile.name
.stream(Unit)
.collect { account ->
state.postValue(
listOf(
ChooseProfileView.ProfileView(
id = account.id,
name = account.name
)
)
}
)
}
.collect { state.postValue(it) }
}
}

View file

@ -0,0 +1,20 @@
package com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.ObserveAccounts
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.RecoverAccount
class SelectAccountViewModelFactory(
private val recoverAccount: RecoverAccount,
private val observeAccounts: ObserveAccounts
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return SelectAccountViewModel(
recoverAccount = recoverAccount,
observeAccounts = observeAccounts
) as T
}
}

View file

@ -2,6 +2,7 @@ package com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.setup
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.agileburo.anytype.feature_login.ui.login.domain.common.PathProvider
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.SelectAccount
import com.agileburo.anytype.feature_login.ui.login.presentation.navigation.NavigationCommand
import com.agileburo.anytype.feature_login.ui.login.presentation.navigation.SupportNavigation
@ -9,7 +10,8 @@ import com.jakewharton.rxrelay2.PublishRelay
import timber.log.Timber
class SetupSelectedAccountViewModel(
private val selectAccount: SelectAccount
private val selectAccount: SelectAccount,
private val pathProvider: PathProvider
) : ViewModel(), SupportNavigation {
private val navigation by lazy {
@ -22,7 +24,8 @@ class SetupSelectedAccountViewModel(
selectAccount.invoke(
scope = viewModelScope,
params = SelectAccount.Params(
id = id
id = id,
path = pathProvider.providePath()
)
) { result ->
result.either(
@ -31,5 +34,4 @@ class SetupSelectedAccountViewModel(
)
}
}
}

View file

@ -2,16 +2,19 @@ package com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.setup
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.agileburo.anytype.feature_login.ui.login.domain.common.PathProvider
import com.agileburo.anytype.feature_login.ui.login.domain.interactor.SelectAccount
class SetupSelectedAccountViewModelFactory(
private val selectAccount: SelectAccount
private val selectAccount: SelectAccount,
private val pathProvider: PathProvider
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return SetupSelectedAccountViewModel(
selectAccount = selectAccount
selectAccount = selectAccount,
pathProvider = pathProvider
) as T
}
}

View file

@ -10,21 +10,21 @@ import com.agileburo.anytype.core_utils.ext.disposedBy
import com.agileburo.anytype.core_utils.ext.hideKeyboard
import com.agileburo.anytype.feature_login.R
import com.agileburo.anytype.feature_login.ui.login.di.CreateProfileSubComponent
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateProfileViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateProfileViewModelFactory
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateAccountViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateAccountViewModelFactory
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.common.BaseFragment
import kotlinx.android.synthetic.main.fragment_create_profile.*
import javax.inject.Inject
class CreateProfileFragment : BaseFragment() {
class CreateAccountFragment : BaseFragment() {
@Inject
lateinit var factory: CreateProfileViewModelFactory
lateinit var factory: CreateAccountViewModelFactory
private val vm by lazy {
ViewModelProviders
.of(this, factory)
.get(CreateProfileViewModel::class.java)
.get(CreateAccountViewModel::class.java)
}
override fun onCreateView(

View file

@ -8,11 +8,11 @@ import com.agileburo.anytype.feature_login.R
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.ChooseProfileView
import kotlinx.android.synthetic.main.item_choose_profile_profile.view.*
class ChooseProfileAdapter(
class SelectAccountAdapter(
private val views: MutableList<ChooseProfileView>,
private val onAddNewProfileClicked: () -> Unit,
private val onProfileClicked: (ChooseProfileView.ProfileView) -> Unit
) : RecyclerView.Adapter<ChooseProfileAdapter.ViewHolder>() {
) : RecyclerView.Adapter<SelectAccountAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {

View file

@ -12,26 +12,26 @@ import com.agileburo.anytype.core_utils.ext.dimen
import com.agileburo.anytype.core_utils.ext.disposedBy
import com.agileburo.anytype.feature_login.R
import com.agileburo.anytype.feature_login.ui.login.di.SelectProfileSubComponent
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.ChooseProfileViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.ChooseProfileViewModelFactory
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.SelectAccountViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.SelectAccountViewModelFactory
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.common.BaseFragment
import com.agileburo.anytype.feature_login.ui.login.presentation.ui.common.SpacingItemDecoration
import kotlinx.android.synthetic.main.fragment_choose_profile.*
import javax.inject.Inject
class ChooseProfileFragment : BaseFragment() {
class SelectAccountFragment : BaseFragment() {
@Inject
lateinit var factory: ChooseProfileViewModelFactory
lateinit var factory: SelectAccountViewModelFactory
private val vm by lazy {
ViewModelProviders
.of(this)
.get(ChooseProfileViewModel::class.java)
.of(this, factory)
.get(SelectAccountViewModel::class.java)
}
private val profileAdapter by lazy {
ChooseProfileAdapter(
SelectAccountAdapter(
views = mutableListOf(),
onAddNewProfileClicked = { vm.onAddProfileClicked() },
onProfileClicked = { vm.onProfileClicked(it.id) }

View file

@ -2,7 +2,7 @@ package com.agileburo.anytype.feature_login.presentation
import com.agileburo.anytype.feature_login.common.DataFactory
import com.agileburo.anytype.feature_login.ui.login.domain.common.Session
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateProfileViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.mvvm.profile.CreateAccountViewModel
import com.agileburo.anytype.feature_login.ui.login.presentation.navigation.NavigationCommand
import org.junit.Before
import org.junit.Test
@ -10,13 +10,13 @@ import kotlin.test.assertNotEquals
class CreateProfileViewModelTest {
lateinit var vm: CreateProfileViewModel
lateinit var vm: CreateAccountViewModel
private val session = Session()
@Before
fun setup() {
vm = CreateProfileViewModel(session)
vm = CreateAccountViewModel(session)
}
@Test

Binary file not shown.

View file

@ -29,9 +29,13 @@ dependencies {
def applicationDependencies = rootProject.ext.mainApplication
def unitTestDependencies = rootProject.ext.unitTesting
def protobufDependencies = rootProject.ext.protobuf
implementation applicationDependencies.kotlin
implementation applicationDependencies.timber
implementation applicationDependencies.coroutines
implementation protobufDependencies.protobufJava
testImplementation unitTestDependencies.junit
testImplementation unitTestDependencies.kotlinTest

View file

@ -0,0 +1,26 @@
package com.agileburo.anytype;
import android.content.Context;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.agileburo.anytype.test", appContext.getPackageName());
}
}

View file

@ -0,0 +1,11 @@
package com.agileburo.anytype.middleware
sealed class Event {
class AccountAdd(
val index: Int,
val id: String,
val name: String
) : Event()
}

View file

@ -0,0 +1,7 @@
package com.agileburo.anytype.middleware
import kotlinx.coroutines.flow.Flow
interface EventProxy {
fun flow(): Flow<Event>
}

View file

@ -0,0 +1,49 @@
package com.agileburo.anytype.middleware.interactor
import anytype.Events
import com.agileburo.anytype.middleware.Event
import com.agileburo.anytype.middleware.EventProxy
import com.google.protobuf.InvalidProtocolBufferException
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import lib.Lib
import timber.log.Timber
class Handler : EventProxy {
init {
Timber.d("Subscribing to events")
Lib.setEventHandlerMobile { bytes ->
handle(bytes)
}
}
private val channel = Channel<Events.Event>()
private fun handle(bytes: ByteArray) {
Timber.d("New event to handle.")
try {
Events.Event.parseFrom(bytes).let {
channel.offer(it)
}
} catch (e: InvalidProtocolBufferException) {
Timber.e(e, "Error while deserialize message")
}
}
private fun events(): Flow<Event> = channel
.consumeAsFlow()
.onEach { Timber.d(it.toString()) }
.map { event ->
Event.AccountAdd(
id = event.accountAdd.account.id,
name = event.accountAdd.account.name,
index = event.accountAdd.index.toInt()
)
}
override fun flow(): Flow<Event> = events()
}

View file

@ -0,0 +1,117 @@
package com.agileburo.anytype.middleware.interactor;
import anytype.Commands.*;
import com.agileburo.anytype.middleware.model.CreateWalletResponse;
import com.agileburo.anytype.middleware.model.SelectAccountResponse;
import lib.Lib;
public class Middleware {
public CreateWalletResponse createWallet(String path) throws Exception {
WalletCreateRequest request = WalletCreateRequest
.newBuilder()
.setRootPath(path)
.build();
byte[] encodedResponse = Lib.walletCreate(request.toByteArray());
WalletCreateResponse response = WalletCreateResponse.parseFrom(encodedResponse);
if (response.getError() != null && response.getError().getCode() != WalletCreateResponse.Error.Code.NULL) {
throw new Exception(response.getError().getDescription());
} else {
return new CreateWalletResponse(response.getMnemonic());
}
}
public void createAccount(String name) throws Exception {
AccountCreateRequest request = AccountCreateRequest
.newBuilder()
.setUsername(name)
.build();
byte[] encodedRequest = request.toByteArray();
byte[] encodedResponse = Lib.accountCreate(encodedRequest);
// TODO remove.
if (encodedResponse == null)
return;
AccountCreateResponse response = AccountCreateResponse.parseFrom(encodedResponse);
if (response.getError() != null && response.getError().getCode() != AccountCreateResponse.Error.Code.NULL) {
throw new Exception(response.getError().getDescription());
}
}
public void recoverWallet(String path, String mnemonic) throws Exception {
WalletRecoverRequest request = WalletRecoverRequest
.newBuilder()
.setMnemonic(mnemonic)
.setRootPath(path)
.build();
byte[] encodedRequest = request.toByteArray();
byte[] encodedResponse = Lib.walletRecover(encodedRequest);
// TODO remove.
if (encodedResponse == null)
return;
WalletRecoverResponse response = WalletRecoverResponse.parseFrom(encodedResponse);
if (response.getError() != null && response.getError().getCode() != WalletRecoverResponse.Error.Code.NULL) {
throw new Exception(response.getError().getDescription());
}
}
public void recoverAccount() throws Exception {
AccountRecoverRequest request = AccountRecoverRequest
.newBuilder()
.build();
byte[] encodedRequest = request.toByteArray();
byte[] encodedResponse = Lib.accountRecover(encodedRequest);
// TODO remove.
if (encodedResponse == null)
return;
AccountRecoverResponse response = AccountRecoverResponse.parseFrom(encodedResponse);
if (response.getError() != null && response.getError().getCode() != AccountRecoverResponse.Error.Code.NULL) {
throw new Exception(response.getError().getDescription());
}
}
public SelectAccountResponse selectAccount(String id, String path) throws Exception {
AccountSelectRequest request = AccountSelectRequest
.newBuilder()
.setId(id)
.setRootPath(path)
.build();
byte[] encodedRequest = request.toByteArray();
byte[] encodedResponse = Lib.accountSelect(encodedRequest);
AccountSelectResponse response = AccountSelectResponse.parseFrom(encodedResponse);
if (response.getError() != null && response.getError().getCode() != AccountSelectResponse.Error.Code.NULL) {
throw new Exception(response.getError().getDescription());
} else {
return new SelectAccountResponse(
response.getAccount().getId(),
response.getAccount().getName()
);
}
}
}

View file

@ -0,0 +1,5 @@
package com.agileburo.anytype.middleware.model
data class CreateWalletResponse(
val mnemonic: String
)

View file

@ -0,0 +1,6 @@
package com.agileburo.anytype.middleware.model
class SelectAccountResponse(
val id: String,
val name: String
)

View file

@ -0,0 +1,17 @@
package com.agileburo.anytype;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
@Test
public void addition_isCorrect() {
assertEquals(4, 2 + 2);
}
}

View file

@ -7,6 +7,7 @@ import "models.proto";
service ClientCommands {
rpc WalletCreate (WalletCreateRequest) returns (WalletCreateResponse);
rpc WalletRecover (WalletRecoverRequest) returns (WalletRecoverResponse);
rpc AccountRecover (AccountRecoverRequest) returns (AccountRecoverResponse);
rpc AccountCreate (AccountCreateRequest) returns (AccountCreateResponse);
rpc AccountSelect (AccountSelectRequest) returns (AccountSelectResponse);
rpc ImageGetBlob (ImageGetBlobRequest) returns (ImageGetBlobResponse);
@ -182,7 +183,7 @@ message IpfsGetFileResponse {
// ...
NOT_FOUND = 101;
TIMOUT = 102;
TIMEOUT = 102;
}
}
}
@ -209,7 +210,7 @@ message IpfsGetDataResponse {
// ...
NOT_FOUND = 101;
TIMOUT = 102;
TIMEOUT = 102;
}
}
}
@ -234,7 +235,7 @@ message ImageGetBlobResponse {
// ...
NOT_FOUND = 101;
TIMOUT = 102;
TIMEOUT = 102;
}
}
}