mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
DROID-1602 Self-hosting | Auth (#659)
Co-authored-by: Evgenii Kozlov <ubuphobos@gmail.com>
This commit is contained in:
parent
cf1731b217
commit
f0b542261d
48 changed files with 1196 additions and 223 deletions
|
@ -11,6 +11,7 @@ import com.anytypeio.anytype.di.feature.AddObjectRelationValueModule
|
|||
import com.anytypeio.anytype.di.feature.CreateBookmarkModule
|
||||
import com.anytypeio.anytype.di.feature.CreateDataViewViewerModule
|
||||
import com.anytypeio.anytype.di.feature.CreateObjectModule
|
||||
import com.anytypeio.anytype.di.feature.DaggerAppPreferencesComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerBacklinkOrAddToObjectComponent
|
||||
import com.anytypeio.anytype.di.feature.DaggerSplashComponent
|
||||
import com.anytypeio.anytype.di.feature.DataViewRelationValueModule
|
||||
|
@ -934,6 +935,12 @@ class ComponentManager(
|
|||
.create(findComponentDependencies())
|
||||
}
|
||||
|
||||
val appPreferencesComponent = Component {
|
||||
DaggerAppPreferencesComponent
|
||||
.factory()
|
||||
.create(findComponentDependencies())
|
||||
}
|
||||
|
||||
|
||||
class Component<T>(private val builder: () -> T) {
|
||||
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.networkmode.GetNetworkMode
|
||||
import com.anytypeio.anytype.domain.networkmode.SetNetworkMode
|
||||
import com.anytypeio.anytype.presentation.settings.PreferencesViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.NetworkModeCopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.ui.settings.system.PreferenceFragment
|
||||
import dagger.Component
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
||||
@Component(
|
||||
dependencies = [AppPreferencesDependencies::class],
|
||||
modules = [AppPreferencesModule::class]
|
||||
)
|
||||
@PerScreen
|
||||
interface AppPreferencesComponent {
|
||||
|
||||
@Component.Factory
|
||||
interface Factory {
|
||||
fun create(dependency: AppPreferencesDependencies): AppPreferencesComponent
|
||||
}
|
||||
|
||||
fun inject(fragment: PreferenceFragment)
|
||||
}
|
||||
|
||||
@Module
|
||||
object AppPreferencesModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideCopyFileToCache(
|
||||
context: Context
|
||||
): CopyFileToCacheDirectory = NetworkModeCopyFileToCacheDirectory(context)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideViewModelFactory(
|
||||
copyFileToCacheDirectory: CopyFileToCacheDirectory,
|
||||
getNetworkMode: GetNetworkMode,
|
||||
setNetworkMode: SetNetworkMode
|
||||
): PreferencesViewModel.Factory = PreferencesViewModel.Factory(
|
||||
copyFileToCacheDirectory = copyFileToCacheDirectory,
|
||||
getNetworkMode = getNetworkMode,
|
||||
setNetworkMode = setNetworkMode
|
||||
)
|
||||
}
|
||||
|
||||
interface AppPreferencesDependencies : ComponentDependencies {
|
||||
fun context(): Context
|
||||
fun dispatchers(): AppCoroutineDispatchers
|
||||
fun authRepository(): AuthRepository
|
||||
}
|
|
@ -59,11 +59,13 @@ object OnboardingSoulCreationModule {
|
|||
fun provideCreateAccountUseCase(
|
||||
authRepository: AuthRepository,
|
||||
configStorage: ConfigStorage,
|
||||
metricsProvider: MetricsProvider
|
||||
metricsProvider: MetricsProvider,
|
||||
dispatchers: AppCoroutineDispatchers
|
||||
): CreateAccount = CreateAccount(
|
||||
repository = authRepository,
|
||||
configStorage = configStorage,
|
||||
metricsProvider = metricsProvider
|
||||
metricsProvider = metricsProvider,
|
||||
dispatcher = dispatchers
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
|
|
@ -51,6 +51,7 @@ import com.anytypeio.anytype.middleware.interactor.ProtobufConverterProvider
|
|||
import com.anytypeio.anytype.middleware.service.MiddlewareService
|
||||
import com.anytypeio.anytype.middleware.service.MiddlewareServiceImplementation
|
||||
import com.anytypeio.anytype.persistence.db.AnytypeDatabase
|
||||
import com.anytypeio.anytype.persistence.networkmode.NetworkModeProvider
|
||||
import com.anytypeio.anytype.persistence.repo.DefaultAuthCache
|
||||
import com.anytypeio.anytype.persistence.repo.DefaultDebugSettingsCache
|
||||
import com.anytypeio.anytype.persistence.repo.DefaultUserSettingsCache
|
||||
|
@ -113,12 +114,14 @@ object DataModule {
|
|||
fun provideAuthCache(
|
||||
db: AnytypeDatabase,
|
||||
@Named("default") defaultPrefs: SharedPreferences,
|
||||
@Named("encrypted") encryptedPrefs: SharedPreferences
|
||||
@Named("encrypted") encryptedPrefs: SharedPreferences,
|
||||
networkModeProvider: NetworkModeProvider
|
||||
): AuthCache {
|
||||
return DefaultAuthCache(
|
||||
db = db,
|
||||
defaultPrefs = defaultPrefs,
|
||||
encryptedPrefs = encryptedPrefs
|
||||
encryptedPrefs = encryptedPrefs,
|
||||
networkModeProvider = networkModeProvider
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.di.main
|
|||
import com.anytypeio.anytype.app.AndroidApplication
|
||||
import com.anytypeio.anytype.di.common.ComponentDependencies
|
||||
import com.anytypeio.anytype.di.common.ComponentDependenciesKey
|
||||
import com.anytypeio.anytype.di.feature.AppPreferencesDependencies
|
||||
import com.anytypeio.anytype.di.feature.BacklinkOrAddToObjectDependencies
|
||||
import com.anytypeio.anytype.di.feature.CreateBookmarkSubComponent
|
||||
import com.anytypeio.anytype.di.feature.CreateObjectSubComponent
|
||||
|
@ -70,7 +71,8 @@ import javax.inject.Singleton
|
|||
LocalNetworkProviderModule::class,
|
||||
SubscriptionsModule::class,
|
||||
CrashReportingModule::class,
|
||||
TemplatesModule::class
|
||||
TemplatesModule::class,
|
||||
NetworkModeModule::class
|
||||
]
|
||||
)
|
||||
interface MainComponent :
|
||||
|
@ -100,7 +102,8 @@ interface MainComponent :
|
|||
CreateSpaceDependencies,
|
||||
SpaceSettingsDependencies,
|
||||
CreateObjectOfTypeDependencies,
|
||||
SpacesStorageDependencies
|
||||
SpacesStorageDependencies,
|
||||
AppPreferencesDependencies
|
||||
{
|
||||
|
||||
fun inject(app: AndroidApplication)
|
||||
|
@ -267,4 +270,9 @@ private abstract class ComponentDependenciesModule private constructor() {
|
|||
@IntoMap
|
||||
@ComponentDependenciesKey(SpacesStorageDependencies::class)
|
||||
abstract fun provideSpacesStorageDependencies(component: MainComponent): ComponentDependencies
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ComponentDependenciesKey(AppPreferencesDependencies::class)
|
||||
abstract fun providePreferencesDependencies(component: MainComponent): ComponentDependencies
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package com.anytypeio.anytype.di.main
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.anytypeio.anytype.persistence.networkmode.DefaultNetworkModeProvider
|
||||
import com.anytypeio.anytype.persistence.networkmode.DefaultNetworkModeProvider.NetworkModeConstants.NAMED_NETWORK_MODE_PREFS
|
||||
import com.anytypeio.anytype.persistence.networkmode.NetworkModeProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
object NetworkModeModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named(NAMED_NETWORK_MODE_PREFS)
|
||||
fun provideNetworkModeSharedPreferences(
|
||||
context: Context
|
||||
): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provider(
|
||||
@Named(NAMED_NETWORK_MODE_PREFS) sharedPreferences: SharedPreferences
|
||||
): NetworkModeProvider = DefaultNetworkModeProvider(sharedPreferences)
|
||||
}
|
|
@ -41,7 +41,7 @@ interface PickerDelegate : PickiTCallbacks {
|
|||
fun clearOnCopyFile()
|
||||
|
||||
sealed class Actions {
|
||||
data class OnStartCopyFileToCacheDir(val uri: Uri) : Actions()
|
||||
data class OnStartCopyFileToCacheDir(val uri: Uri, val path: String? = null) : Actions()
|
||||
object OnCancelCopyFileToCacheDir : Actions()
|
||||
data class OnProceedWithFilePath(val filePath: String) : Actions()
|
||||
data class OnPickedDocImageFromDevice(val ctx: String, val filePath: String) : Actions()
|
||||
|
@ -84,6 +84,14 @@ interface PickerDelegate : PickiTCallbacks {
|
|||
pickiT.getPath(uri, Build.VERSION.SDK_INT)
|
||||
}
|
||||
}
|
||||
FileConstants.REQUEST_NETWORK_MODE_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
actions(Actions.OnStartCopyFileToCacheDir(uri))
|
||||
} ?: run {
|
||||
Timber.e("onActivityResult error, data is null")
|
||||
fragment.toast("Error while getting file")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Timber.e("onActivityResult error, Unknown Request Code:$requestCode")
|
||||
fragment.toast("Unknown Request Code:$requestCode")
|
||||
|
|
|
@ -1,12 +1,142 @@
|
|||
package com.anytypeio.anytype.ui.settings.system
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.DropDownPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.anytypeio.anytype.R
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_CUSTOM
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_DEFAULT
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_LOCAL
|
||||
import com.anytypeio.anytype.core_utils.ext.Mimetype
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_NETWORK_MODE_CODE
|
||||
import com.anytypeio.anytype.core_utils.ext.subscribe
|
||||
import com.anytypeio.anytype.presentation.settings.PreferencesViewModel
|
||||
import com.anytypeio.anytype.ui.editor.PickerDelegate
|
||||
import javax.inject.Inject
|
||||
|
||||
class PreferenceFragment : PreferenceFragmentCompat() {
|
||||
|
||||
private lateinit var pickerDelegate: PickerDelegate
|
||||
private lateinit var filePathPreference: Preference
|
||||
private lateinit var networkModePreference: DropDownPreference
|
||||
|
||||
@Inject
|
||||
lateinit var factory: PreferencesViewModel.Factory
|
||||
private val vm by viewModels<PreferencesViewModel> { factory }
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
injectDependencies()
|
||||
super.onAttach(context)
|
||||
pickerDelegate = PickerDelegate.Impl(this) { actions ->
|
||||
when (actions) {
|
||||
is PickerDelegate.Actions.OnProceedWithFilePath -> {
|
||||
vm.onProceedWithFilePath(actions.filePath)
|
||||
}
|
||||
PickerDelegate.Actions.OnCancelCopyFileToCacheDir -> {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
}
|
||||
is PickerDelegate.Actions.OnPickedDocImageFromDevice -> {
|
||||
vm.onPickedDocImageFromDevice(actions.ctx, actions.filePath)
|
||||
}
|
||||
is PickerDelegate.Actions.OnStartCopyFileToCacheDir -> {
|
||||
vm.onStartCopyFileToCacheDir(actions.uri)
|
||||
}
|
||||
}
|
||||
}
|
||||
pickerDelegate.initPicker("")
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
pickerDelegate.resolveActivityResult(requestCode, resultCode, data)
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
with(lifecycleScope) {
|
||||
subscribe(vm.networkModeState) { state ->
|
||||
when (state.networkMode) {
|
||||
NetworkMode.DEFAULT -> {
|
||||
networkModePreference.value = NETWORK_MODE_DEFAULT
|
||||
filePathPreference.isVisible = false
|
||||
}
|
||||
NetworkMode.LOCAL -> {
|
||||
networkModePreference.value = NETWORK_MODE_LOCAL
|
||||
filePathPreference.isVisible = false
|
||||
}
|
||||
NetworkMode.CUSTOM -> {
|
||||
networkModePreference.value = NETWORK_MODE_CUSTOM
|
||||
filePathPreference.isVisible = true
|
||||
filePathPreference.summary = state.userFilePath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vm.onStart()
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
setPreferencesFromResource(R.xml.preferences, rootKey)
|
||||
val context = preferenceManager.context
|
||||
val screen = preferenceManager.createPreferenceScreen(context)
|
||||
networkModePreference = DropDownPreference(context).apply {
|
||||
summaryProvider = ListPreference.SimpleSummaryProvider.getInstance()
|
||||
isSingleLineTitle = true
|
||||
isIconSpaceReserved = false
|
||||
title = getString(R.string.settings_network_mode)
|
||||
setEntries(R.array.settings_networks_entries)
|
||||
setEntryValues(R.array.settings_networks_entries_values)
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
vm.proceedWithNetworkMode(newValue as String)
|
||||
true
|
||||
}
|
||||
}
|
||||
screen.addPreference(networkModePreference)
|
||||
filePathPreference = Preference(context).apply {
|
||||
isIconSpaceReserved = false
|
||||
title = getString(R.string.settings_network_configuration_file)
|
||||
isSingleLineTitle = true
|
||||
summary = getString(R.string.settings_network_configuration_file_choose)
|
||||
}
|
||||
filePathPreference.setOnPreferenceClickListener {
|
||||
openFilePicker()
|
||||
true
|
||||
}
|
||||
screen.addPreference(filePathPreference)
|
||||
preferenceScreen = screen
|
||||
}
|
||||
|
||||
private fun openFilePicker() {
|
||||
pickerDelegate.openFilePicker(Mimetype.MIME_TEXT_PLAIN, REQUEST_NETWORK_MODE_CODE)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
pickerDelegate.clearPickit()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
releaseDependencies()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun injectDependencies() {
|
||||
componentManager().appPreferencesComponent.get().inject(this)
|
||||
}
|
||||
|
||||
private fun releaseDependencies() {
|
||||
componentManager().appPreferencesComponent.release()
|
||||
}
|
||||
}
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<SwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="@string/trouble_mode"
|
||||
android:title="Troubleshooting mode" />
|
||||
|
||||
</PreferenceScreen>
|
|
@ -5,6 +5,21 @@ import com.anytypeio.anytype.core_models.primitives.TypeKey
|
|||
|
||||
sealed class Command {
|
||||
|
||||
data class AccountCreate(
|
||||
val name: String,
|
||||
val avatarPath: String?,
|
||||
val icon: Int,
|
||||
val networkMode: NetworkMode = NetworkMode.DEFAULT,
|
||||
val networkConfigFilePath: String? = null
|
||||
) : Command()
|
||||
|
||||
data class AccountSelect(
|
||||
val id: String,
|
||||
val path: String,
|
||||
val networkMode: NetworkMode = NetworkMode.DEFAULT,
|
||||
val networkConfigFilePath: String? = null
|
||||
) : Command()
|
||||
|
||||
class UploadFile(
|
||||
val path: String,
|
||||
val type: Block.Content.File.Type?
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.anytypeio.anytype.core_models
|
||||
|
||||
enum class NetworkMode {
|
||||
DEFAULT, LOCAL, CUSTOM
|
||||
}
|
||||
|
||||
data class NetworkModeConfig(
|
||||
val networkMode: NetworkMode = NetworkMode.DEFAULT,
|
||||
val userFilePath: String? = null,
|
||||
val storedFilePath: String? = null
|
||||
)
|
|
@ -0,0 +1,7 @@
|
|||
package com.anytypeio.anytype.core_models
|
||||
|
||||
object NetworkModeConstants {
|
||||
const val NETWORK_MODE_LOCAL = "local"
|
||||
const val NETWORK_MODE_DEFAULT = "default"
|
||||
const val NETWORK_MODE_CUSTOM = "custom"
|
||||
}
|
|
@ -5,4 +5,5 @@ object FileConstants {
|
|||
const val REQUEST_FILE_SAF_CODE = 2211
|
||||
const val REQUEST_MEDIA_CODE = 2212
|
||||
const val REQUEST_PROFILE_IMAGE_CODE = 2213
|
||||
const val REQUEST_NETWORK_MODE_CODE = 2214
|
||||
}
|
|
@ -283,8 +283,8 @@ fun String.normalizeUrl(): String =
|
|||
*/
|
||||
fun Fragment.startFilePicker(mime: Mimetype, requestCode: Int? = null) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = mime.value
|
||||
}
|
||||
val code = if (mime == Mimetype.MIME_FILE_ALL) {
|
||||
|
|
|
@ -14,7 +14,7 @@ object FilePickerUtils {
|
|||
Mimetype.MIME_VIDEO_ALL -> context.isPermissionGranted(getPermissionToRequestForVideos())
|
||||
Mimetype.MIME_IMAGE_ALL -> context.isPermissionGranted(getPermissionToRequestForImages())
|
||||
Mimetype.MIME_IMAGE_AND_VIDEO -> context.isPermissionGranted(getPermissionToRequestForImagesAndVideos())
|
||||
Mimetype.MIME_FILE_ALL -> {
|
||||
Mimetype.MIME_FILE_ALL, Mimetype.MIME_TEXT_PLAIN -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
true
|
||||
} else {
|
||||
|
@ -36,27 +36,28 @@ object FilePickerUtils {
|
|||
Mimetype.MIME_IMAGE_ALL -> getPermissionToRequestForImages()
|
||||
Mimetype.MIME_FILE_ALL -> getPermissionToRequestForFiles()
|
||||
Mimetype.MIME_IMAGE_AND_VIDEO -> getPermissionToRequestForImagesAndVideos()
|
||||
Mimetype.MIME_TEXT_PLAIN -> getPermissionToRequestForFiles()
|
||||
}
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForImages(): Array<String> =
|
||||
private fun getPermissionToRequestForImages(): Array<String> =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arrayOf(Manifest.permission.READ_MEDIA_IMAGES)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForVideos(): Array<String> =
|
||||
private fun getPermissionToRequestForVideos(): Array<String> =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arrayOf(Manifest.permission.READ_MEDIA_VIDEO)
|
||||
} else {
|
||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
}
|
||||
|
||||
fun getPermissionToRequestForFiles(): Array<String> =
|
||||
private fun getPermissionToRequestForFiles(): Array<String> =
|
||||
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
|
||||
fun getPermissionToRequestForImagesAndVideos(): Array<String> =
|
||||
private fun getPermissionToRequestForImagesAndVideos(): Array<String> =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
arrayOf(Manifest.permission.READ_MEDIA_VIDEO, Manifest.permission.READ_MEDIA_IMAGES)
|
||||
} else {
|
||||
|
|
|
@ -4,5 +4,6 @@ enum class Mimetype(val value: String) {
|
|||
MIME_VIDEO_ALL("video/*"),
|
||||
MIME_IMAGE_ALL("image/*"),
|
||||
MIME_FILE_ALL("*/*"),
|
||||
MIME_IMAGE_AND_VIDEO("image/*,video/*")
|
||||
MIME_IMAGE_AND_VIDEO("image/*,video/*"),
|
||||
MIME_TEXT_PLAIN("text/plain"),
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
package com.anytypeio.anytype.data.auth.repo
|
||||
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.data.auth.model.AccountEntity
|
||||
|
||||
interface AuthCache {
|
||||
|
@ -21,4 +23,7 @@ interface AuthCache {
|
|||
suspend fun saveLastOpenedObject(id: Id)
|
||||
suspend fun getLastOpenedObject() : Id?
|
||||
suspend fun clearLastOpenedObject()
|
||||
|
||||
suspend fun getNetworkMode(): NetworkModeConfig
|
||||
suspend fun setNetworkMode(modeConfig: NetworkModeConfig)
|
||||
}
|
|
@ -2,25 +2,20 @@ package com.anytypeio.anytype.data.auth.repo
|
|||
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.data.auth.model.AccountEntity
|
||||
import com.anytypeio.anytype.data.auth.model.WalletEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class AuthCacheDataStore(private val cache: AuthCache) : AuthDataStore {
|
||||
|
||||
override suspend fun selectAccount(
|
||||
id: String,
|
||||
path: String
|
||||
): AccountSetup {
|
||||
override suspend fun selectAccount(command: Command.AccountSelect): AccountSetup {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
iconGradientValue: Int
|
||||
): AccountSetup {
|
||||
override suspend fun createAccount(command: Command.AccountCreate): AccountSetup {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
|
@ -91,4 +86,12 @@ class AuthCacheDataStore(private val cache: AuthCache) : AuthDataStore {
|
|||
override suspend fun saveLastOpenedObject(id: Id) { cache.saveLastOpenedObject(id) }
|
||||
override suspend fun getLastOpenedObject(): Id? = cache.getLastOpenedObject()
|
||||
override suspend fun clearLastOpenedObject() { cache.clearLastOpenedObject() }
|
||||
|
||||
override suspend fun getNetworkMode(): NetworkModeConfig {
|
||||
return cache.getNetworkMode()
|
||||
}
|
||||
|
||||
override suspend fun setNetworkMode(modeConfig: NetworkModeConfig) {
|
||||
cache.setNetworkMode(modeConfig)
|
||||
}
|
||||
}
|
|
@ -3,7 +3,9 @@ package com.anytypeio.anytype.data.auth.repo
|
|||
import com.anytypeio.anytype.core_models.Account
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.data.auth.mapper.toDomain
|
||||
import com.anytypeio.anytype.data.auth.mapper.toEntity
|
||||
import com.anytypeio.anytype.domain.auth.model.Wallet
|
||||
|
@ -25,42 +27,22 @@ class AuthDataRepository(
|
|||
}
|
||||
|
||||
override suspend fun selectAccount(
|
||||
id: String, path: String
|
||||
command: Command.AccountSelect
|
||||
): AccountSetup {
|
||||
return if (debugConfig.setTimeouts) {
|
||||
withTimeout(DebugConfig.SELECT_ACCOUNT_TIMEOUT) {
|
||||
factory.remote.selectAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
factory.remote.selectAccount(command)
|
||||
}
|
||||
} else {
|
||||
factory.remote.selectAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
}
|
||||
} else { factory.remote.selectAccount(command)}
|
||||
}
|
||||
|
||||
override suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
icon: Int
|
||||
command: Command.AccountCreate
|
||||
): AccountSetup {
|
||||
return if (debugConfig.setTimeouts) {
|
||||
withTimeout(DebugConfig.CREATE_ACCOUNT_TIMEOUT) {
|
||||
factory.remote.createAccount(
|
||||
name = name,
|
||||
avatarPath = avatarPath,
|
||||
iconGradientValue = icon
|
||||
)
|
||||
}
|
||||
withTimeout(DebugConfig.CREATE_ACCOUNT_TIMEOUT) { factory.remote.createAccount(command)}
|
||||
} else {
|
||||
factory.remote.createAccount(
|
||||
name = name,
|
||||
avatarPath = avatarPath,
|
||||
iconGradientValue = icon
|
||||
)
|
||||
factory.remote.createAccount(command)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,4 +102,12 @@ class AuthDataRepository(
|
|||
override suspend fun clearLastOpenedObject() { factory.cache.clearLastOpenedObject() }
|
||||
|
||||
|
||||
|
||||
override suspend fun getNetworkMode(): NetworkModeConfig {
|
||||
return factory.cache.getNetworkMode()
|
||||
}
|
||||
|
||||
override suspend fun setNetworkMode(modeConfig: NetworkModeConfig) {
|
||||
factory.cache.setNetworkMode(modeConfig)
|
||||
}
|
||||
}
|
|
@ -2,20 +2,17 @@ package com.anytypeio.anytype.data.auth.repo
|
|||
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.data.auth.model.AccountEntity
|
||||
import com.anytypeio.anytype.data.auth.model.WalletEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthDataStore {
|
||||
|
||||
suspend fun selectAccount(id: String, path: String): AccountSetup
|
||||
|
||||
suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
iconGradientValue: Int
|
||||
): AccountSetup
|
||||
suspend fun selectAccount(command: Command.AccountSelect): AccountSetup
|
||||
suspend fun createAccount(command: Command.AccountCreate): AccountSetup
|
||||
|
||||
suspend fun deleteAccount() : AccountStatus
|
||||
suspend fun restoreAccount() : AccountStatus
|
||||
|
@ -47,4 +44,7 @@ interface AuthDataStore {
|
|||
suspend fun saveLastOpenedObject(id: Id)
|
||||
suspend fun getLastOpenedObject() : Id?
|
||||
suspend fun clearLastOpenedObject()
|
||||
|
||||
suspend fun getNetworkMode(): NetworkModeConfig
|
||||
suspend fun setNetworkMode(modeConfig: NetworkModeConfig)
|
||||
}
|
|
@ -2,17 +2,14 @@ package com.anytypeio.anytype.data.auth.repo
|
|||
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.data.auth.model.AccountEntity
|
||||
import com.anytypeio.anytype.data.auth.model.WalletEntity
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface AuthRemote {
|
||||
suspend fun selectAccount(id: String, path: String): AccountSetup
|
||||
suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
iconGradientValue: Int
|
||||
): AccountSetup
|
||||
suspend fun selectAccount(command: Command.AccountSelect): AccountSetup
|
||||
suspend fun createAccount(command: Command.AccountCreate): AccountSetup
|
||||
suspend fun deleteAccount() : AccountStatus
|
||||
suspend fun restoreAccount() : AccountStatus
|
||||
suspend fun recoverAccount()
|
||||
|
|
|
@ -2,7 +2,9 @@ package com.anytypeio.anytype.data.auth.repo
|
|||
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.data.auth.model.AccountEntity
|
||||
import com.anytypeio.anytype.data.auth.model.WalletEntity
|
||||
|
||||
|
@ -11,21 +13,12 @@ class AuthRemoteDataStore(
|
|||
) : AuthDataStore {
|
||||
|
||||
override suspend fun selectAccount(
|
||||
id: String, path: String
|
||||
) = authRemote.selectAccount(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
command: Command.AccountSelect
|
||||
) = authRemote.selectAccount(command)
|
||||
|
||||
override suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
iconGradientValue: Int
|
||||
) : AccountSetup = authRemote.createAccount(
|
||||
name = name,
|
||||
avatarPath = avatarPath,
|
||||
iconGradientValue = iconGradientValue
|
||||
)
|
||||
command: Command.AccountCreate
|
||||
): AccountSetup = authRemote.createAccount(command)
|
||||
|
||||
override suspend fun deleteAccount(): AccountStatus = authRemote.deleteAccount()
|
||||
|
||||
|
@ -104,4 +97,12 @@ class AuthRemoteDataStore(
|
|||
override suspend fun clearLastOpenedObject() {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkMode(): NetworkModeConfig {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
|
||||
override suspend fun setNetworkMode(modeConfig: NetworkModeConfig) {
|
||||
throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.data
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.StubAccount
|
||||
import com.anytypeio.anytype.core_models.StubAccountSetup
|
||||
import com.anytypeio.anytype.core_models.StubFeatureConfig
|
||||
|
@ -68,22 +69,30 @@ class AuthDataRepositoryTest {
|
|||
val features = StubFeatureConfig()
|
||||
|
||||
authRemote.stub {
|
||||
onBlocking { selectAccount(id = id, path = path) } doReturn StubAccountSetup(
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
onBlocking { selectAccount(command) } doReturn StubAccountSetup(
|
||||
account = account,
|
||||
features = features
|
||||
)
|
||||
}
|
||||
|
||||
repo.selectAccount(
|
||||
id = id,
|
||||
path = path
|
||||
command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
)
|
||||
|
||||
verifyNoInteractions(authCache)
|
||||
|
||||
verify(authRemote, times(1)).selectAccount(
|
||||
id = id,
|
||||
path = path
|
||||
Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
)
|
||||
|
||||
verifyNoMoreInteractions(authRemote)
|
||||
|
@ -102,26 +111,31 @@ class AuthDataRepositoryTest {
|
|||
|
||||
authRemote.stub {
|
||||
onBlocking {
|
||||
createAccount(
|
||||
val command = Command.AccountCreate(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
iconGradientValue = icon
|
||||
icon = icon
|
||||
)
|
||||
createAccount(command)
|
||||
} doReturn setup
|
||||
}
|
||||
|
||||
repo.createAccount(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
icon = icon
|
||||
Command.AccountCreate(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
icon = icon
|
||||
)
|
||||
)
|
||||
|
||||
verifyNoInteractions(authCache)
|
||||
|
||||
verify(authRemote, times(1)).createAccount(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
iconGradientValue = icon
|
||||
Command.AccountCreate(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
icon = icon
|
||||
)
|
||||
)
|
||||
|
||||
verifyNoMoreInteractions(authRemote)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package com.anytypeio.anytype.domain.auth.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.Account
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.platform.MetricsProvider
|
||||
|
||||
|
@ -11,26 +13,34 @@ import com.anytypeio.anytype.domain.platform.MetricsProvider
|
|||
*/
|
||||
open class CreateAccount(
|
||||
private val repository: AuthRepository,
|
||||
// TODO rename config storage
|
||||
private val configStorage: ConfigStorage,
|
||||
private val metricsProvider: MetricsProvider
|
||||
) : BaseUseCase<Account, CreateAccount.Params>() {
|
||||
private val metricsProvider: MetricsProvider,
|
||||
dispatcher: AppCoroutineDispatchers
|
||||
) : ResultInteractor<CreateAccount.Params, Account>(dispatcher.io) {
|
||||
|
||||
override suspend fun run(params: Params) = safe {
|
||||
override suspend fun doWork(params: Params): Account {
|
||||
repository.setMetrics(
|
||||
version = metricsProvider.getVersion(),
|
||||
platform = metricsProvider.getPlatform()
|
||||
)
|
||||
val setup = repository.createAccount(
|
||||
|
||||
val networkMode = repository.getNetworkMode()
|
||||
|
||||
val command = Command.AccountCreate(
|
||||
name = params.name,
|
||||
avatarPath = params.avatarPath,
|
||||
icon = params.iconGradientValue
|
||||
icon = params.iconGradientValue,
|
||||
networkMode = networkMode.networkMode,
|
||||
networkConfigFilePath = networkMode.storedFilePath
|
||||
)
|
||||
val setup = repository.createAccount(command)
|
||||
with(repository) {
|
||||
saveAccount(setup.account)
|
||||
setCurrentAccount(setup.account.id)
|
||||
}
|
||||
configStorage.set(setup.config)
|
||||
setup.account
|
||||
return setup.account
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.domain.auth.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
import com.anytypeio.anytype.domain.base.Either
|
||||
|
@ -32,10 +33,16 @@ class LaunchAccount @Inject constructor(
|
|||
version = metricsProvider.getVersion(),
|
||||
platform = metricsProvider.getPlatform()
|
||||
)
|
||||
repository.selectAccount(
|
||||
|
||||
val networkMode = repository.getNetworkMode()
|
||||
|
||||
val command = Command.AccountSelect(
|
||||
id = repository.getCurrentAccountId(),
|
||||
path = pathProvider.providePath()
|
||||
).let { setup ->
|
||||
path = pathProvider.providePath(),
|
||||
networkMode = networkMode.networkMode,
|
||||
networkConfigFilePath = networkMode.storedFilePath
|
||||
)
|
||||
repository.selectAccount(command).let { setup ->
|
||||
repository.updateAccount(setup.account)
|
||||
featuresConfigProvider.set(
|
||||
enableDataView = setup.features.enableDataView ?: false,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.domain.auth.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
|
@ -30,10 +31,16 @@ class ResumeAccount(
|
|||
path = pathProvider.providePath(),
|
||||
mnemonic = repository.getMnemonic()
|
||||
)
|
||||
repository.selectAccount(
|
||||
|
||||
val networkMode = repository.getNetworkMode()
|
||||
|
||||
val command = Command.AccountSelect(
|
||||
id = repository.getCurrentAccountId(),
|
||||
path = pathProvider.providePath()
|
||||
).let { setup ->
|
||||
path = pathProvider.providePath(),
|
||||
networkMode = networkMode.networkMode,
|
||||
networkConfigFilePath = networkMode.storedFilePath
|
||||
)
|
||||
repository.selectAccount(command).let { setup ->
|
||||
featuresConfigProvider.set(
|
||||
enableDataView = setup.features.enableDataView ?: false,
|
||||
enableDebug = setup.features.enableDebug ?: false,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.anytypeio.anytype.domain.auth.interactor
|
||||
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.BaseUseCase
|
||||
|
@ -24,10 +25,16 @@ class SelectAccount @Inject constructor(
|
|||
version = metricsProvider.getVersion(),
|
||||
platform = metricsProvider.getPlatform()
|
||||
)
|
||||
val setup = repository.selectAccount(
|
||||
|
||||
val networkMode = repository.getNetworkMode()
|
||||
|
||||
val command = Command.AccountSelect(
|
||||
id = params.id,
|
||||
path = params.path
|
||||
path = params.path,
|
||||
networkMode = networkMode.networkMode,
|
||||
networkConfigFilePath = networkMode.storedFilePath
|
||||
)
|
||||
val setup = repository.selectAccount(command)
|
||||
with(repository) {
|
||||
saveAccount(setup.account)
|
||||
setCurrentAccount(setup.account.id)
|
||||
|
|
|
@ -3,7 +3,9 @@ package com.anytypeio.anytype.domain.auth.repo
|
|||
import com.anytypeio.anytype.core_models.Account
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.domain.auth.model.Wallet
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
@ -16,13 +18,8 @@ interface AuthRepository {
|
|||
* @param id user account id
|
||||
* @param path wallet repository path
|
||||
*/
|
||||
suspend fun selectAccount(id: String, path: String): AccountSetup
|
||||
|
||||
suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
icon: Int
|
||||
): AccountSetup
|
||||
suspend fun selectAccount(command: Command.AccountSelect): AccountSetup
|
||||
suspend fun createAccount(command: Command.AccountCreate): AccountSetup
|
||||
|
||||
suspend fun deleteAccount() : AccountStatus
|
||||
suspend fun restoreAccount() : AccountStatus
|
||||
|
@ -64,4 +61,7 @@ interface AuthRepository {
|
|||
suspend fun saveLastOpenedObjectId(id: Id)
|
||||
suspend fun getLastOpenedObjectId() : Id?
|
||||
suspend fun clearLastOpenedObject()
|
||||
|
||||
suspend fun getNetworkMode(): NetworkModeConfig
|
||||
suspend fun setNetworkMode(modeConfig: NetworkModeConfig)
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package com.anytypeio.anytype.domain.networkmode
|
||||
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import javax.inject.Inject
|
||||
|
||||
class GetNetworkMode @Inject constructor(
|
||||
private val repository: AuthRepository,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
) : ResultInteractor<Unit, NetworkModeConfig>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Unit): NetworkModeConfig {
|
||||
return repository.getNetworkMode()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
package com.anytypeio.anytype.domain.networkmode
|
||||
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.base.ResultInteractor
|
||||
import javax.inject.Inject
|
||||
|
||||
class SetNetworkMode @Inject constructor(
|
||||
private val repository: AuthRepository,
|
||||
dispatchers: AppCoroutineDispatchers,
|
||||
) : ResultInteractor<SetNetworkMode.Params, Unit>(dispatchers.io) {
|
||||
|
||||
override suspend fun doWork(params: Params) {
|
||||
repository.setNetworkMode(params.networkModeConfig)
|
||||
}
|
||||
|
||||
data class Params(val networkModeConfig: NetworkModeConfig)
|
||||
}
|
|
@ -1,12 +1,16 @@
|
|||
package com.anytypeio.anytype.domain.auth
|
||||
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.CoroutineTestRule
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.core_models.StubAccountSetup
|
||||
import com.anytypeio.anytype.domain.auth.interactor.CreateAccount
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
|
||||
import com.anytypeio.anytype.domain.common.DefaultCoroutineTestRule
|
||||
import com.anytypeio.anytype.domain.config.ConfigStorage
|
||||
import com.anytypeio.anytype.domain.platform.MetricsProvider
|
||||
import com.anytypeio.anytype.domain.workspace.WorkspaceManager
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
@ -23,9 +27,8 @@ import org.mockito.kotlin.verifyNoMoreInteractions
|
|||
|
||||
class CreateAccountTest {
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@get:Rule
|
||||
var rule = CoroutineTestRule()
|
||||
val rule = DefaultCoroutineTestRule()
|
||||
|
||||
@Mock
|
||||
lateinit var repo: AuthRepository
|
||||
|
@ -33,8 +36,7 @@ class CreateAccountTest {
|
|||
@Mock
|
||||
lateinit var configStorage: ConfigStorage
|
||||
|
||||
@Mock
|
||||
lateinit var workspaceManager: WorkspaceManager
|
||||
lateinit var dispatchers: AppCoroutineDispatchers
|
||||
|
||||
@Mock
|
||||
lateinit var metricsProvider: MetricsProvider
|
||||
|
@ -42,13 +44,20 @@ class CreateAccountTest {
|
|||
private lateinit var createAccount: CreateAccount
|
||||
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
dispatchers = AppCoroutineDispatchers(
|
||||
io = rule.dispatcher,
|
||||
computation = rule.dispatcher,
|
||||
main = rule.dispatcher
|
||||
)
|
||||
createAccount = CreateAccount(
|
||||
repository = repo,
|
||||
configStorage = configStorage,
|
||||
metricsProvider = metricsProvider
|
||||
metricsProvider = metricsProvider,
|
||||
dispatcher = dispatchers
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -66,7 +75,21 @@ class CreateAccountTest {
|
|||
)
|
||||
|
||||
repo.stub {
|
||||
onBlocking { createAccount(name, path, icon) } doReturn setup
|
||||
onBlocking {
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
val command = Command.AccountCreate(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
icon = icon,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
onBlocking { createAccount(command) } doReturn setup
|
||||
}
|
||||
|
||||
val version = MockDataFactory.randomString()
|
||||
|
@ -76,7 +99,14 @@ class CreateAccountTest {
|
|||
|
||||
createAccount.run(param)
|
||||
|
||||
verify(repo, times(1)).createAccount(name, path, icon)
|
||||
val command = Command.AccountCreate(
|
||||
name = name,
|
||||
avatarPath = path,
|
||||
icon = icon,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
verify(repo, times(1)).getNetworkMode()
|
||||
verify(repo, times(1)).createAccount(command)
|
||||
verify(repo, times(1)).saveAccount(setup.account)
|
||||
verify(repo, times(1)).setCurrentAccount(setup.account.id)
|
||||
verify(repo, times(1)).setMetrics(
|
||||
|
|
|
@ -3,10 +3,11 @@ package com.anytypeio.anytype.domain.auth
|
|||
import com.anytypeio.anytype.core_models.Account
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.core_models.CoroutineTestRule
|
||||
import com.anytypeio.anytype.core_models.FeaturesConfig
|
||||
import com.anytypeio.anytype.core_models.StubAccount
|
||||
import com.anytypeio.anytype.core_models.StubAccountSetup
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.core_models.StubConfig
|
||||
import com.anytypeio.anytype.domain.auth.interactor.SelectAccount
|
||||
import com.anytypeio.anytype.domain.auth.repo.AuthRepository
|
||||
|
@ -19,7 +20,6 @@ import com.anytypeio.anytype.test_utils.MockDataFactory
|
|||
import kotlin.test.assertTrue
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
@ -29,7 +29,6 @@ import org.mockito.kotlin.doReturn
|
|||
import org.mockito.kotlin.stub
|
||||
import org.mockito.kotlin.times
|
||||
import org.mockito.kotlin.verify
|
||||
import org.mockito.kotlin.verifyBlocking
|
||||
import org.mockito.kotlin.verifyNoMoreInteractions
|
||||
|
||||
class StartAccountTest {
|
||||
|
@ -97,10 +96,20 @@ class StartAccountTest {
|
|||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
selectAccount(
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
path = path,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
selectAccount(command)
|
||||
} doReturn AccountSetup(
|
||||
account = account,
|
||||
features = featuresConfig,
|
||||
|
@ -116,11 +125,15 @@ class StartAccountTest {
|
|||
|
||||
selectAccount.run(params)
|
||||
|
||||
verify(repo, times(1)).selectAccount(
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
path = path,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
|
||||
verify(repo, times(1)).getNetworkMode()
|
||||
verify(repo, times(1)).selectAccount(command)
|
||||
|
||||
verify(repo, times(1)).saveAccount(account)
|
||||
|
||||
verify(repo, times(1)).setCurrentAccount(account.id)
|
||||
|
@ -159,10 +172,20 @@ class StartAccountTest {
|
|||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
selectAccount(
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
path = path,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
selectAccount(command)
|
||||
} doReturn AccountSetup(
|
||||
account = account,
|
||||
features = featuresConfig,
|
||||
|
@ -202,10 +225,20 @@ class StartAccountTest {
|
|||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
selectAccount(
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
path = path,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
selectAccount(command)
|
||||
} doReturn AccountSetup(
|
||||
account = account,
|
||||
features = featuresConfig,
|
||||
|
@ -252,10 +285,146 @@ class StartAccountTest {
|
|||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
selectAccount(
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path
|
||||
path = path,
|
||||
networkMode = NetworkMode.DEFAULT
|
||||
)
|
||||
selectAccount(command)
|
||||
} doReturn AccountSetup(
|
||||
account = account,
|
||||
features = featuresConfig,
|
||||
status = AccountStatus.Active,
|
||||
config = config
|
||||
)
|
||||
}
|
||||
|
||||
val result = selectAccount.run(params)
|
||||
|
||||
verify(featuresConfigProvider, times(1)).set(
|
||||
enableDataView = true,
|
||||
enableDebug = false,
|
||||
enableChannelSwitch = true,
|
||||
enableSpaces = false
|
||||
)
|
||||
|
||||
assertTrue { result == Either.Right(Pair(config.analytics, AccountStatus.Active)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should send local mode config on account select`() = runBlocking {
|
||||
|
||||
val id = MockDataFactory.randomString()
|
||||
val path = MockDataFactory.randomString()
|
||||
|
||||
val params = SelectAccount.Params(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
||||
val account = Account(
|
||||
id = id,
|
||||
name = MockDataFactory.randomString(),
|
||||
avatar = null,
|
||||
color = null
|
||||
)
|
||||
|
||||
val featuresConfig = FeaturesConfig(
|
||||
enableDataView = true,
|
||||
enableDebug = false,
|
||||
enablePrereleaseChannel = true
|
||||
)
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.LOCAL
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path,
|
||||
networkMode = NetworkMode.LOCAL
|
||||
)
|
||||
selectAccount(command)
|
||||
} doReturn AccountSetup(
|
||||
account = account,
|
||||
features = featuresConfig,
|
||||
status = AccountStatus.Active,
|
||||
config = config
|
||||
)
|
||||
}
|
||||
|
||||
val result = selectAccount.run(params)
|
||||
|
||||
verify(featuresConfigProvider, times(1)).set(
|
||||
enableDataView = true,
|
||||
enableDebug = false,
|
||||
enableChannelSwitch = true,
|
||||
enableSpaces = false
|
||||
)
|
||||
|
||||
assertTrue { result == Either.Right(Pair(config.analytics, AccountStatus.Active)) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should send custom mode config with path on account select`() = runBlocking {
|
||||
|
||||
val id = MockDataFactory.randomString()
|
||||
val path = MockDataFactory.randomString()
|
||||
|
||||
val params = SelectAccount.Params(
|
||||
id = id,
|
||||
path = path
|
||||
)
|
||||
|
||||
val account = Account(
|
||||
id = id,
|
||||
name = MockDataFactory.randomString(),
|
||||
avatar = null,
|
||||
color = null
|
||||
)
|
||||
|
||||
val featuresConfig = FeaturesConfig(
|
||||
enableDataView = true,
|
||||
enableDebug = false,
|
||||
enablePrereleaseChannel = true
|
||||
)
|
||||
|
||||
val storedFilePath = MockDataFactory.randomString()
|
||||
val userPath = MockDataFactory.randomString()
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
getNetworkMode()
|
||||
} doReturn NetworkModeConfig(
|
||||
networkMode = NetworkMode.CUSTOM,
|
||||
storedFilePath = storedFilePath,
|
||||
userFilePath = userPath
|
||||
)
|
||||
}
|
||||
|
||||
repo.stub {
|
||||
onBlocking {
|
||||
val command = Command.AccountSelect(
|
||||
id = id,
|
||||
path = path,
|
||||
networkMode = NetworkMode.CUSTOM,
|
||||
networkConfigFilePath = storedFilePath
|
||||
)
|
||||
selectAccount(command)
|
||||
} doReturn AccountSetup(
|
||||
account = account,
|
||||
features = featuresConfig,
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
package com.anytypeio.anytype.domain.common
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.StandardTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class DefaultCoroutineTestRule() : TestWatcher() {
|
||||
|
||||
val dispatcher = StandardTestDispatcher(name = "Default test dispatcher")
|
||||
|
||||
override fun starting(description: Description) {
|
||||
super.starting(description)
|
||||
Dispatchers.setMain(dispatcher)
|
||||
}
|
||||
|
||||
override fun finished(description: Description) {
|
||||
super.finished(description)
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
|
||||
fun advanceTime(millis: Long = 100L) {
|
||||
dispatcher.scheduler.advanceTimeBy(millis)
|
||||
}
|
||||
}
|
|
@ -826,6 +826,21 @@
|
|||
<string name="debug_mode">debug_mode</string>
|
||||
<string name="trouble_mode">trouble_mode</string>
|
||||
<string name="settings_screen">Settings</string>
|
||||
<string name="settings_network_mode">Network mode</string>
|
||||
<string name="settings_network_configuration_file">Network Configuration File</string>
|
||||
<string name="settings_network_configuration_file_choose">Choose file</string>
|
||||
|
||||
<string-array name="settings_networks_entries">
|
||||
<item>Default</item>
|
||||
<item>Local-only Mode</item>
|
||||
<item>Custom</item>
|
||||
</string-array>
|
||||
|
||||
<array name="settings_networks_entries_values">
|
||||
<item>default</item>
|
||||
<item>local</item>
|
||||
<item>custom</item>
|
||||
</array>
|
||||
|
||||
<string name="snack_link_to">linked to</string>
|
||||
<string name="snack_move_to">moved to</string>
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.anytypeio.anytype.middleware.auth
|
|||
|
||||
import com.anytypeio.anytype.core_models.AccountSetup
|
||||
import com.anytypeio.anytype.core_models.AccountStatus
|
||||
import com.anytypeio.anytype.core_models.Command
|
||||
import com.anytypeio.anytype.data.auth.model.WalletEntity
|
||||
import com.anytypeio.anytype.data.auth.repo.AuthRemote
|
||||
import com.anytypeio.anytype.middleware.EventProxy
|
||||
|
@ -20,18 +21,12 @@ class AuthMiddleware(
|
|||
) : AuthRemote {
|
||||
|
||||
override suspend fun selectAccount(
|
||||
id: String, path: String
|
||||
): AccountSetup = middleware.accountSelect(id, path)
|
||||
command: Command.AccountSelect
|
||||
): AccountSetup = middleware.accountSelect(command)
|
||||
|
||||
override suspend fun createAccount(
|
||||
name: String,
|
||||
avatarPath: String?,
|
||||
iconGradientValue: Int,
|
||||
) : AccountSetup = middleware.accountCreate(
|
||||
name = name,
|
||||
path = avatarPath,
|
||||
iconGradientValue = iconGradientValue
|
||||
)
|
||||
command: Command.AccountCreate
|
||||
) : AccountSetup = middleware.accountCreate(command)
|
||||
|
||||
override suspend fun deleteAccount(): AccountStatus = middleware.accountDelete()
|
||||
override suspend fun restoreAccount(): AccountStatus = middleware.accountRestore()
|
||||
|
|
|
@ -35,6 +35,7 @@ import com.anytypeio.anytype.middleware.BuildConfig
|
|||
import com.anytypeio.anytype.middleware.auth.toAccountSetup
|
||||
import com.anytypeio.anytype.middleware.const.Constants
|
||||
import com.anytypeio.anytype.middleware.mappers.MDVFilter
|
||||
import com.anytypeio.anytype.middleware.mappers.MNetworkMode
|
||||
import com.anytypeio.anytype.middleware.mappers.MRelationFormat
|
||||
import com.anytypeio.anytype.middleware.mappers.config
|
||||
import com.anytypeio.anytype.middleware.mappers.core
|
||||
|
@ -59,15 +60,15 @@ class Middleware @Inject constructor(
|
|||
) {
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun accountCreate(
|
||||
name: String,
|
||||
path: String?,
|
||||
iconGradientValue: Int
|
||||
): AccountSetup {
|
||||
fun accountCreate(command: Command.AccountCreate): AccountSetup {
|
||||
|
||||
val request = Rpc.Account.Create.Request(
|
||||
name = name,
|
||||
avatarLocalPath = path,
|
||||
icon = iconGradientValue.toLong()
|
||||
name = command.name,
|
||||
avatarLocalPath = command.avatarPath,
|
||||
icon = command.icon.toLong(),
|
||||
networkMode = command.networkMode?.toMiddlewareModel() ?: MNetworkMode.DefaultConfig,
|
||||
networkCustomConfigFilePath = command.networkConfigFilePath.orEmpty()
|
||||
|
||||
)
|
||||
if (BuildConfig.DEBUG) logRequest(request)
|
||||
val response = service.accountCreate(request)
|
||||
|
@ -75,6 +76,25 @@ class Middleware @Inject constructor(
|
|||
return response.toAccountSetup()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun accountSelect(command: Command.AccountSelect): AccountSetup {
|
||||
|
||||
val networkMode = command.networkMode?.toMiddlewareModel() ?: MNetworkMode.DefaultConfig
|
||||
val networkCustomConfigFilePath = if (networkMode == MNetworkMode.CustomConfig) {
|
||||
command.networkConfigFilePath.orEmpty()
|
||||
} else ""
|
||||
val request = Rpc.Account.Select.Request(
|
||||
id = command.id,
|
||||
rootPath = command.path,
|
||||
networkMode = networkMode,
|
||||
networkCustomConfigFilePath = networkCustomConfigFilePath
|
||||
)
|
||||
if (BuildConfig.DEBUG) logRequest(request)
|
||||
val response = service.accountSelect(request)
|
||||
if (BuildConfig.DEBUG) logResponse(response)
|
||||
return response.toAccountSetup()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun accountDelete(): AccountStatus {
|
||||
val request = Rpc.Account.Delete.Request()
|
||||
|
@ -105,18 +125,6 @@ class Middleware @Inject constructor(
|
|||
return status.core()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun accountSelect(id: String, path: String): AccountSetup {
|
||||
val request = Rpc.Account.Select.Request(
|
||||
id = id,
|
||||
rootPath = path
|
||||
)
|
||||
if (BuildConfig.DEBUG) logRequest(request)
|
||||
val response = service.accountSelect(request)
|
||||
if (BuildConfig.DEBUG) logResponse(response)
|
||||
return response.toAccountSetup()
|
||||
}
|
||||
|
||||
@Throws(Exception::class)
|
||||
fun accountStop(clearLocalRepositoryData: Boolean) {
|
||||
val request: Rpc.Account.Stop.Request = Rpc.Account.Stop.Request(
|
||||
|
|
|
@ -63,3 +63,4 @@ typealias MDVRestriction = anytype.model.Restrictions.DataviewRestriction
|
|||
|
||||
typealias MWidget = anytype.model.Block.Content.Widget
|
||||
typealias MWidgetLayout = anytype.model.Block.Content.Widget.Layout
|
||||
typealias MNetworkMode = anytype.Rpc.Account.NetworkMode
|
||||
|
|
|
@ -6,6 +6,7 @@ import anytype.model.RelationFormat
|
|||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.BlockSplitMode
|
||||
import com.anytypeio.anytype.core_models.InternalFlags
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.ObjectType
|
||||
import com.anytypeio.anytype.core_models.Position
|
||||
import com.anytypeio.anytype.core_models.Relation
|
||||
|
@ -497,4 +498,10 @@ fun Block.Content.Widget.Layout.mw() : MWidgetLayout = when(this) {
|
|||
Block.Content.Widget.Layout.LINK -> MWidgetLayout.Link
|
||||
Block.Content.Widget.Layout.LIST -> MWidgetLayout.List
|
||||
Block.Content.Widget.Layout.COMPACT_LIST -> MWidgetLayout.CompactList
|
||||
}
|
||||
|
||||
fun NetworkMode.toMiddlewareModel(): MNetworkMode = when (this) {
|
||||
NetworkMode.DEFAULT -> MNetworkMode.DefaultConfig
|
||||
NetworkMode.LOCAL -> MNetworkMode.LocalOnly
|
||||
NetworkMode.CUSTOM -> MNetworkMode.CustomConfig
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package com.anytypeio.anytype.persistence.networkmode
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_CUSTOM
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_DEFAULT
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_LOCAL
|
||||
import com.anytypeio.anytype.persistence.networkmode.DefaultNetworkModeProvider.NetworkModeConstants.NETWORK_MODE_APP_FILE_PATH_PREF
|
||||
import com.anytypeio.anytype.persistence.networkmode.DefaultNetworkModeProvider.NetworkModeConstants.NETWORK_MODE_PREF
|
||||
import com.anytypeio.anytype.persistence.networkmode.DefaultNetworkModeProvider.NetworkModeConstants.NETWORK_MODE_USER_FILE_PATH_PREF
|
||||
|
||||
interface NetworkModeProvider {
|
||||
fun set(networkModeConfig: NetworkModeConfig)
|
||||
fun get(): NetworkModeConfig
|
||||
}
|
||||
|
||||
class DefaultNetworkModeProvider(private val sharedPreferences: SharedPreferences) :
|
||||
NetworkModeProvider {
|
||||
|
||||
override fun set(networkModeConfig: NetworkModeConfig) {
|
||||
val (userFilePath, storedFilePath) = if (networkModeConfig.networkMode == NetworkMode.CUSTOM) {
|
||||
networkModeConfig.userFilePath to networkModeConfig.storedFilePath
|
||||
} else {
|
||||
null to null
|
||||
}
|
||||
|
||||
val modeValue= when (networkModeConfig.networkMode) {
|
||||
NetworkMode.DEFAULT -> NETWORK_MODE_DEFAULT
|
||||
NetworkMode.LOCAL -> NETWORK_MODE_LOCAL
|
||||
NetworkMode.CUSTOM -> NETWORK_MODE_CUSTOM
|
||||
}
|
||||
|
||||
sharedPreferences.edit().apply {
|
||||
putString(NETWORK_MODE_PREF, modeValue)
|
||||
putString(NETWORK_MODE_USER_FILE_PATH_PREF, userFilePath)
|
||||
putString(NETWORK_MODE_APP_FILE_PATH_PREF, storedFilePath)
|
||||
apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(): NetworkModeConfig {
|
||||
val networkMode =
|
||||
when (sharedPreferences.getString(NETWORK_MODE_PREF, NETWORK_MODE_DEFAULT)) {
|
||||
NETWORK_MODE_DEFAULT -> NetworkMode.DEFAULT
|
||||
NETWORK_MODE_LOCAL -> NetworkMode.LOCAL
|
||||
NETWORK_MODE_CUSTOM -> NetworkMode.CUSTOM
|
||||
else -> NetworkMode.DEFAULT
|
||||
}
|
||||
return if (networkMode == NetworkMode.CUSTOM) {
|
||||
val userFilePath = sharedPreferences.getString(
|
||||
NETWORK_MODE_USER_FILE_PATH_PREF, null
|
||||
)
|
||||
val storedFilePath = sharedPreferences.getString(
|
||||
NETWORK_MODE_APP_FILE_PATH_PREF, null
|
||||
)
|
||||
NetworkModeConfig(networkMode, userFilePath, storedFilePath)
|
||||
} else {
|
||||
NetworkModeConfig(networkMode, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
object NetworkModeConstants {
|
||||
const val NETWORK_MODE_PREF = "pref.network_mode"
|
||||
const val NETWORK_MODE_APP_FILE_PATH_PREF = "pref.network_config_file_path"
|
||||
const val NETWORK_MODE_USER_FILE_PATH_PREF = "pref.network_mode_user_config_file_path"
|
||||
|
||||
const val NAMED_NETWORK_MODE_PREFS = "network_mode"
|
||||
}
|
||||
}
|
|
@ -1,16 +1,19 @@
|
|||
package com.anytypeio.anytype.persistence.repo
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.data.auth.model.AccountEntity
|
||||
import com.anytypeio.anytype.data.auth.repo.AuthCache
|
||||
import com.anytypeio.anytype.persistence.db.AnytypeDatabase
|
||||
import com.anytypeio.anytype.persistence.mapper.toEntity
|
||||
import com.anytypeio.anytype.persistence.mapper.toTable
|
||||
import com.anytypeio.anytype.persistence.networkmode.NetworkModeProvider
|
||||
|
||||
class DefaultAuthCache(
|
||||
private val db: AnytypeDatabase,
|
||||
private val defaultPrefs: SharedPreferences,
|
||||
private val encryptedPrefs: SharedPreferences
|
||||
private val encryptedPrefs: SharedPreferences,
|
||||
private val networkModeProvider: NetworkModeProvider
|
||||
) : AuthCache {
|
||||
|
||||
override suspend fun saveAccount(account: AccountEntity) {
|
||||
|
@ -101,6 +104,11 @@ class DefaultAuthCache(
|
|||
encryptedPrefs.edit().remove(LAST_OPENED_OBJECT_KEY).apply()
|
||||
}
|
||||
|
||||
override suspend fun getNetworkMode(): NetworkModeConfig = networkModeProvider.get()
|
||||
override suspend fun setNetworkMode(modeConfig: NetworkModeConfig) {
|
||||
networkModeProvider.set(modeConfig)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val MNEMONIC_KEY = "mnemonic"
|
||||
const val LAST_OPENED_OBJECT_KEY = "last_opened_object"
|
||||
|
|
|
@ -5,6 +5,8 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
|||
import androidx.room.Room
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.anytypeio.anytype.persistence.db.AnytypeDatabase
|
||||
import com.anytypeio.anytype.persistence.networkmode.DefaultNetworkModeProvider
|
||||
import com.anytypeio.anytype.persistence.networkmode.NetworkModeProvider
|
||||
import com.anytypeio.anytype.persistence.repo.DefaultAuthCache
|
||||
import com.anytypeio.anytype.test_utils.MockDataFactory
|
||||
import kotlin.test.assertEquals
|
||||
|
@ -41,6 +43,8 @@ class DefaultAuthCacheTest {
|
|||
Context.MODE_PRIVATE
|
||||
)
|
||||
|
||||
lateinit var networkModeProvider: NetworkModeProvider
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
database.close()
|
||||
|
@ -51,10 +55,15 @@ class DefaultAuthCacheTest {
|
|||
|
||||
val givenMnemonic = MockDataFactory.randomString()
|
||||
|
||||
val networkModeProvider = DefaultNetworkModeProvider(
|
||||
sharedPreferences = defaultPrefs
|
||||
)
|
||||
|
||||
val cache = DefaultAuthCache(
|
||||
db = database,
|
||||
encryptedPrefs = encryptedPrefs,
|
||||
defaultPrefs = defaultPrefs
|
||||
defaultPrefs = defaultPrefs,
|
||||
networkModeProvider = networkModeProvider
|
||||
)
|
||||
|
||||
cache.saveMnemonic(mnemonic = givenMnemonic)
|
||||
|
|
|
@ -238,7 +238,7 @@ import com.anytypeio.anytype.presentation.templates.ObjectTypeTemplatesContainer
|
|||
import com.anytypeio.anytype.presentation.util.CopyFileStatus
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.OnCopyFileToCacheAction
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheStatus
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -6221,14 +6221,14 @@ class EditorViewModel(
|
|||
copyFileToCache.cancel()
|
||||
}
|
||||
|
||||
private val copyFileListener = object : OnCopyFileToCacheAction {
|
||||
private val copyFileListener = object : CopyFileToCacheStatus {
|
||||
override fun onCopyFileStart() {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Started)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCopyFileResult(result: String?) {
|
||||
override fun onCopyFileResult(result: String?, fileName: String?) {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Completed(result))
|
||||
}
|
||||
|
|
|
@ -85,36 +85,33 @@ class OnboardingSetProfileNameViewModel @Inject constructor(
|
|||
|
||||
private fun proceedWithCreatingAccount(name: String) {
|
||||
val startTime = System.currentTimeMillis()
|
||||
createAccount.invoke(
|
||||
scope = viewModelScope,
|
||||
params = CreateAccount.Params(
|
||||
name = name,
|
||||
avatarPath = null,
|
||||
iconGradientValue = spaceGradientProvider.randomId()
|
||||
)
|
||||
) { result ->
|
||||
result.either(
|
||||
fnL = { error ->
|
||||
val params = CreateAccount.Params(
|
||||
name = name,
|
||||
avatarPath = null,
|
||||
iconGradientValue = spaceGradientProvider.randomId()
|
||||
)
|
||||
viewModelScope.launch {
|
||||
createAccount.async(params = params).fold(
|
||||
onFailure = { error ->
|
||||
Timber.d("Error while creating account: ${error.message ?: "Unknown error"}").also {
|
||||
when(error) {
|
||||
CreateAccountException.NetworkError -> {
|
||||
sendToast(
|
||||
"Failed to create your account due to a network error: ${error.message ?: "Unknown error"}"
|
||||
)
|
||||
}
|
||||
CreateAccountException.OfflineDevice -> {
|
||||
sendToast("Your device seems to be offline. Please, check your connection and try again.")
|
||||
}
|
||||
else -> {
|
||||
sendToast("Error while creating an account: ${error.message ?: "Unknown error"}")
|
||||
when (error) {
|
||||
CreateAccountException.NetworkError -> {
|
||||
sendToast(
|
||||
"Failed to create your account due to a network error: ${error.message ?: "Unknown error"}"
|
||||
)
|
||||
}
|
||||
CreateAccountException.OfflineDevice -> {
|
||||
sendToast("Your device seems to be offline. Please, check your connection and try again.")
|
||||
}
|
||||
else -> {
|
||||
sendToast("Error: ${error.message ?: "Unknown error"}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
state.value = ScreenState.Idle
|
||||
},
|
||||
fnR = {
|
||||
viewModelScope.launch {
|
||||
analytics.sendEvent(eventName = EventsDictionary.createSpace)
|
||||
}
|
||||
onSuccess = {
|
||||
analytics.sendEvent(eventName = EventsDictionary.createSpace)
|
||||
createAccountAnalytics(startTime)
|
||||
val config = configStorage.getOrNull()
|
||||
if (config != null) {
|
||||
|
|
|
@ -78,18 +78,17 @@ class OnboardingVoidViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
@Deprecated("To be deleted")
|
||||
private fun proceedWithCreatingAccount() {
|
||||
val startTime = System.currentTimeMillis()
|
||||
createAccount.invoke(
|
||||
scope = viewModelScope,
|
||||
params = CreateAccount.Params(
|
||||
name = "",
|
||||
avatarPath = null,
|
||||
iconGradientValue = spaceGradientProvider.randomId()
|
||||
)
|
||||
) { result ->
|
||||
result.either(
|
||||
fnL = { error ->
|
||||
val params = CreateAccount.Params(
|
||||
name = "",
|
||||
avatarPath = null,
|
||||
iconGradientValue = spaceGradientProvider.randomId()
|
||||
)
|
||||
viewModelScope.launch {
|
||||
createAccount.async(params = params).fold(
|
||||
onFailure = { error ->
|
||||
Timber.d("Error while creating account: ${error.message ?: "Unknown error"}").also {
|
||||
when(error) {
|
||||
CreateAccountException.NetworkError -> {
|
||||
|
@ -101,12 +100,13 @@ class OnboardingVoidViewModel @Inject constructor(
|
|||
sendToast("Your device seems to be offline. Please, check your connection and try again.")
|
||||
}
|
||||
else -> {
|
||||
sendToast("Error while creating an account: ${error.message ?: "Unknown error"}")
|
||||
sendToast("Error: ${error.message ?: "Unknown error"}")
|
||||
}
|
||||
}
|
||||
}
|
||||
state.value = ScreenState.Idle
|
||||
},
|
||||
fnR = {
|
||||
onSuccess = {
|
||||
createAccountAnalytics(startTime)
|
||||
val config = configStorage.getOrNull()
|
||||
if (config != null) {
|
||||
|
@ -115,7 +115,8 @@ class OnboardingVoidViewModel @Inject constructor(
|
|||
objectTypesSubscriptionManager.onStart()
|
||||
proceedWithSettingUpMobileUseCase(config.space)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvide
|
|||
import com.anytypeio.anytype.presentation.util.CopyFileStatus
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.OnCopyFileToCacheAction
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheStatus
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
@ -569,14 +569,14 @@ abstract class RelationValueBaseViewModel(
|
|||
copyFileToCache.cancel()
|
||||
}
|
||||
|
||||
private val copyFileListener = object : OnCopyFileToCacheAction {
|
||||
private val copyFileListener = object : CopyFileToCacheStatus {
|
||||
override fun onCopyFileStart() {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Started)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCopyFileResult(result: String?) {
|
||||
override fun onCopyFileResult(result: String?, fileName: String?) {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Completed(result))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package com.anytypeio.anytype.presentation.settings
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
import com.anytypeio.anytype.core_models.NetworkMode
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConfig
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_CUSTOM
|
||||
import com.anytypeio.anytype.core_models.NetworkModeConstants.NETWORK_MODE_LOCAL
|
||||
import com.anytypeio.anytype.domain.base.fold
|
||||
import com.anytypeio.anytype.domain.networkmode.GetNetworkMode
|
||||
import com.anytypeio.anytype.domain.networkmode.SetNetworkMode
|
||||
import com.anytypeio.anytype.presentation.editor.picker.PickerListener
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheStatus
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
||||
class PreferencesViewModel(
|
||||
private val copyFileToCache: CopyFileToCacheDirectory,
|
||||
private val getNetworkMode: GetNetworkMode,
|
||||
private val setNetworkMode: SetNetworkMode
|
||||
) : ViewModel(), PickerListener {
|
||||
|
||||
val networkModeState = MutableStateFlow(NetworkModeConfig(NetworkMode.DEFAULT, "", ""))
|
||||
|
||||
fun onStart() {
|
||||
Timber.d("onStart")
|
||||
viewModelScope.launch {
|
||||
getNetworkMode.async(Unit).fold(
|
||||
onSuccess = {
|
||||
networkModeState.value = it
|
||||
Timber.d("Successfully get network mode on Start: $it")
|
||||
},
|
||||
onFailure = {
|
||||
Timber.e(it, "Failed to get network mode")
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun proceedWithNetworkMode(mode: String?) {
|
||||
viewModelScope.launch {
|
||||
val config = when (mode) {
|
||||
NETWORK_MODE_LOCAL -> NetworkModeConfig(NetworkMode.LOCAL)
|
||||
NETWORK_MODE_CUSTOM -> NetworkModeConfig(NetworkMode.CUSTOM)
|
||||
else -> NetworkModeConfig()
|
||||
}
|
||||
networkModeState.value = config
|
||||
setNetworkMode.async(SetNetworkMode.Params(config)).fold(
|
||||
onSuccess = { Timber.d("Successfully update network mode with config:$config") },
|
||||
onFailure = { Timber.e(it, "Failed to set network mode") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun proceedWithConfigFiles(userFilePath: String?, storedFilePath: String?) {
|
||||
viewModelScope.launch {
|
||||
val config = NetworkModeConfig(
|
||||
networkMode = NetworkMode.CUSTOM,
|
||||
userFilePath = userFilePath,
|
||||
storedFilePath = storedFilePath
|
||||
)
|
||||
val params = SetNetworkMode.Params(config)
|
||||
setNetworkMode.async(params).fold(
|
||||
onSuccess = {
|
||||
networkModeState.value = config
|
||||
Timber.d("Successfully update network mode with config:$config")
|
||||
},
|
||||
onFailure = { Timber.e(it, "Failed to set network mode") }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStartCopyFileToCacheDir(uri: Uri) {
|
||||
Timber.d("onStartCopyFileToCacheDir: $uri")
|
||||
copyFileToCache.execute(
|
||||
uri = uri,
|
||||
scope = viewModelScope,
|
||||
listener = copyFileListener
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCancelCopyFileToCacheDir() {
|
||||
copyFileToCache.cancel()
|
||||
}
|
||||
|
||||
override fun onPickedDocImageFromDevice(ctx: Id, path: String) {}
|
||||
override fun onProceedWithFilePath(filePath: String?) {}
|
||||
|
||||
private val copyFileListener = object : CopyFileToCacheStatus {
|
||||
override fun onCopyFileStart() {}
|
||||
override fun onCopyFileError(msg: String) {}
|
||||
|
||||
override fun onCopyFileResult(result: String?, fileName: String?) {
|
||||
proceedWithConfigFiles(
|
||||
userFilePath = fileName,
|
||||
storedFilePath = result
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val copyFileToCacheDirectory: CopyFileToCacheDirectory,
|
||||
private val getNetworkMode: GetNetworkMode,
|
||||
private val setNetworkMode: SetNetworkMode
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>
|
||||
): T = PreferencesViewModel(
|
||||
copyFileToCache = copyFileToCacheDirectory,
|
||||
getNetworkMode = getNetworkMode,
|
||||
setNetworkMode = setNetworkMode
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ package com.anytypeio.anytype.presentation.util
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import com.anytypeio.anytype.core_utils.ext.msg
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -14,17 +15,49 @@ import java.io.File
|
|||
import java.io.FileOutputStream
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
/**
|
||||
* Interface defining the contract for copying files to a cache directory.
|
||||
*/
|
||||
interface CopyFileToCacheDirectory {
|
||||
|
||||
fun execute(uri: Uri, scope: CoroutineScope, listener: OnCopyFileToCacheAction)
|
||||
/**
|
||||
* Executes the file copying operation for the given [uri].
|
||||
*
|
||||
* @param uri The URI of the file to be copied.
|
||||
* @param scope The [CoroutineScope] used for the asynchronous operation.
|
||||
* @param listener The [CopyFileToCacheStatus] listener to handle events during the operation.
|
||||
*/
|
||||
fun execute(uri: Uri, scope: CoroutineScope, listener: CopyFileToCacheStatus)
|
||||
|
||||
/**
|
||||
* Cancels the ongoing file copying operation.
|
||||
*/
|
||||
fun cancel()
|
||||
|
||||
/**
|
||||
* Checks if the file copying operation is currently active.
|
||||
*
|
||||
* @return `true` if the operation is active, `false` otherwise.
|
||||
*/
|
||||
fun isActive(): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener interface to handle events during the file copying operation.
|
||||
*/
|
||||
interface CopyFileToCacheStatus {
|
||||
fun onCopyFileStart()
|
||||
fun onCopyFileResult(result: String?, fileName: String? = null)
|
||||
fun onCopyFileError(msg: String)
|
||||
}
|
||||
|
||||
const val SCHEME_CONTENT = "content"
|
||||
const val CHAR_SLASH = '/'
|
||||
const val TEMPORARY_DIRECTORY_NAME = "TemporaryFiles"
|
||||
|
||||
/**
|
||||
* Represents the status of a file copying operation.
|
||||
*/
|
||||
sealed class CopyFileStatus {
|
||||
data class Error(val msg: String) : CopyFileStatus()
|
||||
object Started : CopyFileStatus()
|
||||
|
@ -42,7 +75,7 @@ class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirecto
|
|||
|
||||
override fun isActive(): Boolean = job?.isActive == true
|
||||
|
||||
override fun execute(uri: Uri, scope: CoroutineScope, listener: OnCopyFileToCacheAction) {
|
||||
override fun execute(uri: Uri, scope: CoroutineScope, listener: CopyFileToCacheStatus) {
|
||||
getNewPathInCacheDir(
|
||||
uri = uri,
|
||||
scope = scope,
|
||||
|
@ -58,7 +91,7 @@ class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirecto
|
|||
private fun getNewPathInCacheDir(
|
||||
uri: Uri,
|
||||
scope: CoroutineScope,
|
||||
listener: OnCopyFileToCacheAction
|
||||
listener: CopyFileToCacheStatus
|
||||
) {
|
||||
var path: String? = null
|
||||
job = scope.launch {
|
||||
|
@ -68,7 +101,7 @@ class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirecto
|
|||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while getNewPathInCacheDir")
|
||||
listener.onCopyFileError(e.localizedMessage ?: "Unknown error")
|
||||
listener.onCopyFileError(e.msg())
|
||||
} finally {
|
||||
if (scope.isActive) {
|
||||
listener.onCopyFileResult(path)
|
||||
|
@ -77,9 +110,9 @@ class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirecto
|
|||
}
|
||||
}
|
||||
|
||||
fun copyFileToCacheDir(
|
||||
private fun copyFileToCacheDir(
|
||||
uri: Uri,
|
||||
listener: OnCopyFileToCacheAction
|
||||
listener: CopyFileToCacheStatus
|
||||
): String? {
|
||||
var newFile: File? = null
|
||||
mContext?.get()?.let { context: Context ->
|
||||
|
@ -142,6 +175,123 @@ class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirecto
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Network mode-specific implementation of the [CopyFileToCacheDirectory] interface.
|
||||
*
|
||||
* @param context The application context.
|
||||
*/
|
||||
class NetworkModeCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirectory {
|
||||
|
||||
private var mContext: WeakReference<Context>? = null
|
||||
private var job: Job? = null
|
||||
|
||||
init {
|
||||
mContext = WeakReference(context)
|
||||
}
|
||||
|
||||
override fun isActive(): Boolean = job?.isActive == true
|
||||
|
||||
override fun execute(uri: Uri, scope: CoroutineScope, listener: CopyFileToCacheStatus) {
|
||||
getNewPathInCacheDir(
|
||||
uri = uri,
|
||||
scope = scope,
|
||||
listener = listener,
|
||||
)
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
job?.cancel()
|
||||
}
|
||||
|
||||
private fun getNewPathInCacheDir(
|
||||
uri: Uri,
|
||||
scope: CoroutineScope,
|
||||
listener: CopyFileToCacheStatus
|
||||
) {
|
||||
var path: String? = null
|
||||
var fileName: String? = null
|
||||
job = scope.launch {
|
||||
try {
|
||||
withContext(Dispatchers.IO) {
|
||||
val pair = copyFileToCacheDir(uri, listener)
|
||||
path = pair.first
|
||||
fileName = pair.second
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while getNewPathInCacheDir")
|
||||
listener.onCopyFileError(e.msg())
|
||||
} finally {
|
||||
if (scope.isActive) {
|
||||
listener.onCopyFileResult(path, fileName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyFileToCacheDir(
|
||||
uri: Uri,
|
||||
listener: CopyFileToCacheStatus
|
||||
): Pair<String?, String?> {
|
||||
var newFile: File? = null
|
||||
mContext?.get()?.let { context: Context ->
|
||||
|
||||
val fileName = getFileName(context, uri)
|
||||
|
||||
val cacheDir = context.getExternalCustomNetworkDirTemp()
|
||||
if (cacheDir != null && !cacheDir.exists()) {
|
||||
cacheDir.mkdirs()
|
||||
}
|
||||
try {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
inputStream?.use { input ->
|
||||
newFile = File(cacheDir?.path + "/" + CONFIG_FILE_NAME);
|
||||
listener.onCopyFileStart()
|
||||
Timber.d("Start copy file to cache : ${newFile?.path}")
|
||||
FileOutputStream(newFile).use { output ->
|
||||
val buffer = ByteArray(1024)
|
||||
var read: Int = input.read(buffer)
|
||||
while (read != -1) {
|
||||
output.write(buffer, 0, read)
|
||||
read = input.read(buffer)
|
||||
}
|
||||
}
|
||||
return Pair(newFile?.path, fileName)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
val deleteResult = newFile?.deleteRecursively()
|
||||
Timber.d("Get exception while copying file, deleteRecursively success: $deleteResult")
|
||||
Timber.e(e, "Error while coping file")
|
||||
}
|
||||
}
|
||||
return Pair(null, null)
|
||||
}
|
||||
|
||||
private fun getFileName(context: Context, uri: Uri): String? {
|
||||
var result: String? = null
|
||||
if (uri.scheme == SCHEME_CONTENT) {
|
||||
context.contentResolver.query(
|
||||
uri,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)?.use { cursor ->
|
||||
if (cursor.moveToFirst()) {
|
||||
val index = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||
if (index != -1) {
|
||||
result = cursor.getString(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CONFIG_FILE_NAME = "configCustom.txt"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the /storage/emulated/0/Android/data/package/files/$TEMPORARY_DIRECTORY_NAME folder.
|
||||
*/
|
||||
|
@ -160,8 +310,7 @@ private fun Context.deleteTemporaryFolder() {
|
|||
*/
|
||||
private fun Context.getExternalFilesDirTemp(): File? = getExternalFilesDir(TEMPORARY_DIRECTORY_NAME)
|
||||
|
||||
interface OnCopyFileToCacheAction {
|
||||
fun onCopyFileStart()
|
||||
fun onCopyFileResult(result: String?)
|
||||
fun onCopyFileError(msg: String)
|
||||
}
|
||||
/**
|
||||
* Return /storage/emulated/0/Android/data/io.anytype.app/files/networkModeConfig directory
|
||||
*/
|
||||
private fun Context.getExternalCustomNetworkDirTemp(): File? = getExternalFilesDir("networkModeConfig")
|
Loading…
Add table
Add a link
Reference in a new issue