mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Tech | API 31 (#2031)
* Tech | API 30, Local Network Address (#2004) * 30 api + java 11 + scoped storage * update network address handler * fix * fix * put back permission * fix * fix * fix * ci * set tests off Co-authored-by: konstantiniiv <ki@anytype.io> * Fix | JVM version (#2033) * Tech | Update to API 31 (#2032) * update to API 31 * fix Co-authored-by: konstantiniiv <ki@anytype.io> * Fix | Network address handler (#2034) * Tech | Editor, update permissions (#2039) * add permission * remove legacy * fix permissions * fix * fix * add ext fun Co-authored-by: konstantiniiv <ki@anytype.io> * Tech | Permissions in Relations Value (#2041) * add permission * remove legacy * fix permissions * fix * fix * add ext fun * permissions in relations Co-authored-by: konstantiniiv <ki@anytype.io> * Tech | Update file logic to SAF (#2048) * update file logic * fixes * fix * fix * fixes * fixes * fix * fix * rename * fix * fix * style * fix * delete legacy test * add kdoc Co-authored-by: konstantiniiv <ki@anytype.io> * Tech | Add signing (#2047) * add signing * rename * fixes * debug signing Co-authored-by: E. Kozlov <ubuphobos@gmail.com> Co-authored-by: konstantiniiv <ki@anytype.io> * Tech | API 31, files refactoring (#2053) * refactoring * fix * fix * fix * fix Co-authored-by: konstantiniiv <ki@anytype.io> * put back create page * fix tests * temporary turn off signing * fix viewmodel nullable * ci off Co-authored-by: konstantiniiv <ki@anytype.io> Co-authored-by: E. Kozlov <ubuphobos@gmail.com>
This commit is contained in:
parent
b5ff81394c
commit
312289768a
100 changed files with 1022 additions and 492 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -13,3 +13,4 @@
|
|||
.externalNativeBuild
|
||||
ktlint
|
||||
.idea/*.xml
|
||||
signing.properties
|
||||
|
|
|
@ -25,8 +25,8 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,10 @@ def apikeyPropertiesFile = rootProject.file("apikeys.properties")
|
|||
def apikeyProperties = new Properties()
|
||||
apikeyProperties.load(new FileInputStream(apikeyPropertiesFile))
|
||||
|
||||
//def keystorePropertiesFile = rootProject.file("signing.properties")
|
||||
//def keystoreProperties = new Properties()
|
||||
//keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
|
||||
|
||||
android {
|
||||
def config = rootProject.extensions.getByName("ext")
|
||||
|
||||
|
@ -26,15 +30,13 @@ android {
|
|||
versionName getBuildVersionName()
|
||||
testInstrumentationRunner config["test_runner"]
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
exclude 'LICENSE.txt'
|
||||
exclude 'META-INF/DEPENDENCIES'
|
||||
exclude 'META-INF/ASL2.0'
|
||||
exclude 'META-INF/NOTICE'
|
||||
exclude 'META-INF/LICENSE'
|
||||
resources {
|
||||
excludes += ['LICENSE.txt', 'META-INF/DEPENDENCIES', 'META-INF/ASL2.0', 'META-INF/NOTICE', 'META-INF/LICENSE']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lintOptions {
|
||||
quiet true
|
||||
abortOnError false
|
||||
|
@ -45,18 +47,39 @@ android {
|
|||
disable 'IconMissingDensityFolder' //For testing purpose. This is safe to remove.
|
||||
}
|
||||
|
||||
// signingConfigs {
|
||||
// //For proper signing, use debuggable false for a build.
|
||||
// release {
|
||||
// keyAlias keystoreProperties['RELEASE_KEY_ALIAS']
|
||||
// keyPassword keystoreProperties['RELEASE_KEY_PASSWORD']
|
||||
// storeFile file(keystoreProperties['RELEASE_STORE_FILE'])
|
||||
// storePassword keystoreProperties['RELEASE_STORE_PASSWORD']
|
||||
// v1SigningEnabled true
|
||||
// v2SigningEnabled true
|
||||
// }
|
||||
// debug {
|
||||
// keyAlias keystoreProperties['DEBUG_KEY_ALIAS']
|
||||
// keyPassword keystoreProperties['DEBUG_KEY_PASSWORD']
|
||||
// storeFile file(keystoreProperties['DEBUG_STORE_FILE'])
|
||||
// storePassword keystoreProperties['DEBUG_STORE_PASSWORD']
|
||||
// v1SigningEnabled true
|
||||
// v2SigningEnabled true
|
||||
// }
|
||||
// }
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
useProguard false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
buildConfigField("String", "AMPLITUDE_KEY", apikeyProperties['amplitude.release'])
|
||||
//signingConfig signingConfigs.release
|
||||
}
|
||||
|
||||
debug {
|
||||
applicationIdSuffix ".debug"
|
||||
debuggable true
|
||||
buildConfigField("String", "AMPLITUDE_KEY", apikeyProperties['amplitude.debug'])
|
||||
//signingConfig signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,12 +113,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
androidExtensions {
|
||||
|
@ -131,7 +154,6 @@ dependencies {
|
|||
//Compile time dependencies
|
||||
kapt applicationDependencies.daggerCompiler
|
||||
kapt applicationDependencies.glideCompiler
|
||||
kapt applicationDependencies.permissionDispCompiler
|
||||
compileOnly applicationDependencies.javaxAnnotation
|
||||
compileOnly applicationDependencies.javaxInject
|
||||
|
||||
|
@ -149,7 +171,6 @@ dependencies {
|
|||
implementation applicationDependencies.dagger
|
||||
implementation applicationDependencies.timber
|
||||
implementation applicationDependencies.gson
|
||||
implementation applicationDependencies.permissionDisp
|
||||
implementation applicationDependencies.pickT
|
||||
implementation applicationDependencies.emojiCompat
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
<activity
|
||||
android:name="com.anytypeio.anytype.ui.main.MainActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:exported="true"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<intent-filter android:label="filter">
|
||||
|
@ -43,6 +44,7 @@
|
|||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:screenOrientation="fullSensor"
|
||||
android:exported="false"
|
||||
tools:replace="screenOrientation" />
|
||||
</application>
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import com.anytypeio.anytype.di.common.ComponentManager
|
|||
import com.anytypeio.anytype.di.main.ContextModule
|
||||
import com.anytypeio.anytype.di.main.DaggerMainComponent
|
||||
import com.anytypeio.anytype.di.main.MainComponent
|
||||
import com.anytypeio.anytype.middleware.interactor.LocalNetworkAddressHandler
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -21,6 +22,9 @@ class AndroidApplication : Application() {
|
|||
@Inject
|
||||
lateinit var amplitudeTracker: AmplitudeTracker
|
||||
|
||||
@Inject
|
||||
lateinit var localNetworkAddressHandler: LocalNetworkAddressHandler
|
||||
|
||||
private val main: MainComponent by lazy {
|
||||
DaggerMainComponent
|
||||
.builder()
|
||||
|
@ -38,6 +42,7 @@ class AndroidApplication : Application() {
|
|||
setupAnalytics()
|
||||
setupEmojiCompat()
|
||||
setupTimber()
|
||||
setupLocalNetworkAddressHandler()
|
||||
}
|
||||
|
||||
private fun setupEmojiCompat() {
|
||||
|
@ -61,4 +66,8 @@ class AndroidApplication : Application() {
|
|||
private fun setupAnalytics() {
|
||||
Amplitude.getInstance().initialize(this, BuildConfig.AMPLITUDE_KEY)
|
||||
}
|
||||
|
||||
private fun setupLocalNetworkAddressHandler() {
|
||||
localNetworkAddressHandler.start()
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.analytics.base.Analytics
|
||||
import com.anytypeio.anytype.core_models.Block
|
||||
import com.anytypeio.anytype.core_models.Id
|
||||
|
@ -52,6 +53,8 @@ import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
|
|||
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
|
||||
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
|
||||
import com.anytypeio.anytype.presentation.relations.providers.*
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.providers.DefaultCoverImageHashProvider
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
|
@ -147,7 +150,8 @@ object EditorSessionModule {
|
|||
objectTypesProvider: ObjectTypesProvider,
|
||||
searchObjects: SearchObjects,
|
||||
getDefaultEditorType: GetDefaultEditorType,
|
||||
findObjectSetForType: FindObjectSetForType
|
||||
findObjectSetForType: FindObjectSetForType,
|
||||
copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
): EditorViewModelFactory = EditorViewModelFactory(
|
||||
openPage = openPage,
|
||||
closeObject = closePage,
|
||||
|
@ -173,7 +177,8 @@ object EditorSessionModule {
|
|||
searchObjects = searchObjects,
|
||||
getDefaultEditorType = getDefaultEditorType,
|
||||
findObjectSetForType = findObjectSetForType,
|
||||
createObjectSet = createObjectSet
|
||||
createObjectSet = createObjectSet,
|
||||
copyFileToCacheDirectory = copyFileToCacheDirectory
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
|
@ -752,4 +757,11 @@ object EditorUseCaseModule {
|
|||
fun provideCreateObjectSetUseCase(
|
||||
repo: BlockRepository
|
||||
): CreateObjectSet = CreateObjectSet(repo = repo)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerScreen
|
||||
fun provideCopyFileToCache(
|
||||
context: Context
|
||||
): CopyFileToCacheDirectory = DefaultCopyFileToCacheDirectory(context)
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.di.feature
|
||||
|
||||
import android.content.Context
|
||||
import com.anytypeio.anytype.core_models.Payload
|
||||
import com.anytypeio.anytype.core_utils.di.scope.PerModal
|
||||
import com.anytypeio.anytype.domain.`object`.ObjectTypesProvider
|
||||
|
@ -14,6 +15,8 @@ import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProv
|
|||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueDVViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.DefaultCopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.ui.relations.RelationValueDVFragment
|
||||
import com.anytypeio.anytype.ui.relations.RelationValueFragment
|
||||
|
@ -88,6 +91,13 @@ object ObjectRelationValueModule {
|
|||
@Module
|
||||
object ObjectSetObjectRelationValueModule {
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
fun provideCopyFileToCache(
|
||||
context: Context
|
||||
): CopyFileToCacheDirectory = DefaultCopyFileToCacheDirectory(context)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@PerModal
|
||||
|
@ -99,9 +109,9 @@ object ObjectSetObjectRelationValueModule {
|
|||
removeTagFromDataViewRecord: RemoveTagFromDataViewRecord,
|
||||
removeStatusFromDataViewRecord: RemoveStatusFromDataViewRecord,
|
||||
urlBuilder: UrlBuilder,
|
||||
dispatcher: Dispatcher<Payload>,
|
||||
updateDataViewRecord: UpdateDataViewRecord,
|
||||
addFileToRecord: AddFileToRecord
|
||||
addFileToRecord: AddFileToRecord,
|
||||
copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
): RelationValueDVViewModel.Factory = RelationValueDVViewModel.Factory(
|
||||
relations = relations,
|
||||
values = values,
|
||||
|
@ -110,9 +120,9 @@ object ObjectSetObjectRelationValueModule {
|
|||
removeTagFromRecord = removeTagFromDataViewRecord,
|
||||
removeStatusFromDataViewRecord = removeStatusFromDataViewRecord,
|
||||
urlBuilder = urlBuilder,
|
||||
dispatcher = dispatcher,
|
||||
updateDataViewRecord = updateDataViewRecord,
|
||||
addFileToRecord = addFileToRecord
|
||||
addFileToRecord = addFileToRecord,
|
||||
copyFileToCache = copyFileToCacheDirectory
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -130,7 +140,8 @@ object ObjectObjectRelationValueModule {
|
|||
urlBuilder: UrlBuilder,
|
||||
dispatcher: Dispatcher<Payload>,
|
||||
updateDetail: UpdateDetail,
|
||||
addFileToObject: AddFileToObject
|
||||
addFileToObject: AddFileToObject,
|
||||
copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
): RelationValueViewModel.Factory = RelationValueViewModel.Factory(
|
||||
relations = relations,
|
||||
values = values,
|
||||
|
@ -139,6 +150,7 @@ object ObjectObjectRelationValueModule {
|
|||
urlBuilder = urlBuilder,
|
||||
dispatcher = dispatcher,
|
||||
updateDetail = updateDetail,
|
||||
addFileToObject = addFileToObject
|
||||
addFileToObject = addFileToObject,
|
||||
copyFileToCache = copyFileToCacheDirectory
|
||||
)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package com.anytypeio.anytype.di.main
|
||||
|
||||
import com.anytypeio.anytype.middleware.interactor.LocalNetworkAddressHandler
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
class LocalNetworkAddressModule {
|
||||
|
||||
@Singleton
|
||||
@Provides
|
||||
fun provideHandler(): LocalNetworkAddressHandler = LocalNetworkAddressHandler()
|
||||
}
|
|
@ -17,7 +17,8 @@ import javax.inject.Singleton
|
|||
UtilModule::class,
|
||||
EmojiModule::class,
|
||||
ClipboardModule::class,
|
||||
AnalyticsModule::class
|
||||
AnalyticsModule::class,
|
||||
LocalNetworkAddressModule::class
|
||||
]
|
||||
)
|
||||
interface MainComponent {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.anytypeio.anytype.ui.editor
|
||||
|
||||
import android.Manifest
|
||||
import android.Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
import android.animation.ObjectAnimator
|
||||
import android.app.Activity
|
||||
import android.app.ProgressDialog
|
||||
|
@ -22,7 +22,7 @@ import android.widget.Button
|
|||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.activity.addCallback
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
|
@ -67,6 +67,8 @@ import com.anytypeio.anytype.core_ui.reactive.clicks
|
|||
import com.anytypeio.anytype.core_ui.tools.*
|
||||
import com.anytypeio.anytype.core_ui.widgets.text.TextInputWidget
|
||||
import com.anytypeio.anytype.core_utils.common.EventWrapper
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_FILE_SAF_CODE
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
|
||||
import com.anytypeio.anytype.core_utils.ext.*
|
||||
import com.anytypeio.anytype.core_utils.ext.PopupExtensions.calculateRectInWindow
|
||||
import com.anytypeio.anytype.di.common.componentManager
|
||||
|
@ -84,6 +86,7 @@ import com.anytypeio.anytype.presentation.editor.editor.sam.ScrollAndMoveTarget
|
|||
import com.anytypeio.anytype.presentation.editor.editor.sam.ScrollAndMoveTargetDescriptor
|
||||
import com.anytypeio.anytype.presentation.editor.markup.MarkupColorView
|
||||
import com.anytypeio.anytype.presentation.editor.model.EditorFooter
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileStatus
|
||||
import com.anytypeio.anytype.ui.alert.AlertUpdateAppFragment
|
||||
import com.anytypeio.anytype.ui.base.NavigationFragment
|
||||
import com.anytypeio.anytype.ui.editor.cover.SelectCoverObjectFragment
|
||||
|
@ -115,15 +118,12 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import permissions.dispatcher.*
|
||||
import timber.log.Timber
|
||||
import java.util.ArrayList
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.abs
|
||||
|
||||
const val REQUEST_FILE_CODE = 745
|
||||
|
||||
@RuntimePermissions
|
||||
open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
||||
open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
||||
OnFragmentInteractionListener,
|
||||
TurnIntoActionReceiver,
|
||||
SelectProgrammingLanguageReceiver,
|
||||
|
@ -349,76 +349,6 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_FILE_CODE -> {
|
||||
data?.data?.let {
|
||||
pickiT.getPath(it, Build.VERSION.SDK_INT)
|
||||
} ?: run {
|
||||
toast("Error while getting file")
|
||||
}
|
||||
}
|
||||
else -> toast("Unknown Request Code:$requestCode")
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
onRequestPermissionsResult(requestCode, grantResults)
|
||||
}
|
||||
|
||||
@NeedsPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
fun startDownload(id: String) {
|
||||
vm.startDownloadingFile(id)
|
||||
}
|
||||
|
||||
@NeedsPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun openGallery(type: String) {
|
||||
try {
|
||||
startActivityForResult(getVideoFileIntent(type), REQUEST_FILE_CODE)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to open gallery")
|
||||
}
|
||||
}
|
||||
|
||||
@OnShowRationale(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun showRationaleForReadExternalStoragePermission(request: PermissionRequest) {
|
||||
showRationaleDialog(R.string.permission_read_rationale, request)
|
||||
}
|
||||
|
||||
@OnPermissionDenied(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun onReadExternalStoragePermissionDenied() {
|
||||
toast(getString(R.string.permission_read_denied))
|
||||
}
|
||||
|
||||
@OnNeverAskAgain(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun onReadExternalStoragePermissionNeverAskAgain() {
|
||||
toast(getString(R.string.permission_read_never_ask_again))
|
||||
}
|
||||
|
||||
@OnShowRationale(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
fun showRationaleForWriteExternalStoragePermission(request: PermissionRequest) {
|
||||
showRationaleDialog(R.string.permission_write_rationale, request)
|
||||
}
|
||||
|
||||
@OnPermissionDenied(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
fun onWriteExternalStoragePermissionDenied() {
|
||||
toast(getString(R.string.permission_write_denied))
|
||||
}
|
||||
|
||||
@OnNeverAskAgain(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
fun onWriteExternalStoragePermissionNeverAskAgain() {
|
||||
toast(getString(R.string.permission_write_never_ask_again))
|
||||
}
|
||||
|
||||
@Inject
|
||||
lateinit var factory: EditorViewModelFactory
|
||||
|
||||
|
@ -465,6 +395,9 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
}
|
||||
}
|
||||
}
|
||||
jobs += subscribe(vm.copyFileStatus) { command ->
|
||||
onCopyFileCommand(command)
|
||||
}
|
||||
}
|
||||
vm.onStart(id = extractDocumentId())
|
||||
super.onStart()
|
||||
|
@ -746,8 +679,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
if (isVisible) {
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
behavior.addBottomSheetCallback(onHideBottomSheetCallback)
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
behavior.removeBottomSheetCallback(onHideBottomSheetCallback)
|
||||
behavior.state = BottomSheetBehavior.STATE_HIDDEN
|
||||
}
|
||||
|
@ -779,6 +711,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
|
||||
override fun onDestroy() {
|
||||
pickiT.deleteTemporaryFile(requireContext())
|
||||
clearOnCopyFile()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
@ -849,10 +782,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
).show(childFragmentManager, null)
|
||||
}
|
||||
is Command.OpenGallery -> {
|
||||
openGalleryWithPermissionCheck(command.mediaType)
|
||||
}
|
||||
is Command.RequestDownloadPermission -> {
|
||||
startDownloadWithPermissionCheck(command.id)
|
||||
openFilePicker(command.mimeType)
|
||||
}
|
||||
is Command.PopBackStack -> {
|
||||
childFragmentManager.popBackStack()
|
||||
|
@ -1720,15 +1650,6 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
hideSoftInput()
|
||||
}
|
||||
|
||||
private fun showRationaleDialog(@StringRes messageResId: Int, request: PermissionRequest) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setPositiveButton(R.string.button_allow) { _, _ -> request.proceed() }
|
||||
.setNegativeButton(R.string.button_deny) { _, _ -> request.cancel() }
|
||||
.setCancelable(false)
|
||||
.setMessage(messageResId)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun extractDocumentId(): String {
|
||||
return requireArguments()
|
||||
.getString(ID_KEY)
|
||||
|
@ -1756,7 +1677,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
private fun getEditorSettings() {
|
||||
}
|
||||
|
||||
// ----------- PickiT Listeners ------------------------------
|
||||
//region PICK IT
|
||||
|
||||
private var pickitProgressDialog: ProgressDialog? = null
|
||||
private var pickitProgressBar: ProgressBar? = null
|
||||
|
@ -1770,11 +1691,14 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
* are waiting for the file to be returned.
|
||||
*/
|
||||
override fun PickiTonUriReturned() {
|
||||
pickitProgressDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage(getString(R.string.pickit_waiting))
|
||||
setCancelable(false)
|
||||
Timber.d("PickiTonUriReturned")
|
||||
if (pickitProgressDialog == null || pickitProgressDialog?.isShowing == false) {
|
||||
pickitProgressDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage(getString(R.string.pickit_waiting))
|
||||
setCancelable(false)
|
||||
}
|
||||
pickitProgressDialog?.show()
|
||||
}
|
||||
pickitProgressDialog?.show()
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1791,6 +1715,7 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
* if the selected file is not local
|
||||
*/
|
||||
override fun PickiTonStartListener() {
|
||||
Timber.d("PickiTonStartListener")
|
||||
if (pickitProgressDialog?.isShowing == true) {
|
||||
pickitProgressDialog?.cancel()
|
||||
}
|
||||
|
@ -1829,6 +1754,9 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
if (pickitAlertDialog?.isShowing == true) {
|
||||
pickitAlertDialog?.cancel()
|
||||
}
|
||||
if (pickitProgressDialog?.isShowing == true) {
|
||||
pickitProgressDialog?.dismiss()
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
when {
|
||||
wasDriveFile -> toast(getString(R.string.pickit_drive))
|
||||
|
@ -1842,8 +1770,23 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
}
|
||||
}
|
||||
|
||||
override fun PickiTonMultipleCompleteListener(
|
||||
paths: ArrayList<String>?,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
toast("Not implemented yet")
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file was picked from file picker.
|
||||
*/
|
||||
private fun onFilePathReady(filePath: String?) {
|
||||
vm.onProceedWithFilePath(filePath)
|
||||
if (filePath != null) {
|
||||
vm.onProceedWithFilePath(filePath = filePath)
|
||||
} else {
|
||||
Timber.e("onFilePathReady, filePath is null")
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPickit() {
|
||||
|
@ -1852,6 +1795,8 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
pickitProgressDialog?.dismiss()
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
override fun onExitToDesktopClicked() {
|
||||
vm.navigateToDesktop()
|
||||
}
|
||||
|
@ -1899,10 +1844,6 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
vm.onLayoutClicked()
|
||||
}
|
||||
|
||||
override fun onDownloadClicked() {
|
||||
vm.onDownloadClicked()
|
||||
}
|
||||
|
||||
override fun onTextValueChanged(ctx: Id, text: String, objectId: Id, relationId: Id) {
|
||||
vm.onRelationTextValueChanged(
|
||||
ctx = ctx,
|
||||
|
@ -2350,6 +2291,100 @@ open class EditorFragment : NavigationFragment(R.layout.fragment_editor),
|
|||
|
||||
//endregion
|
||||
|
||||
//region READ PERMISSION
|
||||
private fun takeReadStoragePermission() {
|
||||
if (requireActivity().shouldShowRequestPermissionRationaleCompat(READ_EXTERNAL_STORAGE)) {
|
||||
root.showSnackbar(
|
||||
R.string.permission_read_rationale,
|
||||
Snackbar.LENGTH_INDEFINITE,
|
||||
R.string.button_ok
|
||||
) {
|
||||
permissionReadStorage.launch(arrayOf(READ_EXTERNAL_STORAGE))
|
||||
}
|
||||
} else {
|
||||
permissionReadStorage.launch(arrayOf(READ_EXTERNAL_STORAGE))
|
||||
}
|
||||
}
|
||||
|
||||
private val permissionReadStorage =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
|
||||
val readResult = grantResults[READ_EXTERNAL_STORAGE]
|
||||
if (readResult == true) {
|
||||
startFilePicker(mMimeType)
|
||||
} else {
|
||||
root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region UPLOAD FILE LOGIC
|
||||
private var mMimeType = ""
|
||||
private var mSnackbar: Snackbar? = null
|
||||
|
||||
private fun openFilePicker(mimeType: String) {
|
||||
mMimeType = mimeType
|
||||
if (requireContext().isPermissionGranted(mimeType)) {
|
||||
startFilePicker(mimeType)
|
||||
} else {
|
||||
takeReadStoragePermission()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyFileCommand(command: CopyFileStatus) {
|
||||
when (command) {
|
||||
is CopyFileStatus.Error -> {
|
||||
mSnackbar?.dismiss()
|
||||
activity?.toast("Error while loading file:${command.msg}")
|
||||
}
|
||||
is CopyFileStatus.Completed -> {
|
||||
mSnackbar?.dismiss()
|
||||
onFilePathReady(command.result)
|
||||
}
|
||||
CopyFileStatus.Started -> {
|
||||
mSnackbar = root.showSnackbar(
|
||||
R.string.loading_file,
|
||||
Snackbar.LENGTH_INDEFINITE,
|
||||
R.string.cancel
|
||||
) {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_MEDIA_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
pickiT.getPath(uri, Build.VERSION.SDK_INT)
|
||||
}
|
||||
}
|
||||
REQUEST_FILE_SAF_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
vm.onStartCopyFileToCacheDir(uri)
|
||||
} ?: run {
|
||||
Timber.e("onActivityResult error, data is null")
|
||||
toast("Error while getting file")
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
Timber.e("onActivityResult error, Unknown Request Code:$requestCode")
|
||||
toast("Unknown Request Code:$requestCode")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearOnCopyFile() {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
mSnackbar?.dismiss()
|
||||
mSnackbar = null
|
||||
}
|
||||
//endregion
|
||||
|
||||
//------------ End of Anytype Custom Context Menu ------------
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -198,7 +198,6 @@ abstract class ObjectMenuBaseFragment : BaseBottomSheetFragment() {
|
|||
fun onAddCoverClicked()
|
||||
fun onSetIconClicked()
|
||||
fun onLayoutClicked()
|
||||
fun onDownloadClicked()
|
||||
fun onUndoRedoClicked()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package com.anytypeio.anytype.ui.relations
|
||||
|
||||
import android.Manifest
|
||||
import android.Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
import android.app.Activity
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Intent
|
||||
|
@ -11,7 +11,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ProgressBar
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.viewModels
|
||||
|
@ -27,6 +27,8 @@ import com.anytypeio.anytype.core_models.Id
|
|||
import com.anytypeio.anytype.core_ui.features.sets.RelationValueAdapter
|
||||
import com.anytypeio.anytype.core_ui.reactive.clicks
|
||||
import com.anytypeio.anytype.core_ui.tools.DefaultDragAndDropBehavior
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_FILE_SAF_CODE
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
|
||||
import com.anytypeio.anytype.core_utils.ext.*
|
||||
import com.anytypeio.anytype.core_utils.ui.BaseBottomSheetFragment
|
||||
import com.anytypeio.anytype.core_utils.ui.DragAndDropViewHolder
|
||||
|
@ -36,17 +38,19 @@ import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
|||
import com.anytypeio.anytype.presentation.sets.RelationValueBaseViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueDVViewModel
|
||||
import com.anytypeio.anytype.presentation.sets.RelationValueViewModel
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileStatus
|
||||
import com.anytypeio.anytype.ui.editor.EditorFragment
|
||||
import com.anytypeio.anytype.ui.editor.REQUEST_FILE_CODE
|
||||
import com.anytypeio.anytype.ui.sets.ObjectSetFragment
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.hbisoft.pickit.PickiT
|
||||
import com.hbisoft.pickit.PickiTCallbacks
|
||||
import kotlinx.android.synthetic.main.fragment_relation_value.*
|
||||
import permissions.dispatcher.*
|
||||
import kotlinx.android.synthetic.main.fragment_relation_value.recycler
|
||||
import kotlinx.android.synthetic.main.fragment_relation_value.root
|
||||
import timber.log.Timber
|
||||
import java.util.ArrayList
|
||||
import javax.inject.Inject
|
||||
|
||||
@RuntimePermissions
|
||||
abstract class RelationValueBaseFragment : BaseBottomSheetFragment(),
|
||||
OnStartDragListener,
|
||||
RelationObjectValueAddFragment.ObjectValueAddReceiver,
|
||||
|
@ -145,6 +149,7 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment(),
|
|||
jobs += lifecycleScope.subscribe(vm.name) { tvTagOrStatusRelationHeader.text = it }
|
||||
jobs += lifecycleScope.subscribe(vm.navigation) { command -> navigate(command) }
|
||||
jobs += lifecycleScope.subscribe(vm.isLoading) { isLoading -> observeLoading(isLoading) }
|
||||
jobs += lifecycleScope.subscribe(vm.copyFileStatus) { command -> onCopyFileCommand(command) }
|
||||
super.onStart()
|
||||
vm.onStart(relationId = relation, objectId = target)
|
||||
}
|
||||
|
@ -212,48 +217,10 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment(),
|
|||
}
|
||||
}
|
||||
|
||||
//region READ STORAGE
|
||||
//region PICK IT
|
||||
|
||||
abstract fun onFilePathReady(filePath: String?)
|
||||
|
||||
@NeedsPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun openGallery(type: String) {
|
||||
startActivityForResult(getVideoFileIntent(type), REQUEST_FILE_CODE)
|
||||
}
|
||||
|
||||
@OnShowRationale(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun showRationaleForReadExternalStoragePermission(request: PermissionRequest) {
|
||||
showRationaleDialog(R.string.permission_read_rationale, request)
|
||||
}
|
||||
|
||||
@OnPermissionDenied(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun onReadExternalStoragePermissionDenied() {
|
||||
toast(getString(R.string.permission_read_denied))
|
||||
}
|
||||
|
||||
@OnNeverAskAgain(Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||
fun onReadExternalStoragePermissionNeverAskAgain() {
|
||||
toast(getString(R.string.permission_read_never_ask_again))
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(
|
||||
requestCode: Int,
|
||||
permissions: Array<out String>,
|
||||
grantResults: IntArray
|
||||
) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
onRequestPermissionsResult(requestCode, grantResults)
|
||||
}
|
||||
|
||||
private fun showRationaleDialog(@StringRes messageResId: Int, request: PermissionRequest) {
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setPositiveButton(R.string.button_allow) { _, _ -> request.proceed() }
|
||||
.setNegativeButton(R.string.button_deny) { _, _ -> request.cancel() }
|
||||
.setCancelable(false)
|
||||
.setMessage(messageResId)
|
||||
.show()
|
||||
}
|
||||
|
||||
private lateinit var pickiT: PickiT
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -261,33 +228,18 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment(),
|
|||
pickiT = PickiT(requireContext(), this, requireActivity())
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_FILE_CODE -> {
|
||||
data?.data?.let {
|
||||
pickiT.getPath(it, Build.VERSION.SDK_INT)
|
||||
} ?: run {
|
||||
toast("Error while getting file")
|
||||
}
|
||||
}
|
||||
else -> toast("Unknown Request Code:$requestCode")
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private var pickitProgressDialog: ProgressDialog? = null
|
||||
private var pickitProgressBar: ProgressBar? = null
|
||||
private var pickitAlertDialog: AlertDialog? = null
|
||||
|
||||
override fun PickiTonUriReturned() {
|
||||
pickitProgressDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage(getString(R.string.pickit_waiting))
|
||||
setCancelable(false)
|
||||
if (pickitProgressDialog == null || pickitProgressDialog?.isShowing == false) {
|
||||
pickitProgressDialog = ProgressDialog(requireContext()).apply {
|
||||
setMessage(getString(R.string.pickit_waiting))
|
||||
setCancelable(false)
|
||||
}
|
||||
pickitProgressDialog?.show()
|
||||
}
|
||||
pickitProgressDialog?.show()
|
||||
}
|
||||
|
||||
override fun PickiTonStartListener() {
|
||||
|
@ -327,6 +279,9 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment(),
|
|||
if (pickitAlertDialog?.isShowing == true) {
|
||||
pickitAlertDialog?.cancel()
|
||||
}
|
||||
if (pickitProgressDialog?.isShowing == true) {
|
||||
pickitProgressDialog?.cancel()
|
||||
}
|
||||
if (BuildConfig.DEBUG) {
|
||||
when {
|
||||
wasDriveFile -> toast(getString(R.string.pickit_drive))
|
||||
|
@ -341,14 +296,107 @@ abstract class RelationValueBaseFragment : BaseBottomSheetFragment(),
|
|||
}
|
||||
|
||||
private fun clearPickit() {
|
||||
val ctx = context
|
||||
if (ctx != null) {
|
||||
pickiT.deleteTemporaryFile(ctx)
|
||||
}
|
||||
pickiT.cancelTask()
|
||||
pickitAlertDialog?.dismiss()
|
||||
pickitProgressDialog?.dismiss()
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region READ PERMISSION
|
||||
private fun takeReadStoragePermission() {
|
||||
if (requireActivity().shouldShowRequestPermissionRationaleCompat(READ_EXTERNAL_STORAGE)) {
|
||||
root.showSnackbar(
|
||||
R.string.permission_read_rationale,
|
||||
Snackbar.LENGTH_INDEFINITE,
|
||||
R.string.button_ok
|
||||
) {
|
||||
permissionReadStorage.launch(arrayOf(READ_EXTERNAL_STORAGE))
|
||||
}
|
||||
} else {
|
||||
permissionReadStorage.launch(arrayOf(READ_EXTERNAL_STORAGE))
|
||||
}
|
||||
}
|
||||
|
||||
private val permissionReadStorage =
|
||||
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { grantResults ->
|
||||
val readResult = grantResults[READ_EXTERNAL_STORAGE]
|
||||
if (readResult == true) {
|
||||
startFilePicker(MIME_FILE_ALL)
|
||||
} else {
|
||||
root.showSnackbar(R.string.permission_read_denied, Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region UPLOAD FILE LOGIC
|
||||
private var mSnackbar: Snackbar? = null
|
||||
|
||||
protected fun openFilePicker() {
|
||||
if (requireContext().isPermissionGranted(MIME_FILE_ALL)) {
|
||||
startFilePicker(MIME_FILE_ALL)
|
||||
} else {
|
||||
takeReadStoragePermission()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
when (requestCode) {
|
||||
REQUEST_MEDIA_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
pickiT.getPath(uri, Build.VERSION.SDK_INT)
|
||||
}
|
||||
}
|
||||
REQUEST_FILE_SAF_CODE -> {
|
||||
data?.data?.let { uri ->
|
||||
vm.onStartCopyFileToCacheDir(uri)
|
||||
} ?: run {
|
||||
toast("Error while getting file")
|
||||
}
|
||||
}
|
||||
else -> toast("Unknown Request Code:$requestCode")
|
||||
}
|
||||
} else {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onCopyFileCommand(command: CopyFileStatus) {
|
||||
when (command) {
|
||||
is CopyFileStatus.Error -> {
|
||||
mSnackbar?.dismiss()
|
||||
activity?.toast("Error while loading file:${command.msg}")
|
||||
}
|
||||
is CopyFileStatus.Completed -> {
|
||||
mSnackbar?.dismiss()
|
||||
onFilePathReady(command.result)
|
||||
}
|
||||
CopyFileStatus.Started -> {
|
||||
mSnackbar = root.showSnackbar(
|
||||
R.string.loading_file,
|
||||
Snackbar.LENGTH_INDEFINITE,
|
||||
R.string.cancel
|
||||
) {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearOnCopyFile() {
|
||||
vm.onCancelCopyFileToCacheDir()
|
||||
mSnackbar?.dismiss()
|
||||
mSnackbar = null
|
||||
}
|
||||
//endregion
|
||||
|
||||
override fun onDestroyView() {
|
||||
clearPickit()
|
||||
clearOnCopyFile()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
|
@ -487,7 +535,7 @@ open class RelationValueDVFragment : RelationValueBaseFragment() {
|
|||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
|
||||
//turn off for now https://app.clickup.com/t/h59z1j
|
||||
//FileActionsFragment().show(childFragmentManager, null)
|
||||
openGalleryWithPermissionCheck(type = MIME_FILE_ALL)
|
||||
openFilePicker()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -501,6 +549,9 @@ open class RelationValueDVFragment : RelationValueBaseFragment() {
|
|||
vm.onFileValueActionUploadFromGalleryClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file was picked from file picker.
|
||||
*/
|
||||
override fun onFilePathReady(filePath: String?) {
|
||||
if (filePath != null) {
|
||||
vm.onAddFileToRecord(
|
||||
|
@ -520,6 +571,14 @@ open class RelationValueDVFragment : RelationValueBaseFragment() {
|
|||
vm.onFileValueActionUploadFromStorageClicked()
|
||||
}
|
||||
|
||||
override fun PickiTonMultipleCompleteListener(
|
||||
paths: ArrayList<String>?,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
toast("Not implemented yet")
|
||||
}
|
||||
|
||||
override fun injectDependencies() {
|
||||
componentManager().objectSetObjectRelationValueComponent.get(ctx).inject(this)
|
||||
}
|
||||
|
@ -620,6 +679,14 @@ class RelationValueFragment : RelationValueBaseFragment() {
|
|||
)
|
||||
}
|
||||
|
||||
override fun PickiTonMultipleCompleteListener(
|
||||
paths: ArrayList<String>?,
|
||||
wasSuccessful: Boolean,
|
||||
Reason: String?
|
||||
) {
|
||||
toast("Not implemented yet")
|
||||
}
|
||||
|
||||
override fun observeCommands(command: RelationValueBaseViewModel.ObjectRelationValueCommand) {
|
||||
when (command) {
|
||||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowAddObjectScreen -> {
|
||||
|
@ -651,7 +718,7 @@ class RelationValueFragment : RelationValueBaseFragment() {
|
|||
RelationValueBaseViewModel.ObjectRelationValueCommand.ShowFileValueActionScreen -> {
|
||||
//turn off for now https://app.clickup.com/t/h59z1j
|
||||
//FileActionsFragment().show(childFragmentManager, null)
|
||||
openGalleryWithPermissionCheck(type = MIME_FILE_ALL)
|
||||
openFilePicker()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -670,6 +737,9 @@ class RelationValueFragment : RelationValueBaseFragment() {
|
|||
vm.onFileValueActionUploadFromStorageClicked()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a file was picked from file picker.
|
||||
*/
|
||||
override fun onFilePathReady(filePath: String?) {
|
||||
if (filePath != null) {
|
||||
vm.onAddFileToObject(
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/root"
|
||||
android:orientation="vertical"
|
||||
tools:context=".ui.relations.RelationValueBaseFragment">
|
||||
|
||||
|
|
|
@ -106,13 +106,14 @@ Do the computation of an expensive paragraph of text on a background thread:
|
|||
<string name="button_link">Link</string>
|
||||
|
||||
<string name="permission_read_rationale">Read permission is needed to load file to media block.</string>
|
||||
<string name="permission_read_denied">Read permission was denied. Please consider granting it in order to load files to media blocks!</string>
|
||||
<string name="permission_read_never_ask_again">Read permission was denied with never ask again.</string>
|
||||
<string name="permission_read_denied">User denied permission. Please, approve this permission in \"Permissions\" in the app settings on your device.</string>
|
||||
<string name="permission_read_never_ask_again">Read permission was denied with never ask again.\n You must approve this permission in \"Permissions\" in the app settings on your device.</string>
|
||||
<string name="permission_write_rationale">Write permission is needed to load file to device.</string>
|
||||
<string name="permission_write_denied">Write permission was denied. Please consider granting it in order to load files to device!</string>
|
||||
<string name="permission_write_never_ask_again">Write permission was denied with never ask again.</string>
|
||||
<string name="button_allow">Allow</string>
|
||||
<string name="button_deny">Deny</string>
|
||||
<string name="button_ok">Ok</string>
|
||||
<string name="error">Error</string>
|
||||
|
||||
<string name="page_icon">Page icon</string>
|
||||
|
|
|
@ -3,12 +3,12 @@ apply from: './dependencies.gradle'
|
|||
buildscript {
|
||||
ext.kotlin_version = '1.5.21'
|
||||
ext.gradle_tools = '3.1.3'
|
||||
ext.build_tools = '29.0.3'
|
||||
ext.build_tools = '31.0.0'
|
||||
ext.nav_version = '2.3.0'
|
||||
ext.dokka_version = '1.4.32'
|
||||
|
||||
ext.compile_sdk = 29
|
||||
ext.target_sdk = 29
|
||||
ext.compile_sdk = 31
|
||||
ext.target_sdk = 31
|
||||
ext.min_sdk = 24
|
||||
|
||||
ext.application_id = 'com.anytypeio.anytype'
|
||||
|
@ -21,7 +21,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:4.2.2'
|
||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
|
||||
classpath 'com.google.gms:google-services:4.3.8'
|
||||
|
|
|
@ -36,12 +36,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -63,12 +63,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -175,6 +175,7 @@
|
|||
|
||||
|
||||
<string name="loading">Loading…</string>
|
||||
<string name="loading_file">Loading file, please wait</string>
|
||||
<string name="error_while_loading">Error while loading</string>
|
||||
<string name="error_while_loading_picture">Error while loading picture</string>
|
||||
<string name="block_with_a_picture">Block with a picture</string>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package com.anytypeio.anytype.core_utils.const
|
||||
|
||||
object FileConstants {
|
||||
const val REQUEST_FILE_SAF_CODE = 2211
|
||||
const val REQUEST_MEDIA_CODE = 2212
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package com.anytypeio.anytype.core_utils.ext
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.MediaStore
|
||||
import android.text.Editable
|
||||
|
@ -22,11 +25,15 @@ import android.widget.EditText
|
|||
import android.widget.TextView
|
||||
import androidx.annotation.DimenRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.BlendModeColorFilterCompat
|
||||
import androidx.core.graphics.BlendModeCompat
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_FILE_SAF_CODE
|
||||
import com.anytypeio.anytype.core_utils.const.FileConstants.REQUEST_MEDIA_CODE
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
@ -135,21 +142,6 @@ inline fun <reified T> Editable.removeSpans() {
|
|||
getSpans(0, length, T::class.java).forEach { removeSpan(it) }
|
||||
}
|
||||
|
||||
fun getVideoFileIntent(mediaType: String): Intent {
|
||||
val intent =
|
||||
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
|
||||
Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
|
||||
} else {
|
||||
Intent(Intent.ACTION_PICK, MediaStore.Video.Media.INTERNAL_CONTENT_URI)
|
||||
}
|
||||
return intent.apply {
|
||||
type = mediaType
|
||||
action = Intent.ACTION_GET_CONTENT
|
||||
putExtra("return-data", true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
fun String.getFileName(mime: String?): String =
|
||||
if (mime != null) {
|
||||
"$this.${mime.substringAfter("/")}"
|
||||
|
@ -253,4 +245,48 @@ fun View.focusAndShowKeyboard() {
|
|||
}
|
||||
|
||||
fun String.normalizeUrl(): String =
|
||||
if (!startsWith("http://") && !startsWith("https://")) "https://$this" else this
|
||||
if (!startsWith("http://") && !startsWith("https://")) "https://$this" else this
|
||||
|
||||
fun Context.isPermissionGranted(mimeType: String): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && mimeType == MIME_FILE_ALL) {
|
||||
true
|
||||
} else {
|
||||
val readExternalStorage: Int = ContextCompat.checkSelfPermission(
|
||||
this,
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||
)
|
||||
readExternalStorage == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
}
|
||||
|
||||
fun Activity.shouldShowRequestPermissionRationaleCompat(permission: String) =
|
||||
ActivityCompat.shouldShowRequestPermissionRationale(this, permission)
|
||||
|
||||
fun Fragment.startFilePicker(mime: String) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, false)
|
||||
type = mime
|
||||
}
|
||||
val code = if (mime == MIME_FILE_ALL) {
|
||||
REQUEST_FILE_SAF_CODE
|
||||
} else {
|
||||
REQUEST_MEDIA_CODE
|
||||
}
|
||||
startActivityForResult(intent, code)
|
||||
} else {
|
||||
val intent =
|
||||
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
|
||||
Intent(Intent.ACTION_PICK, MediaStore.Video.Media.EXTERNAL_CONTENT_URI)
|
||||
} else {
|
||||
Intent(Intent.ACTION_PICK, MediaStore.Video.Media.INTERNAL_CONTENT_URI)
|
||||
}
|
||||
intent.apply {
|
||||
type = mime
|
||||
action = Intent.ACTION_GET_CONTENT
|
||||
putExtra("return-data", true)
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
startActivityForResult(intent, REQUEST_MEDIA_CODE)
|
||||
}
|
||||
}
|
|
@ -30,6 +30,33 @@ fun View.showSnackbar(text: String) {
|
|||
Snackbar.make(this, text, Snackbar.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun View.showSnackbar(msgId: Int, length: Int) = showSnackbar(context.getString(msgId), length)
|
||||
fun View.showSnackbar(msg: String, length: Int) = showSnackbar(msg, length, null, {})
|
||||
|
||||
fun View.showSnackbar(
|
||||
msgId: Int,
|
||||
length: Int,
|
||||
actionMessageId: Int,
|
||||
action: (View) -> Unit
|
||||
): Snackbar =
|
||||
showSnackbar(context.getString(msgId), length, context.getString(actionMessageId), action)
|
||||
|
||||
fun View.showSnackbar(
|
||||
msg: String,
|
||||
length: Int,
|
||||
actionMessage: CharSequence?,
|
||||
action: (View) -> Unit
|
||||
): Snackbar {
|
||||
val snackbar = Snackbar.make(this, msg, length)
|
||||
if (actionMessage != null) {
|
||||
snackbar.setAction(actionMessage) {
|
||||
action(this)
|
||||
}
|
||||
}
|
||||
snackbar.show()
|
||||
return snackbar
|
||||
}
|
||||
|
||||
fun View.hideKeyboard() {
|
||||
val inputMethodManager =
|
||||
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
|
@ -50,7 +77,7 @@ fun Activity.hideSoftInput() {
|
|||
|
||||
fun Fragment.hideSoftInput() = requireActivity().hideSoftInput()
|
||||
|
||||
fun RecyclerView.containsItemDecoration(decoration: RecyclerView.ItemDecoration) : Boolean {
|
||||
fun RecyclerView.containsItemDecoration(decoration: RecyclerView.ItemDecoration): Boolean {
|
||||
if (itemDecorationCount > 0) {
|
||||
for (i in 0..itemDecorationCount.dec()) {
|
||||
val d = getItemDecorationAt(i)
|
||||
|
@ -63,7 +90,7 @@ fun RecyclerView.containsItemDecoration(decoration: RecyclerView.ItemDecoration)
|
|||
}
|
||||
}
|
||||
|
||||
val Activity.statusBarHeight : Int
|
||||
val Activity.statusBarHeight: Int
|
||||
get() {
|
||||
val rectangle = Rect()
|
||||
window.decorView.getWindowVisibleDisplayFrame(rectangle)
|
||||
|
|
|
@ -4,24 +4,24 @@ ext {
|
|||
kotlinx_serialization_json_version = '1.2.1'
|
||||
|
||||
// AndroidX
|
||||
androidx_core_version = '1.6.0'
|
||||
androidx_core_ktx_version = '1.6.0'
|
||||
androidx_core_version = '1.7.0'
|
||||
androidx_core_ktx_version = '1.7.0'
|
||||
androidx_test_core_version = '1.4.0'
|
||||
androidx_core_testing_version = '2.1.0'
|
||||
androidx_security_crypto_version = '1.0.0'
|
||||
|
||||
// Other Android framework dependencies
|
||||
appcompat_version = '1.3.0'
|
||||
constraintLayout_version = '2.0.4'
|
||||
constraintLayout_version = '2.1.2'
|
||||
recyclerview_version = '1.2.1'
|
||||
cardview_version = '1.0.0'
|
||||
material_version = '1.3.0'
|
||||
fragment_version = "1.3.6"
|
||||
fragment_version = "1.4.0"
|
||||
emoji_compat_version = '1.1.0'
|
||||
view_pager_2_version = '1.0.0'
|
||||
|
||||
// Architecture Components
|
||||
lifecycle_version = '2.3.1'
|
||||
lifecycle_version = '2.4.0'
|
||||
navigation_version = '2.3.5'
|
||||
|
||||
// Third party libraries
|
||||
|
@ -37,8 +37,7 @@ ext {
|
|||
gson_version = '2.8.6'
|
||||
better_link_method_version = '2.2.0'
|
||||
table_view_version = '0.8.9.4'
|
||||
permission_disp_version = '4.8.0'
|
||||
pickt_version = "0.1.14"
|
||||
pickt_version = "2.0.2"
|
||||
zxing_version = "4.1.0"
|
||||
urlcleaner_version = "0.4.0"
|
||||
katex_version = "1.0.2"
|
||||
|
@ -124,8 +123,6 @@ ext {
|
|||
timber: "com.jakewharton.timber:timber:$timber_version",
|
||||
tableView: "com.evrencoskun.library:tableview:$table_view_version",
|
||||
exoPlayer: "com.google.android.exoplayer:exoplayer:$exoplayer_version",
|
||||
permissionDisp: "com.github.permissions-dispatcher:permissionsdispatcher:$permission_disp_version",
|
||||
permissionDispCompiler: "com.github.permissions-dispatcher:permissionsdispatcher-processor:$permission_disp_version",
|
||||
pickT: "com.github.HBiSoft:PickiT:$pickt_version",
|
||||
zxing: "com.journeyapps:zxing-android-embedded:$zxing_version",
|
||||
urlcleaner: "com.shekhargulati.urlcleaner:urlcleaner:$urlcleaner_version",
|
||||
|
|
|
@ -30,12 +30,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
|||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
|
||||
|
|
|
@ -30,12 +30,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -59,12 +59,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,12 +31,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
package com.anytypeio.anytype.middleware.interactor
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import service.InterfaceAddr
|
||||
import service.InterfaceAddrIterator
|
||||
import service.InterfaceAddrsGetter
|
||||
import service.Service.setInterfaceAddrsGetter
|
||||
import timber.log.Timber
|
||||
import java.net.InterfaceAddress
|
||||
import java.net.NetworkInterface
|
||||
|
||||
/**
|
||||
* This class is used for sending local Ip addresses to middleware.
|
||||
* See https://discuss.ipfs.io/t/basichosts-updatelocalipaddr-fails-on-android-11-net-interfaceaddrs-returns-error/13003
|
||||
*/
|
||||
class LocalNetworkAddressHandler(
|
||||
private val scope: CoroutineScope = GlobalScope
|
||||
) {
|
||||
|
||||
fun start() {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
setInterfaceAddrsGetter(DefaultAddressProvider())
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultAddressProvider : InterfaceAddrsGetter {
|
||||
|
||||
private var mLastUpdateTime: Long = 0
|
||||
private var addresses = mutableListOf<InterfaceAddress>()
|
||||
|
||||
override fun interfaceAddrs(): InterfaceAddrIterator {
|
||||
Timber.d("Getting addresses")
|
||||
if (!isNeedToUpdateNetworkAddresses()) {
|
||||
return DefaultAddressIterator(addresses.iterator())
|
||||
}
|
||||
return try {
|
||||
addresses.clear()
|
||||
val interfaces = NetworkInterface.getNetworkInterfaces().toList()
|
||||
addresses.addAll(interfaces.flatMap { it.interfaceAddresses })
|
||||
mLastUpdateTime = System.currentTimeMillis()
|
||||
DefaultAddressIterator(addresses.iterator())
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error getting local net address")
|
||||
mLastUpdateTime = System.currentTimeMillis()
|
||||
DefaultAddressIterator(addresses.iterator())
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNeedToUpdateNetworkAddresses(): Boolean {
|
||||
return System.currentTimeMillis() - mLastUpdateTime >= UPDATE_INTERVAL_MILLI_SECONDS
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultAddressIterator(private val ia: Iterator<InterfaceAddress>) :
|
||||
InterfaceAddrIterator {
|
||||
override fun next(): LocalInterfaceAddr? {
|
||||
if (ia.hasNext()) {
|
||||
return LocalInterfaceAddr(ia.next())
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class LocalInterfaceAddr(private val interfaceAddress: InterfaceAddress?) : InterfaceAddr {
|
||||
|
||||
override fun ip(): ByteArray? {
|
||||
return interfaceAddress?.address?.address
|
||||
}
|
||||
|
||||
override fun prefix(): Long {
|
||||
return interfaceAddress?.networkPrefixLength?.toLong() ?: 0L
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val UPDATE_INTERVAL_MILLI_SECONDS = 180000
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ android {
|
|||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
|
|
@ -1,85 +1,85 @@
|
|||
package com.anytypeio.anytype
|
||||
|
||||
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.model.AccountTable
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.After
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertTrue
|
||||
//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.model.AccountTable
|
||||
//import kotlinx.coroutines.delay
|
||||
//import kotlinx.coroutines.runBlocking
|
||||
//import org.junit.After
|
||||
//import org.junit.Rule
|
||||
//import org.junit.Test
|
||||
//import org.junit.runner.RunWith
|
||||
//import org.robolectric.RobolectricTestRunner
|
||||
//import org.robolectric.annotation.Config
|
||||
//import kotlin.test.assertEquals
|
||||
//import kotlin.test.assertTrue
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE)
|
||||
class AccountDaoTest {
|
||||
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
private val database = Room.inMemoryDatabaseBuilder(
|
||||
InstrumentationRegistry.getInstrumentation().context,
|
||||
AnytypeDatabase::class.java
|
||||
).allowMainThreadQueries().build()
|
||||
|
||||
@After
|
||||
fun after() {
|
||||
database.close()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return empty list if there are no last account in db`() {
|
||||
runBlocking {
|
||||
val accounts = database.accountDao().lastAccount()
|
||||
assertTrue { accounts.isEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return last account`() = runBlocking {
|
||||
|
||||
val firstAccount = AccountTable(
|
||||
id = MockDataFactory.randomString(),
|
||||
name = MockDataFactory.randomString(),
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
delay(1)
|
||||
|
||||
val secondAccount = AccountTable(
|
||||
id = MockDataFactory.randomString(),
|
||||
name = MockDataFactory.randomString(),
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
database.accountDao().insert(firstAccount)
|
||||
database.accountDao().insert(secondAccount)
|
||||
|
||||
val result = database.accountDao().lastAccount()
|
||||
|
||||
assertTrue { result.size == 1 }
|
||||
assertTrue { result.first() == secondAccount }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should return expected account when queried using account id`() = runBlocking {
|
||||
|
||||
val account = AccountTable(
|
||||
id = MockDataFactory.randomString(),
|
||||
name = MockDataFactory.randomString(),
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
|
||||
database.accountDao().insert(account)
|
||||
|
||||
val result = database.accountDao().getAccount(account.id)
|
||||
|
||||
assertEquals(account, result)
|
||||
}
|
||||
}
|
||||
//@RunWith(RobolectricTestRunner::class)
|
||||
//@Config(manifest = Config.NONE)
|
||||
//class AccountDaoTest {
|
||||
//
|
||||
// @get:Rule
|
||||
// val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
//
|
||||
// private val database = Room.inMemoryDatabaseBuilder(
|
||||
// InstrumentationRegistry.getInstrumentation().context,
|
||||
// AnytypeDatabase::class.java
|
||||
// ).allowMainThreadQueries().build()
|
||||
//
|
||||
// @After
|
||||
// fun after() {
|
||||
// database.close()
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `should return empty list if there are no last account in db`() {
|
||||
// runBlocking {
|
||||
// val accounts = database.accountDao().lastAccount()
|
||||
// assertTrue { accounts.isEmpty() }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `should return last account`() = runBlocking {
|
||||
//
|
||||
// val firstAccount = AccountTable(
|
||||
// id = MockDataFactory.randomString(),
|
||||
// name = MockDataFactory.randomString(),
|
||||
// timestamp = System.currentTimeMillis()
|
||||
// )
|
||||
//
|
||||
// delay(1)
|
||||
//
|
||||
// val secondAccount = AccountTable(
|
||||
// id = MockDataFactory.randomString(),
|
||||
// name = MockDataFactory.randomString(),
|
||||
// timestamp = System.currentTimeMillis()
|
||||
// )
|
||||
//
|
||||
// database.accountDao().insert(firstAccount)
|
||||
// database.accountDao().insert(secondAccount)
|
||||
//
|
||||
// val result = database.accountDao().lastAccount()
|
||||
//
|
||||
// assertTrue { result.size == 1 }
|
||||
// assertTrue { result.first() == secondAccount }
|
||||
// }
|
||||
//
|
||||
// @Test
|
||||
// fun `should return expected account when queried using account id`() = runBlocking {
|
||||
//
|
||||
// val account = AccountTable(
|
||||
// id = MockDataFactory.randomString(),
|
||||
// name = MockDataFactory.randomString(),
|
||||
// timestamp = System.currentTimeMillis()
|
||||
// )
|
||||
//
|
||||
// database.accountDao().insert(account)
|
||||
//
|
||||
// val result = database.accountDao().getAccount(account.id)
|
||||
//
|
||||
// assertEquals(account, result)
|
||||
// }
|
||||
//}
|
|
@ -9,7 +9,7 @@ class CreateAccountViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CreateAccountViewModel(
|
||||
session = session
|
||||
) as T
|
||||
|
|
|
@ -13,7 +13,7 @@ class SelectAccountViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SelectAccountViewModel(
|
||||
startLoadingAccounts = startLoadingAccounts,
|
||||
observeAccounts = observeAccounts,
|
||||
|
|
|
@ -15,7 +15,7 @@ class SetupNewAccountViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SetupNewAccountViewModel(
|
||||
createAccount = createAccount,
|
||||
session = session,
|
||||
|
|
|
@ -15,7 +15,7 @@ class SetupSelectedAccountViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SetupSelectedAccountViewModel(
|
||||
startAccount = startAccount,
|
||||
pathProvider = pathProvider,
|
||||
|
|
|
@ -17,7 +17,7 @@ class KeychainLoginViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return KeychainLoginViewModel(
|
||||
recoverWallet = recoverWallet,
|
||||
convertWallet = convertWallet,
|
||||
|
|
|
@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
class ChoosePinCodeViewModelFactory : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ChoosePinCodeViewModel() as T
|
||||
}
|
||||
}
|
|
@ -5,7 +5,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
|
||||
class ConfirmPinCodeViewModelFactory : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ConfirmPinCodeViewModel() as T
|
||||
}
|
||||
}
|
|
@ -6,7 +6,7 @@ import androidx.lifecycle.ViewModelProvider
|
|||
|
||||
class EnterPinCodeViewModelFactory : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return EnterPinCodeViewModel() as T
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@ class StartLoginViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return StartLoginViewModel(
|
||||
setupWallet = setupWallet,
|
||||
pathProvider = pathProvider,
|
||||
|
|
|
@ -37,7 +37,7 @@ class HomeDashboardViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return HomeDashboardViewModel(
|
||||
getProfile = getProfile,
|
||||
openDashboard = openDashboard,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.editor
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -105,7 +106,10 @@ import com.anytypeio.anytype.presentation.relations.DocumentRelationView
|
|||
import com.anytypeio.anytype.presentation.relations.views
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchConstants
|
||||
import com.anytypeio.anytype.presentation.search.ObjectSearchViewModel
|
||||
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 kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
@ -140,7 +144,8 @@ class EditorViewModel(
|
|||
private val searchObjects: SearchObjects,
|
||||
private val getDefaultEditorType: GetDefaultEditorType,
|
||||
private val findObjectSetForType: FindObjectSetForType,
|
||||
private val createObjectSet: CreateObjectSet
|
||||
private val createObjectSet: CreateObjectSet,
|
||||
private val copyFileToCache: CopyFileToCacheDirectory
|
||||
) : ViewStateViewModel<ViewState>(),
|
||||
SupportNavigation<EventWrapper<AppNavigation.Command>>,
|
||||
SupportCommand<Command>,
|
||||
|
@ -1655,14 +1660,7 @@ class EditorViewModel(
|
|||
ActionItemType.Style -> {
|
||||
viewModelScope.launch { proceedWithOpeningStyleToolbarFromActionMenu(id) }
|
||||
}
|
||||
ActionItemType.Download -> {
|
||||
viewModelScope.launch {
|
||||
onExitActionMode()
|
||||
dispatch(Command.PopBackStack)
|
||||
delay(300)
|
||||
dispatch(Command.RequestDownloadPermission(id))
|
||||
}
|
||||
}
|
||||
ActionItemType.Download -> { }
|
||||
ActionItemType.SAM -> {
|
||||
mode = EditorMode.SAM
|
||||
viewModelScope.launch { orchestrator.stores.focus.update(Editor.Focus.empty()) }
|
||||
|
@ -1919,12 +1917,12 @@ class EditorViewModel(
|
|||
|
||||
private fun onAddLocalVideoClicked(blockId: String) {
|
||||
mediaBlockId = blockId
|
||||
dispatch(Command.OpenGallery(mediaType = MIME_VIDEO_ALL))
|
||||
dispatch(Command.OpenGallery(mimeType = MIME_VIDEO_ALL))
|
||||
}
|
||||
|
||||
private fun onAddLocalPictureClicked(blockId: String) {
|
||||
mediaBlockId = blockId
|
||||
dispatch(Command.OpenGallery(mediaType = MIME_IMAGE_ALL))
|
||||
dispatch(Command.OpenGallery(mimeType = MIME_IMAGE_ALL))
|
||||
}
|
||||
|
||||
fun onTogglePlaceholderClicked(target: Id) {
|
||||
|
@ -1951,7 +1949,7 @@ class EditorViewModel(
|
|||
|
||||
private fun onAddLocalFileClicked(blockId: String) {
|
||||
mediaBlockId = blockId
|
||||
dispatch(Command.OpenGallery(mediaType = MIME_FILE_ALL))
|
||||
dispatch(Command.OpenGallery(mimeType = MIME_FILE_ALL))
|
||||
}
|
||||
|
||||
fun onAddFileBlockClicked(type: Content.File.Type) {
|
||||
|
@ -2802,16 +2800,6 @@ class EditorViewModel(
|
|||
)
|
||||
}
|
||||
|
||||
fun onDownloadClicked() {
|
||||
Timber.d("onDownloadClicked, ")
|
||||
val block = blocks.firstOrNull { it.content is Content.File }
|
||||
if (block != null) {
|
||||
dispatch(Command.RequestDownloadPermission(block.id))
|
||||
} else {
|
||||
Timber.e("onDownloadClicked, file not found in object")
|
||||
}
|
||||
}
|
||||
|
||||
fun onLayoutDialogDismissed() {
|
||||
Timber.d("onLayoutDialogDismissed, ")
|
||||
proceedWithOpeningObjectMenu()
|
||||
|
@ -5389,4 +5377,40 @@ class EditorViewModel(
|
|||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region COPY FILE TO CACHE
|
||||
val copyFileStatus = MutableSharedFlow<CopyFileStatus>(replay = 0)
|
||||
|
||||
fun onStartCopyFileToCacheDir(uri: Uri) {
|
||||
copyFileToCache.execute(
|
||||
uri = uri,
|
||||
scope = viewModelScope,
|
||||
listener = copyFileListener
|
||||
)
|
||||
}
|
||||
|
||||
fun onCancelCopyFileToCacheDir() {
|
||||
copyFileToCache.cancel()
|
||||
}
|
||||
|
||||
private val copyFileListener = object : OnCopyFileToCacheAction {
|
||||
override fun onCopyFileStart() {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Started)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCopyFileResult(result: String?) {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Completed(result))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCopyFileError(msg: String) {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Error(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
}
|
|
@ -24,6 +24,7 @@ import com.anytypeio.anytype.presentation.common.StateReducer
|
|||
import com.anytypeio.anytype.presentation.editor.editor.DetailModificationManager
|
||||
import com.anytypeio.anytype.presentation.editor.editor.Orchestrator
|
||||
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
|
||||
open class EditorViewModelFactory(
|
||||
|
@ -51,11 +52,12 @@ open class EditorViewModelFactory(
|
|||
private val objectTypesProvider: ObjectTypesProvider,
|
||||
private val searchObjects: SearchObjects,
|
||||
private val getDefaultEditorType: GetDefaultEditorType,
|
||||
private val findObjectSetForType: FindObjectSetForType
|
||||
private val findObjectSetForType: FindObjectSetForType,
|
||||
private val copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return EditorViewModel(
|
||||
openPage = openPage,
|
||||
closePage = closeObject,
|
||||
|
@ -81,7 +83,8 @@ open class EditorViewModelFactory(
|
|||
searchObjects = searchObjects,
|
||||
getDefaultEditorType = getDefaultEditorType,
|
||||
findObjectSetForType = findObjectSetForType,
|
||||
createObjectSet = createObjectSet
|
||||
createObjectSet = createObjectSet,
|
||||
copyFileToCache = copyFileToCacheDirectory
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -54,7 +54,7 @@ class LinkAddViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return LinkAddViewModel(unlink) as T
|
||||
}
|
||||
}
|
|
@ -27,7 +27,7 @@ open class ArchiveViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ArchiveViewModel(
|
||||
openPage = openPage,
|
||||
closePage = closePage,
|
||||
|
|
|
@ -22,7 +22,7 @@ class CreateBookmarkViewModel() : ViewStateViewModel<ViewState>() {
|
|||
class Factory() : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T =
|
||||
CreateBookmarkViewModel() as T
|
||||
}
|
||||
}
|
|
@ -203,7 +203,7 @@ class SelectCoverObjectViewModel(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SelectCoverObjectViewModel(
|
||||
setCoverColor = setCoverColor,
|
||||
setCoverImage = setCoverImage,
|
||||
|
@ -246,7 +246,7 @@ class SelectCoverObjectSetViewModel(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SelectCoverObjectSetViewModel(
|
||||
setCoverColor = setCoverColor,
|
||||
setCoverImage = setCoverImage,
|
||||
|
|
|
@ -18,7 +18,7 @@ sealed class Command {
|
|||
) : Command()
|
||||
|
||||
data class OpenGallery(
|
||||
val mediaType: String
|
||||
val mimeType: String
|
||||
) : Command()
|
||||
|
||||
data class OpenBookmarkSetter(
|
||||
|
@ -43,10 +43,6 @@ sealed class Command {
|
|||
val excludedTypes: List<String> = emptyList()
|
||||
) : Command()
|
||||
|
||||
data class RequestDownloadPermission(
|
||||
val id: String
|
||||
) : Command()
|
||||
|
||||
data class OpenFileByDefaultApp(
|
||||
val id: String,
|
||||
val mime: String,
|
||||
|
|
|
@ -97,7 +97,7 @@ class ObjectLayoutViewModel(
|
|||
private val storage: Editor.Storage
|
||||
): ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectLayoutViewModel(
|
||||
dispatcher = dispatcher,
|
||||
setObjectLayout = setObjectLayout,
|
||||
|
|
|
@ -65,7 +65,7 @@ class DocumentAddBlockViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return DocumentAddBlockViewModel(getObjectTypes) as T
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ class DocumentIconActionMenuViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T = DocumentIconActionMenuViewModel(
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = DocumentIconActionMenuViewModel(
|
||||
setEmojiIcon = setEmojiIcon,
|
||||
setImageIcon = setImageIcon,
|
||||
dispatcher = dispatcher,
|
||||
|
|
|
@ -20,7 +20,7 @@ class ObjectIconPickerViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectIconPickerViewModel(
|
||||
setEmojiIcon = setEmojiIcon,
|
||||
setImageIcon = setImageIcon,
|
||||
|
@ -42,7 +42,7 @@ class ObjectSetIconPickerViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectSetIconPickerViewModel(
|
||||
setEmojiIcon = setEmojiIcon,
|
||||
setImageIcon = setImageIcon,
|
||||
|
|
|
@ -30,7 +30,7 @@ class KeychainPhraseViewModelFactory(
|
|||
private val getMnemonic: GetMnemonic
|
||||
) : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return KeychainPhraseViewModel(
|
||||
getMnemonic = getMnemonic
|
||||
) as T
|
||||
|
|
|
@ -15,7 +15,7 @@ class LinkToObjectViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return LinkToObjectViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectTypes = getObjectTypes,
|
||||
|
@ -33,7 +33,7 @@ class LinkToObjectOrWebViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return LinkToObjectOrWebViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectTypes = getObjectTypes,
|
||||
|
|
|
@ -14,7 +14,7 @@ class MainViewModelFactory(
|
|||
private val restoreWallpaper: RestoreWallpaper
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>
|
||||
): T = MainViewModel(
|
||||
launchAccount = launchAccount,
|
||||
|
|
|
@ -15,7 +15,7 @@ class MoveToViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return MoveToViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectTypes = getObjectTypes,
|
||||
|
|
|
@ -17,7 +17,7 @@ class PageNavigationViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return PageNavigationViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectInfoWithLinks = getObjectInfoWithLinks,
|
||||
|
|
|
@ -45,7 +45,7 @@ class CreateObjectViewModel(private val createPage: CreatePage) : ViewModel(){
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CreateObjectViewModel(createPage = createPage) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -274,7 +274,7 @@ class ObjectMenuViewModel(
|
|||
private val analytics: Analytics,
|
||||
private val dispatcher: Dispatcher<Payload>
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectMenuViewModel(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
addToFavorite = addToFavorite,
|
||||
|
@ -312,7 +312,7 @@ class ObjectSetMenuViewModel(
|
|||
private val analytics: Analytics,
|
||||
private val state: StateFlow<ObjectSet>
|
||||
) : ViewModelProvider.Factory {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectSetMenuViewModel(
|
||||
setObjectIsArchived = setObjectIsArchived,
|
||||
addToFavorite = addToFavorite,
|
||||
|
|
|
@ -9,7 +9,7 @@ class ObjectTypeChangeViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectTypeChangeViewModel(
|
||||
getCompatibleObjectTypes = getCompatibleObjectTypes
|
||||
) as T
|
||||
|
|
|
@ -15,7 +15,7 @@ class ProfileViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ProfileViewModel(
|
||||
logout = logout,
|
||||
getCurrentAccount = getCurrentAccount,
|
||||
|
|
|
@ -404,7 +404,7 @@ class RelationOptionValueDVAddViewModel(
|
|||
private val dispatcher: Dispatcher<Payload>,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationOptionValueDVAddViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
|
@ -570,7 +570,7 @@ class RelationOptionValueAddViewModel(
|
|||
private val dispatcher: Dispatcher<Payload>,
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationOptionValueAddViewModel(
|
||||
details = details,
|
||||
values = values,
|
||||
|
|
|
@ -26,7 +26,7 @@ class ObjectRelationListViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationListViewModel(
|
||||
stores = stores,
|
||||
urlBuilder = urlBuilder,
|
||||
|
|
|
@ -113,7 +113,7 @@ class RelationAddToObjectViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationAddToObjectViewModel(
|
||||
addRelationToObject = addRelationToObject,
|
||||
objectRelationList = objectRelationList,
|
||||
|
@ -202,7 +202,7 @@ class RelationAddToDataViewViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationAddToDataViewViewModel(
|
||||
addRelationToDataView = addRelationToDataView,
|
||||
objectRelationList = objectRelationList,
|
||||
|
|
|
@ -104,7 +104,7 @@ class RelationCreateFromScratchForObjectViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationCreateFromScratchForObjectViewModel(
|
||||
dispatcher = dispatcher,
|
||||
addNewRelationToObject = addNewRelationToObject,
|
||||
|
@ -157,7 +157,7 @@ class RelationCreateFromScratchForObjectBlockViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationCreateFromScratchForObjectBlockViewModel(
|
||||
dispatcher = dispatcher,
|
||||
addNewRelationToObject = addNewRelationToObject,
|
||||
|
@ -247,7 +247,7 @@ class RelationCreateFromScratchForDataViewViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationCreateFromScratchForDataViewViewModel(
|
||||
dispatcher = dispatcher,
|
||||
addNewRelationToDataView = addNewRelationToDataView,
|
||||
|
|
|
@ -163,7 +163,7 @@ class RelationFileValueAddViewModel(
|
|||
private val urlBuilder: UrlBuilder
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationFileValueAddViewModel(
|
||||
relations = relations,
|
||||
values = values,
|
||||
|
|
|
@ -194,7 +194,7 @@ class RelationObjectValueAddViewModel(
|
|||
private val objectTypesProvider: ObjectTypesProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationObjectValueAddViewModel(
|
||||
relations = relations,
|
||||
values = values,
|
||||
|
|
|
@ -259,7 +259,7 @@ class ViewerRelationsViewModel(
|
|||
private val deleteRelationFromDataView: DeleteRelationFromDataView
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerRelationsViewModel(
|
||||
objectSetState = state,
|
||||
session = session,
|
||||
|
|
|
@ -15,7 +15,7 @@ class ObjectSearchViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectSearchViewModel(
|
||||
urlBuilder = urlBuilder,
|
||||
getObjectTypes = getObjectTypes,
|
||||
|
|
|
@ -82,7 +82,7 @@ class CreateDataViewViewerViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CreateDataViewViewerViewModel(
|
||||
addDataViewViewer = addDataViewViewer,
|
||||
dispatcher = dispatcher,
|
||||
|
|
|
@ -118,7 +118,7 @@ class CreateObjectSetViewModel(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CreateObjectSetViewModel(
|
||||
getObjectTypes = getObjectTypes,
|
||||
createObjectSet = createObjectSet,
|
||||
|
|
|
@ -44,7 +44,7 @@ class CreateObjectTypeViewModel : ViewModel() {
|
|||
|
||||
class Factory : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CreateObjectTypeViewModel() as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class DataViewViewerActionViewModel(
|
|||
private val objectSetState: StateFlow<ObjectSet>
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return DataViewViewerActionViewModel(
|
||||
duplicateDataViewViewer = duplicateDataViewViewer,
|
||||
deleteDataViewViewer = deleteDataViewViewer,
|
||||
|
|
|
@ -233,7 +233,7 @@ class EditDataViewViewerViewModel(
|
|||
private val objectSetSession: ObjectSetSession
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return EditDataViewViewerViewModel(
|
||||
renameDataViewViewer = renameDataViewViewer,
|
||||
deleteDataViewViewer = deleteDataViewViewer,
|
||||
|
|
|
@ -101,7 +101,7 @@ class ManageViewerViewModel(
|
|||
private val setActiveViewer: SetActiveViewer
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ManageViewerViewModel(
|
||||
state = state,
|
||||
session = session,
|
||||
|
|
|
@ -51,7 +51,7 @@ class ObjectSetRecordViewModel(
|
|||
private val objectSetRecordCache: ObjectSetRecordCache
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectSetRecordViewModel(
|
||||
objectSetState = objectSetState,
|
||||
updateDataViewRecord = updateDataViewRecord,
|
||||
|
|
|
@ -32,7 +32,7 @@ class ObjectSetViewModelFactory(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ObjectSetViewModel(
|
||||
reducer = reducer,
|
||||
openObjectSet = openObjectSet,
|
||||
|
|
|
@ -135,7 +135,7 @@ class RelationDateValueViewModel(
|
|||
private val values: ObjectValueProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationDateValueViewModel(relations, values) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ class RelationTextValueViewModel(
|
|||
private val values: ObjectValueProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return RelationTextValueViewModel(relations, values) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.anytypeio.anytype.presentation.sets
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
@ -22,7 +23,10 @@ import com.anytypeio.anytype.presentation.objects.getProperName
|
|||
import com.anytypeio.anytype.presentation.relations.providers.ObjectDetailProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectRelationProvider
|
||||
import com.anytypeio.anytype.presentation.relations.providers.ObjectValueProvider
|
||||
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 kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
|
@ -33,7 +37,8 @@ abstract class RelationValueBaseViewModel(
|
|||
private val values: ObjectValueProvider,
|
||||
private val details: ObjectDetailProvider,
|
||||
private val types: ObjectTypesProvider,
|
||||
private val urlBuilder: UrlBuilder
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val copyFileToCache: CopyFileToCacheDirectory
|
||||
) : BaseViewModel() {
|
||||
|
||||
val navigation = MutableSharedFlow<AppNavigation.Command>()
|
||||
|
@ -304,6 +309,42 @@ abstract class RelationValueBaseViewModel(
|
|||
}
|
||||
}
|
||||
|
||||
//region COPY FILE TO CACHE
|
||||
val copyFileStatus = MutableSharedFlow<CopyFileStatus>(replay = 0)
|
||||
|
||||
fun onStartCopyFileToCacheDir(uri: Uri) {
|
||||
copyFileToCache.execute(
|
||||
uri = uri,
|
||||
scope = viewModelScope,
|
||||
listener = copyFileListener
|
||||
)
|
||||
}
|
||||
|
||||
fun onCancelCopyFileToCacheDir() {
|
||||
copyFileToCache.cancel()
|
||||
}
|
||||
|
||||
private val copyFileListener = object : OnCopyFileToCacheAction {
|
||||
override fun onCopyFileStart() {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Started)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCopyFileResult(result: String?) {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Completed(result))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCopyFileError(msg: String) {
|
||||
viewModelScope.launch {
|
||||
copyFileStatus.emit(CopyFileStatus.Error(msg))
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion
|
||||
|
||||
sealed class ObjectRelationValueCommand {
|
||||
object ShowAddStatusOrTagScreen : ObjectRelationValueCommand()
|
||||
object ShowAddObjectScreen : ObjectRelationValueCommand()
|
||||
|
@ -384,15 +425,16 @@ class RelationValueDVViewModel(
|
|||
private val removeTagFromDataViewRecord: RemoveTagFromDataViewRecord,
|
||||
private val removeStatusFromDataViewRecord: RemoveStatusFromDataViewRecord,
|
||||
private val updateDataViewRecord: UpdateDataViewRecord,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val addFileToRecord: AddFileToRecord
|
||||
private val addFileToRecord: AddFileToRecord,
|
||||
copyFileToCache: CopyFileToCacheDirectory
|
||||
) : RelationValueBaseViewModel(
|
||||
relations = relations,
|
||||
values = values,
|
||||
details = details,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder
|
||||
urlBuilder = urlBuilder,
|
||||
copyFileToCache = copyFileToCache
|
||||
) {
|
||||
|
||||
fun onRemoveTagFromDataViewRecordClicked(
|
||||
|
@ -582,27 +624,27 @@ class RelationValueDVViewModel(
|
|||
class Factory(
|
||||
private val relations: ObjectRelationProvider,
|
||||
private val values: ObjectValueProvider,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val details: ObjectDetailProvider,
|
||||
private val types: ObjectTypesProvider,
|
||||
private val updateDataViewRecord: UpdateDataViewRecord,
|
||||
private val removeTagFromRecord: RemoveTagFromDataViewRecord,
|
||||
private val removeStatusFromDataViewRecord: RemoveStatusFromDataViewRecord,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val addFileToRecord: AddFileToRecord
|
||||
private val addFileToRecord: AddFileToRecord,
|
||||
private val copyFileToCache: CopyFileToCacheDirectory
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T = RelationValueDVViewModel(
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = RelationValueDVViewModel(
|
||||
relations = relations,
|
||||
values = values,
|
||||
details = details,
|
||||
types = types,
|
||||
dispatcher = dispatcher,
|
||||
removeTagFromDataViewRecord = removeTagFromRecord,
|
||||
removeStatusFromDataViewRecord = removeStatusFromDataViewRecord,
|
||||
urlBuilder = urlBuilder,
|
||||
updateDataViewRecord = updateDataViewRecord,
|
||||
addFileToRecord = addFileToRecord
|
||||
addFileToRecord = addFileToRecord,
|
||||
copyFileToCache = copyFileToCache
|
||||
) as T
|
||||
}
|
||||
}
|
||||
|
@ -615,13 +657,15 @@ class RelationValueViewModel(
|
|||
private val updateDetail: UpdateDetail,
|
||||
private val dispatcher: Dispatcher<Payload>,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val addFileToObject: AddFileToObject
|
||||
private val addFileToObject: AddFileToObject,
|
||||
copyFileToCache: CopyFileToCacheDirectory
|
||||
) : RelationValueBaseViewModel(
|
||||
relations = relations,
|
||||
values = values,
|
||||
details = details,
|
||||
types = types,
|
||||
urlBuilder = urlBuilder
|
||||
urlBuilder = urlBuilder,
|
||||
copyFileToCache = copyFileToCache
|
||||
) {
|
||||
|
||||
fun onObjectValueOrderChanged(
|
||||
|
@ -791,10 +835,11 @@ class RelationValueViewModel(
|
|||
private val types: ObjectTypesProvider,
|
||||
private val updateDetail: UpdateDetail,
|
||||
private val urlBuilder: UrlBuilder,
|
||||
private val addFileToObject: AddFileToObject
|
||||
private val addFileToObject: AddFileToObject,
|
||||
private val copyFileToCache: CopyFileToCacheDirectory
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T = RelationValueViewModel(
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T = RelationValueViewModel(
|
||||
relations = relations,
|
||||
values = values,
|
||||
details = details,
|
||||
|
@ -802,7 +847,8 @@ class RelationValueViewModel(
|
|||
dispatcher = dispatcher,
|
||||
updateDetail = updateDetail,
|
||||
urlBuilder = urlBuilder,
|
||||
addFileToObject = addFileToObject
|
||||
addFileToObject = addFileToObject,
|
||||
copyFileToCache = copyFileToCache
|
||||
) as T
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ class SelectFilterRelationViewModel(
|
|||
private val session: ObjectSetSession
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SelectFilterRelationViewModel(
|
||||
objectSetState = state,
|
||||
session = session
|
||||
|
|
|
@ -51,7 +51,7 @@ class SelectSortRelationViewModel(
|
|||
private val addDataViewViewerSort: AddDataViewViewerSort
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SelectSortRelationViewModel(
|
||||
objectSetState = state,
|
||||
session = session,
|
||||
|
|
|
@ -45,7 +45,7 @@ class ViewerCustomizeViewModel(private val state: StateFlow<ObjectSet>) : ViewMo
|
|||
private val state: StateFlow<ObjectSet>
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerCustomizeViewModel(state) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -159,7 +159,7 @@ class ViewerSortByViewModel(private val state: StateFlow<ObjectSet>) : ViewModel
|
|||
class Factory(private val state: StateFlow<ObjectSet>) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerSortByViewModel(state = state) as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -706,7 +706,7 @@ open class FilterViewModel(
|
|||
private val objectTypesProvider: ObjectTypesProvider
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return FilterViewModel(
|
||||
objectSetState = objectSetState,
|
||||
session = session,
|
||||
|
|
|
@ -40,7 +40,7 @@ class PickFilterConditionViewModel : BaseViewModel() {
|
|||
|
||||
class Factory() : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return PickFilterConditionViewModel() as T
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ class ViewerFilterViewModel(
|
|||
private val urlBuilder: UrlBuilder
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerFilterViewModel(
|
||||
objectSetState = state,
|
||||
session = session,
|
||||
|
|
|
@ -95,7 +95,7 @@ class ModifyViewerSortViewModel(
|
|||
private val updateDataViewViewer: UpdateDataViewViewer
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ModifyViewerSortViewModel(
|
||||
objectSetState = state,
|
||||
dispatcher = dispatcher,
|
||||
|
|
|
@ -132,7 +132,7 @@ class ViewerSortViewModel(
|
|||
private val updateDataViewViewer: UpdateDataViewViewer
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerSortViewModel(
|
||||
objectSetState = state,
|
||||
session = session,
|
||||
|
|
|
@ -102,7 +102,7 @@ class ViewerCardSizeSelectViewModel(
|
|||
private val updateDataViewViewer: UpdateDataViewViewer
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerCardSizeSelectViewModel(
|
||||
objectSetState = objectSetState,
|
||||
session = session,
|
||||
|
|
|
@ -98,7 +98,7 @@ class ViewerImagePreviewSelectViewModel(
|
|||
private val updateDataViewViewer: UpdateDataViewViewer
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return ViewerImagePreviewSelectViewModel(
|
||||
objectSetState = objectSetState,
|
||||
session = session,
|
||||
|
|
|
@ -128,7 +128,7 @@ class OtherSettingsViewModel(
|
|||
private val analytics: Analytics
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(
|
||||
override fun <T : ViewModel> create(
|
||||
modelClass: Class<T>
|
||||
): T = OtherSettingsViewModel(
|
||||
getDefaultEditorType = getDefaultEditorType,
|
||||
|
|
|
@ -32,7 +32,7 @@ class SplashViewModelFactory(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T =
|
||||
SplashViewModel(
|
||||
checkAuthorizationStatus = checkAuthorizationStatus,
|
||||
launchAccount = launchAccount,
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
package com.anytypeio.anytype.presentation.util
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.OpenableColumns
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.single
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.lang.Exception
|
||||
import java.lang.ref.WeakReference
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
interface CopyFileToCacheDirectory {
|
||||
|
||||
fun execute(uri: Uri, scope: CoroutineScope, listener: OnCopyFileToCacheAction)
|
||||
fun cancel()
|
||||
}
|
||||
|
||||
const val SCHEME_CONTENT = "content"
|
||||
const val CHAR_SLASH = '/'
|
||||
const val TEMPORARY_DIRECTORY_NAME = "TemporaryFiles"
|
||||
|
||||
sealed class CopyFileStatus {
|
||||
data class Error(val msg: String) : CopyFileStatus()
|
||||
object Started: CopyFileStatus()
|
||||
data class Completed(val result: String?): CopyFileStatus()
|
||||
}
|
||||
|
||||
class DefaultCopyFileToCacheDirectory(context: Context) : CopyFileToCacheDirectory {
|
||||
|
||||
private var mContext: WeakReference<Context>? = null
|
||||
private var job: Job? = null
|
||||
|
||||
init {
|
||||
mContext = WeakReference(context)
|
||||
}
|
||||
|
||||
override fun execute(uri: Uri, scope: CoroutineScope, listener: OnCopyFileToCacheAction) {
|
||||
getNewPathInCacheDir(uri, scope, listener)
|
||||
}
|
||||
|
||||
override fun cancel() {
|
||||
job?.cancel()
|
||||
mContext?.get()?.deleteTemporaryFolder()
|
||||
}
|
||||
|
||||
private fun getNewPathInCacheDir(
|
||||
uri: Uri,
|
||||
scope: CoroutineScope,
|
||||
listener: OnCopyFileToCacheAction
|
||||
) {
|
||||
var path: String? = null
|
||||
job = scope.launch {
|
||||
try {
|
||||
val time = measureTimeMillis {
|
||||
path = flow {
|
||||
emit(copyFileToCacheDir(uri, job, listener))
|
||||
}
|
||||
.flowOn(Dispatchers.IO)
|
||||
.single()
|
||||
}
|
||||
Timber.d("Total load file time: $time")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Error while getNewPathInCacheDir")
|
||||
listener.onCopyFileError(e.localizedMessage ?: "Unknown error")
|
||||
} finally {
|
||||
if (scope.isActive) {
|
||||
listener.onCopyFileResult(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyFileToCacheDir(
|
||||
uri: Uri,
|
||||
job: Job?,
|
||||
listener: OnCopyFileToCacheAction
|
||||
): String? {
|
||||
var newFile: File? = null
|
||||
mContext?.get()?.let { context: Context ->
|
||||
val cacheDir = context.getExternalFilesDirTemp()
|
||||
if (cacheDir != null && !cacheDir.exists()) {
|
||||
cacheDir.mkdirs()
|
||||
}
|
||||
try {
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
inputStream?.use { input ->
|
||||
newFile = File(cacheDir?.path + "/" + getFileName(context, uri));
|
||||
listener.onCopyFileStart()
|
||||
Timber.d("Start copy file to cache : ${newFile?.path}")
|
||||
FileOutputStream(newFile).use { output ->
|
||||
job?.ensureActive()
|
||||
val buffer = ByteArray(1024)
|
||||
var read: Int = input.read(buffer)
|
||||
while (read != -1) {
|
||||
job?.ensureActive()
|
||||
output.write(buffer, 0, read)
|
||||
read = input.read(buffer)
|
||||
}
|
||||
}
|
||||
return newFile?.path
|
||||
}
|
||||
} 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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
uri.path?.let { path ->
|
||||
val cut = path.lastIndexOf(CHAR_SLASH)
|
||||
if (cut != -1) {
|
||||
result = path.substring(cut)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the /storage/emulated/0/Android/data/package/files/$TEMPORARY_DIRECTORY_NAME folder.
|
||||
*/
|
||||
private fun Context.deleteTemporaryFolder() {
|
||||
getExternalFilesDirTemp()?.let { folder ->
|
||||
if (folder.deleteRecursively()) {
|
||||
Timber.d("${folder.absolutePath} delete successfully")
|
||||
} else {
|
||||
Timber.d("${folder.absolutePath} delete is unsuccessfully")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return /storage/emulated/0/Android/data/package/files/$TEMPORARY_DIRECTORY_NAME directory
|
||||
*/
|
||||
private fun Context.getExternalFilesDirTemp(): File? = getExternalFilesDir(TEMPORARY_DIRECTORY_NAME)
|
||||
|
||||
interface OnCopyFileToCacheAction {
|
||||
fun onCopyFileStart()
|
||||
fun onCopyFileResult(result: String?)
|
||||
fun onCopyFileError(msg: String)
|
||||
}
|
|
@ -66,7 +66,7 @@ class WallpaperSelectViewModel(
|
|||
) : ViewModelProvider.Factory {
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return WallpaperSelectViewModel(
|
||||
setWallpaper = setWallpaper
|
||||
) as T
|
||||
|
|
|
@ -349,42 +349,4 @@ class BlockReadModeTest : EditorViewModelTest() {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `should enter edit mode after action menu is closed by action item download`() {
|
||||
|
||||
val paragraphs = blocks
|
||||
stubObserveEvents(flow)
|
||||
stubOpenPage()
|
||||
buildViewModel()
|
||||
|
||||
vm.onStart(root)
|
||||
|
||||
coroutineTestRule.advanceTime(100)
|
||||
|
||||
// TESTING
|
||||
|
||||
vm.onClickListener(
|
||||
clicked = ListenerType.LongClick(
|
||||
target = paragraphs[1].id,
|
||||
dimensions = BlockDimensions(0, 0, 0, 0, 0, 0)
|
||||
)
|
||||
)
|
||||
|
||||
vm.onActionMenuItemClicked(id = paragraphs[1].id, action = ActionItemType.Download)
|
||||
|
||||
val testObserver = vm.state.test()
|
||||
|
||||
val initial = blockViewsEditMode
|
||||
|
||||
coroutineTestRule.advanceTime(EditorViewModel.TEXT_CHANGES_DEBOUNCE_DURATION)
|
||||
|
||||
runBlockingTest {
|
||||
testObserver.assertValue(
|
||||
ViewState.Success(
|
||||
blocks = listOf(titleEditModeView) + initial
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
|
|||
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
|
||||
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
|
||||
import com.anytypeio.anytype.presentation.navigation.AppNavigation
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.CoroutinesTestRule
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import com.anytypeio.anytype.presentation.util.TXT
|
||||
|
@ -226,6 +227,9 @@ open class EditorViewModelTest {
|
|||
@Mock
|
||||
lateinit var createObjectSet: CreateObjectSet
|
||||
|
||||
@Mock
|
||||
lateinit var copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
|
||||
private lateinit var updateDetail: UpdateDetail
|
||||
|
||||
lateinit var vm: EditorViewModel
|
||||
|
@ -3980,7 +3984,8 @@ open class EditorViewModelTest {
|
|||
objectTypesProvider = objectTypesProvider,
|
||||
searchObjects = searchObjects,
|
||||
findObjectSetForType = findObjectSetForType,
|
||||
createObjectSet = createObjectSet
|
||||
createObjectSet = createObjectSet,
|
||||
copyFileToCache = copyFileToCacheDirectory
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ import com.anytypeio.anytype.presentation.editor.editor.pattern.DefaultPatternMa
|
|||
import com.anytypeio.anytype.presentation.editor.render.DefaultBlockViewRenderer
|
||||
import com.anytypeio.anytype.presentation.editor.selection.SelectionStateHolder
|
||||
import com.anytypeio.anytype.presentation.editor.toggle.ToggleStateHolder
|
||||
import com.anytypeio.anytype.presentation.util.CopyFileToCacheDirectory
|
||||
import com.anytypeio.anytype.presentation.util.Dispatcher
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
|
@ -196,6 +197,9 @@ open class EditorPresentationTestSetup {
|
|||
@Mock
|
||||
lateinit var createObjectSet: CreateObjectSet
|
||||
|
||||
@Mock
|
||||
lateinit var copyFileToCacheDirectory: CopyFileToCacheDirectory
|
||||
|
||||
private val builder: UrlBuilder get() = UrlBuilder(gateway)
|
||||
|
||||
private lateinit var updateDetail: UpdateDetail
|
||||
|
@ -281,7 +285,8 @@ open class EditorPresentationTestSetup {
|
|||
searchObjects = searchObjects,
|
||||
getDefaultEditorType = getDefaultEditorType,
|
||||
findObjectSetForType = findObjectSetForType,
|
||||
createObjectSet = createObjectSet
|
||||
createObjectSet = createObjectSet,
|
||||
copyFileToCache = copyFileToCacheDirectory
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,14 +4,14 @@ apply plugin: 'kotlin-android-extensions'
|
|||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileSdkVersion 29
|
||||
buildToolsVersion "29.0.3"
|
||||
compileSdkVersion 31
|
||||
buildToolsVersion "31.0.0"
|
||||
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.anytypeio.anytype.sample"
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 29
|
||||
targetSdkVersion 31
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
|
||||
|
@ -39,12 +39,12 @@ android {
|
|||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
sourceCompatibility JavaVersion.VERSION_11
|
||||
targetCompatibility JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
jvmTarget = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,13 +66,10 @@ dependencies {
|
|||
implementation applicationDependencies.timber
|
||||
implementation applicationDependencies.fragment
|
||||
implementation applicationDependencies.design
|
||||
implementation applicationDependencies.permissionDisp
|
||||
implementation applicationDependencies.pickT
|
||||
|
||||
//implementation 'com.github.gregcockroft:AndroidMath:ALPHA'
|
||||
|
||||
kapt applicationDependencies.permissionDispCompiler
|
||||
|
||||
testImplementation unitTestDependencies.junit
|
||||
testImplementation unitTestDependencies.kotlinTest
|
||||
testImplementation unitTestDependencies.robolectricLatest
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue