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

DROID-1451 Space | Enhancement | Move 'Space debug' button to the 'Space' screen + Refact (#197)

This commit is contained in:
Evgenii Kozlov 2023-07-13 19:45:12 +02:00 committed by uburoiubu
parent 073d92e44a
commit 0b1936a78f
No known key found for this signature in database
GPG key ID: C8FB80E0A595FBB6
16 changed files with 201 additions and 25 deletions

View file

@ -2,21 +2,32 @@ package com.anytypeio.anytype.di.feature.settings
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.core_utils.di.scope.PerScreen
import com.anytypeio.anytype.device.share.debug.DebugSpaceDeviceFileContentSaver
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.debugging.DebugSpaceContentSaver
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.domain.search.SubscriptionEventChannel
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
import com.anytypeio.anytype.providers.DefaultUriFileProvider
import com.anytypeio.anytype.ui.settings.MainSettingFragment
import dagger.Binds
import dagger.Module
import dagger.Provides
import dagger.Subcomponent
@Subcomponent(modules = [MainSettingsModule::class])
@Subcomponent(
modules = [
MainSettingsModule::class,
MainSettingsModule.Bindings::class
]
)
@PerScreen
interface MainSettingsSubComponent {
@ -70,13 +81,30 @@ object MainSettingsModule {
configStorage: ConfigStorage,
urlBuilder: UrlBuilder,
setObjectDetails: SetObjectDetails,
spaceGradientProvider: SpaceGradientProvider
spaceGradientProvider: SpaceGradientProvider,
debugSpaceShareDownloader: DebugSpaceShareDownloader
): MainSettingsViewModel.Factory = MainSettingsViewModel.Factory(
analytics,
storelessSubscriptionContainer,
configStorage,
urlBuilder,
setObjectDetails,
spaceGradientProvider
analytics = analytics,
storelessSubscriptionContainer = storelessSubscriptionContainer,
configStorage = configStorage,
urlBuilder = urlBuilder,
setObjectDetails = setObjectDetails,
spaceGradientProvider = spaceGradientProvider,
debugSpaceShareDownloader = debugSpaceShareDownloader
)
@Module
interface Bindings {
@PerScreen
@Binds
fun bindUriFileProvider(
defaultProvider: DefaultUriFileProvider
): UriFileProvider
@PerScreen
@Binds
fun bindSpaceDebugDeviceSharer(
saver: DebugSpaceDeviceFileContentSaver
): DebugSpaceContentSaver
}
}

View file

@ -16,6 +16,7 @@ import androidx.lifecycle.repeatOnLifecycle
import com.anytypeio.anytype.R
import com.anytypeio.anytype.core_ui.common.ComposeDialogView
import com.anytypeio.anytype.core_utils.ext.setupBottomSheetBehavior
import com.anytypeio.anytype.core_utils.ext.shareFile
import com.anytypeio.anytype.core_utils.ext.toast
import com.anytypeio.anytype.core_utils.tools.FeatureToggles
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
@ -23,12 +24,15 @@ import com.anytypeio.anytype.di.common.componentManager
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Command
import com.anytypeio.anytype.presentation.settings.MainSettingsViewModel.Event
import com.anytypeio.anytype.presentation.util.downloader.UriFileProvider
import com.anytypeio.anytype.ui.editor.modals.IconPickerFragmentBase.Companion.ARG_CONTEXT_ID_KEY
import com.anytypeio.anytype.ui.sets.ARG_SHOW_REMOVE_BUTTON
import com.anytypeio.anytype.ui.settings.system.SettingsActivity
import com.anytypeio.anytype.ui_settings.main.MainSettingScreen
import kotlinx.coroutines.launch
import java.io.File
import javax.inject.Inject
import kotlinx.coroutines.launch
import timber.log.Timber
class MainSettingFragment : BaseBottomSheetComposeFragment() {
@ -38,6 +42,9 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
@Inject
lateinit var featureToggles: FeatureToggles
@Inject
lateinit var uriFileProvider: UriFileProvider
private val vm by viewModels<MainSettingsViewModel> { factory }
private val onProfileClicked = {
@ -135,13 +142,23 @@ class MainSettingFragment : BaseBottomSheetComposeFragment() {
)
)
}
Command.OpenFilesStorageScreen -> {
is Command.OpenFilesStorageScreen -> {
safeNavigate(R.id.actionOpenFilesStorageScreen)
}
is Command.Toast -> {
toast(msg = command.msg)
}
is Command.ShareSpaceDebug -> {
try {
shareFile(
uriFileProvider.getUriForFile(File(command.path))
)
} catch (e: Exception) {
Timber.e(e, "Error while sharing space debug").also {
toast("Error while sharing space debug. Please try again later.")
}
}
}
}
}

View file

@ -79,7 +79,7 @@ class ProfileFragment : BaseBottomSheetComposeFragment() {
onSpaceDebugClicked = { throttle { vm.onSpaceDebugClicked() } },
isLogoutInProgress = vm.isLoggingOut.collectAsState().value,
isDebugSpaceReportInProgress = vm.isDebugSpaceReportInProgress.collectAsState().value,
isShowDebug = true,
isShowDebug = false,
onNameChange = { vm.onNameChange(it) },
onProfileIconClick = { proceedWithIconClick() },
account = vm.profileData.collectAsStateWithLifecycle().value,

View file

@ -6,6 +6,7 @@ typealias Url = String
typealias Hash = String
typealias Struct = Map<Id, Any?>
typealias Name = String
typealias Filepath = String
typealias Document = List<Block>

View file

@ -48,9 +48,9 @@ import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetComposeFragment
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetDialog
import timber.log.Timber
import java.text.SimpleDateFormat
import java.util.*
import timber.log.Timber
fun Context.dimen(res: Int): Float {
return resources

View file

@ -3,8 +3,7 @@ package com.anytypeio.anytype.presentation.util.downloader
import android.net.Uri
import java.io.File
// TODO move to device module
interface UriFileProvider {
fun getUriForFile(file: File): Uri
}

View file

@ -0,0 +1,5 @@
package com.anytypeio.anytype.data.auth.other
import com.anytypeio.anytype.domain.debugging.DebugSpaceContentSaver
interface DebugSpaceDeviceContentSaver : DebugSpaceContentSaver

View file

@ -6,6 +6,7 @@ plugins {
dependencies {
implementation project(':data')
implementation project(':domain')
implementation libs.kotlin
implementation libs.coroutinesAndroid
@ -17,4 +18,6 @@ dependencies {
testImplementation libs.kotlinTest
testImplementation libs.androidXTestCore
testImplementation libs.robolectric
compileOnly libs.javaxInject
}

View file

@ -0,0 +1,70 @@
package com.anytypeio.anytype.device.share.debug
import android.content.Context
import com.anytypeio.anytype.data.auth.other.DebugSpaceDeviceContentSaver
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
import javax.inject.Inject
class DebugSpaceDeviceFileContentSaver @Inject constructor(
private val context: Context
) : DebugSpaceDeviceContentSaver {
@Throws(IOException::class)
override fun save(content: String): File {
val cacheDir = context.cacheDir
require(cacheDir != null) { "Impossible to cache files!" }
val filename = getFileName()
// Creating folder
val downloadFolder = File("${cacheDir.path}/$FOLDER_NAME/").apply {
mkdirs()
}
val resultFilePath = "${cacheDir.path}/$FOLDER_NAME/${filename}"
val resultFile = File(resultFilePath)
// Writing content
val tempFileFolderPath = "${downloadFolder.absolutePath}/$TEMP_FOLDER_NAME"
val tempDir = File(tempFileFolderPath)
if (tempDir.exists()) tempDir.deleteRecursively()
tempDir.mkdirs()
val tempResult = File(tempFileFolderPath, filename)
FileOutputStream(tempResult).use { stream ->
stream.write(content.toByteArray())
}
tempResult.renameTo(resultFile)
// Clearing
tempDir.deleteRecursively()
// Sending file
return resultFile
}
private fun getFileName(): String {
val date = Calendar.getInstance().time
val dateFormat = SimpleDateFormat(DATE_FORMAT, Locale.getDefault())
val formattedDate = dateFormat.format(date)
return "DebugSpace$formattedDate.txt"
}
companion object {
const val FOLDER_NAME = "debug_space"
const val TEMP_FOLDER_NAME = "tmp"
const val DATE_FORMAT = "dd-MM-yyyy-HH:mm:ss"
}
}

View file

@ -3,8 +3,9 @@ package com.anytypeio.anytype.domain.debugging
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import com.anytypeio.anytype.domain.block.repo.BlockRepository
import javax.inject.Inject
class DebugSpace(
class DebugSpace @Inject constructor(
private val repo: BlockRepository,
dispatchers: AppCoroutineDispatchers
) : ResultInteractor<Unit, String>(dispatchers.io) {

View file

@ -0,0 +1,7 @@
package com.anytypeio.anytype.domain.debugging
import java.io.File
interface DebugSpaceContentSaver {
fun save(content: String) : File
}

View file

@ -0,0 +1,18 @@
package com.anytypeio.anytype.domain.debugging
import com.anytypeio.anytype.core_models.Filepath
import com.anytypeio.anytype.domain.base.AppCoroutineDispatchers
import com.anytypeio.anytype.domain.base.ResultInteractor
import javax.inject.Inject
class DebugSpaceShareDownloader @Inject constructor(
private val debugSpace: DebugSpace,
private val debugSpaceContentSaver: DebugSpaceContentSaver,
dispatchers: AppCoroutineDispatchers,
) : ResultInteractor<Unit, Filepath>(dispatchers.io) {
override suspend fun doWork(params: Unit): Filepath {
val content = debugSpace.run(Unit)
val file = debugSpaceContentSaver.save(content = content)
return file.path
}
}

View file

@ -1,22 +1,22 @@
package com.anytypeio.anytype.presentation.settings
import android.os.Build
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.anytypeio.anytype.analytics.base.Analytics
import com.anytypeio.anytype.analytics.base.EventsDictionary
import com.anytypeio.anytype.analytics.base.sendEvent
import com.anytypeio.anytype.core_models.Filepath
import com.anytypeio.anytype.core_models.Id
import com.anytypeio.anytype.core_models.Relations
import com.anytypeio.anytype.core_utils.ext.throttleFirst
import com.anytypeio.anytype.domain.base.fold
import com.anytypeio.anytype.domain.config.ConfigStorage
import com.anytypeio.anytype.domain.debugging.DebugSpaceShareDownloader
import com.anytypeio.anytype.domain.library.StoreSearchByIdsParams
import com.anytypeio.anytype.domain.library.StorelessSubscriptionContainer
import com.anytypeio.anytype.domain.misc.UrlBuilder
import com.anytypeio.anytype.domain.`object`.SetObjectDetails
import com.anytypeio.anytype.presentation.BuildConfig
import com.anytypeio.anytype.presentation.profile.ProfileIconView
import com.anytypeio.anytype.presentation.profile.profileIcon
import com.anytypeio.anytype.presentation.spaces.SpaceGradientProvider
@ -37,7 +37,8 @@ class MainSettingsViewModel(
private val configStorage: ConfigStorage,
private val urlBuilder: UrlBuilder,
private val setObjectDetails: SetObjectDetails,
private val spaceGradientProvider: SpaceGradientProvider
private val spaceGradientProvider: SpaceGradientProvider,
private val debugSpaceShareDownloader: DebugSpaceShareDownloader
) : ViewModel() {
val events = MutableSharedFlow<Event>(replay = 0)
@ -99,7 +100,9 @@ class MainSettingsViewModel(
Event.OnProfileClicked -> commands.emit(Command.OpenProfileScreen)
Event.OnAppearanceClicked -> commands.emit(Command.OpenAppearanceScreen)
Event.OnPersonalizationClicked -> commands.emit(Command.OpenPersonalizationScreen)
Event.OnDebugClicked -> commands.emit(Command.OpenDebugScreen)
Event.OnDebugClicked -> {
proceedWithSpaceDebug()
}
Event.OnSpaceImageClicked -> commands.emit(
Command.OpenSpaceImageSet(
configStorage.get().workspace
@ -111,6 +114,22 @@ class MainSettingsViewModel(
}
}
private fun proceedWithSpaceDebug() {
viewModelScope.launch {
debugSpaceShareDownloader
.stream(Unit)
.collect { result ->
result.fold(
onSuccess = { path ->
commands.emit(
Command.ShareSpaceDebug(path)
)
}
)
}
}
}
private fun dispatchAnalyticEvent(event: Event) {
when (event) {
Event.OnAboutClicked -> {
@ -178,7 +197,8 @@ class MainSettingsViewModel(
private val configStorage: ConfigStorage,
private val urlBuilder: UrlBuilder,
private val setObjectDetails: SetObjectDetails,
private val spaceGradientProvider: SpaceGradientProvider
private val spaceGradientProvider: SpaceGradientProvider,
private val debugSpaceShareDownloader: DebugSpaceShareDownloader
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
@ -189,7 +209,8 @@ class MainSettingsViewModel(
configStorage = configStorage,
urlBuilder = urlBuilder,
setObjectDetails = setObjectDetails,
spaceGradientProvider = spaceGradientProvider
spaceGradientProvider = spaceGradientProvider,
debugSpaceShareDownloader = debugSpaceShareDownloader
) as T
}
@ -212,6 +233,7 @@ class MainSettingsViewModel(
class OpenSpaceImageSet(val id: Id) : Command()
object OpenFilesStorageScreen : Command()
data class Toast(val msg: String) : Command()
data class ShareSpaceDebug(val path: Filepath): Command()
}
sealed class WorkspaceAndAccount {
@ -230,10 +252,7 @@ class MainSettingsViewModel(
val name: String,
val icon: ProfileIconView,
)
}
}
const val SPACE_STORAGE_SUBSCRIPTION_ID = "settings_space_storage_subscription"

View file

@ -6,6 +6,7 @@ import com.anytypeio.anytype.domain.base.ResultInteractor
import java.io.File
import java.io.FileOutputStream
@Deprecated("To be deleted")
class DebugSpaceFileContentSaver(
private val context: Context,
dispatchers: AppCoroutineDispatchers,

View file

@ -9,6 +9,7 @@ import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Locale
@Deprecated("To be deleted")
class DebugSpaceShareDownloader(
private val debugSpace: DebugSpace,
private val fileSaver: DebugSpaceFileContentSaver,

View file

@ -114,6 +114,12 @@ private fun Settings(
onClick = onFileStorageClick
)
Divider(paddingStart = 60.dp)
Option(
image = R.drawable.ic_debug,
text = stringResource(R.string.space_debug),
onClick = onDebugClicked
)
Divider(paddingStart = 60.dp)
Option(
image = R.drawable.ic_about,
text = stringResource(R.string.about),