diff --git a/storage-api/src/main/java/com/omh/android/storage/api/OmhStorageClient.kt b/storage-api/src/main/java/com/omh/android/storage/api/OmhStorageClient.kt index abdebb38..70c9b4ca 100644 --- a/storage-api/src/main/java/com/omh/android/storage/api/OmhStorageClient.kt +++ b/storage-api/src/main/java/com/omh/android/storage/api/OmhStorageClient.kt @@ -1,10 +1,19 @@ package com.omh.android.storage.api import com.omh.android.auth.api.OmhAuthClient +import com.omh.android.auth.api.async.OmhTask +import com.omh.android.storage.api.async.OmhStorageTaskImpl import com.omh.android.storage.api.domain.repository.OmhFileRepository import com.omh.android.storage.api.domain.usecase.CreateFileUseCase +import com.omh.android.storage.api.domain.usecase.CreateFileUseCaseParams +import com.omh.android.storage.api.domain.usecase.CreateFileUseCaseResult import com.omh.android.storage.api.domain.usecase.DeleteFileUseCase +import com.omh.android.storage.api.domain.usecase.DeleteFileUseCaseParams +import com.omh.android.storage.api.domain.usecase.DeleteFileUseCaseResult import com.omh.android.storage.api.domain.usecase.GetFilesListUseCase +import com.omh.android.storage.api.domain.usecase.GetFilesListUseCaseParams +import com.omh.android.storage.api.domain.usecase.GetFilesListUseCaseResult +import com.omh.android.storage.api.domain.usecase.OmhResult abstract class OmhStorageClient protected constructor( protected val authClient: OmhAuthClient @@ -17,15 +26,34 @@ abstract class OmhStorageClient protected constructor( protected abstract fun getRepository(): OmhFileRepository - /* - * TODO: This must return an asynchronous task that can be executed with any library - * capable to manage this. In the future, this task must be implemented from auth - * and will not return an use case - */ - @SuppressWarnings("ForbiddenComment") - fun listFiles() = GetFilesListUseCase(getRepository()) + fun listFiles(parentId: String): OmhTask { + val getFilesListUseCase = GetFilesListUseCase(getRepository()) + return OmhStorageTaskImpl { + val parameters = GetFilesListUseCaseParams(parentId) + val result: OmhResult = getFilesListUseCase(parameters) + result + } + } - fun createFile() = CreateFileUseCase(getRepository()) + fun createFile( + name: String, + mimeType: String, + parentId: String + ): OmhTask { + val createFileUseCase = CreateFileUseCase(getRepository()) + return OmhStorageTaskImpl { + val parameters = CreateFileUseCaseParams(name, mimeType, parentId) + val result: OmhResult = createFileUseCase(parameters) + result + } + } - fun deleteFile() = DeleteFileUseCase(getRepository()) + fun deleteFile(id: String): OmhTask { + val deleteFileUseCase = DeleteFileUseCase(getRepository()) + return OmhStorageTaskImpl { + val parameters = DeleteFileUseCaseParams(id) + val result = deleteFileUseCase(parameters) + result + } + } } diff --git a/storage-api/src/main/java/com/omh/android/storage/api/async/OmhStorageTaskImpl.kt b/storage-api/src/main/java/com/omh/android/storage/api/async/OmhStorageTaskImpl.kt new file mode 100644 index 00000000..c012cf3e --- /dev/null +++ b/storage-api/src/main/java/com/omh/android/storage/api/async/OmhStorageTaskImpl.kt @@ -0,0 +1,42 @@ +package com.omh.android.storage.api.async + +import com.omh.android.auth.api.async.OmhCancellable +import com.omh.android.auth.api.async.OmhTask +import com.omh.android.storage.api.domain.usecase.OmhResult +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +internal class OmhStorageTaskImpl(private val task: suspend () -> OmhResult) : OmhTask() { + + private val coroutineContext = Dispatchers.Main + SupervisorJob() + private val customScope: CoroutineScope = CoroutineScope(context = coroutineContext) + + override fun execute(): OmhCancellable { + customScope.launch { + executeScopedTask() + } + return OmhCancellable { coroutineContext.cancelChildren() } + } + + @SuppressWarnings("TooGenericExceptionCaught") + private suspend fun executeScopedTask() { + when (val result: OmhResult = task()) { + is OmhResult.OmhSuccess -> executeSuccess(result.data) + is OmhResult.OmhError -> executeFailure(result.exception) + } + } + + private suspend fun executeFailure(e: Exception) = withContext(Dispatchers.Main) { + onFailure?.invoke(e) + } + + private suspend fun executeSuccess(data: T) { + withContext(Dispatchers.Main) { + onSuccess?.invoke(data) + } + } +} diff --git a/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/BaseViewModel.kt b/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/BaseViewModel.kt index 93dc8754..0083325e 100644 --- a/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/BaseViewModel.kt +++ b/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/BaseViewModel.kt @@ -4,6 +4,7 @@ import android.util.Log import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.omh.android.auth.api.async.CancellableCollector import com.omh.android.storage.sample.util.LOG_MESSAGE_EVENT import com.omh.android.storage.sample.util.TAG_VIEW_UPDATE import com.omh.android.storage.sample.util.launchSafe @@ -12,6 +13,7 @@ abstract class BaseViewModel : ViewModel() val state: MutableLiveData = MutableLiveData() val toastMessage: MutableLiveData = MutableLiveData() + protected val cancellableCollector = CancellableCollector() init { setInitialState() @@ -37,4 +39,9 @@ abstract class BaseViewModel : ViewModel() protected fun setState(state: State) { this.state.postValue(state) } + + override fun onCleared() { + super.onCleared() + cancellableCollector.clear() + } } diff --git a/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/file_viewer/FileViewerViewModel.kt b/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/file_viewer/FileViewerViewModel.kt index af9ab991..02072a66 100644 --- a/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/file_viewer/FileViewerViewModel.kt +++ b/storage-sample/src/main/java/com/omh/android/storage/sample/presentation/file_viewer/FileViewerViewModel.kt @@ -3,14 +3,6 @@ package com.omh.android.storage.sample.presentation.file_viewer import com.omh.android.storage.api.OmhStorageClient import com.omh.android.storage.api.domain.model.OmhFile import com.omh.android.storage.api.domain.model.OmhFileType -import com.omh.android.storage.api.domain.usecase.CreateFileUseCase -import com.omh.android.storage.api.domain.usecase.CreateFileUseCaseParams -import com.omh.android.storage.api.domain.usecase.DeleteFileUseCase -import com.omh.android.storage.api.domain.usecase.DeleteFileUseCaseParams -import com.omh.android.storage.api.domain.usecase.DeleteFileUseCaseResult -import com.omh.android.storage.api.domain.usecase.GetFilesListUseCase -import com.omh.android.storage.api.domain.usecase.GetFilesListUseCaseParams -import com.omh.android.storage.api.domain.usecase.OmhResult import com.omh.android.storage.sample.domain.model.FileType import com.omh.android.storage.sample.presentation.BaseViewModel import dagger.hilt.android.lifecycle.HiltViewModel @@ -51,28 +43,25 @@ class FileViewerViewModel @Inject constructor( } } - private suspend fun initializeEvent() { + private fun initializeEvent() { refreshFileListEvent() } - private suspend fun refreshFileListEvent() { + private fun refreshFileListEvent() { setState(FileViewerViewState.Loading) val parentId = parentIdStack.peek() - val listFilesUseCase: GetFilesListUseCase = omhStorageClient.listFiles() - - when ( - val result = listFilesUseCase(GetFilesListUseCaseParams(parentId)) - ) { - is OmhResult.OmhSuccess -> { - setState(FileViewerViewState.Content(result.data.files)) + val cancellable = omhStorageClient.listFiles(parentId) + .addOnSuccess { data -> + val files: List = data.files + setState(FileViewerViewState.Content(files)) } - - is OmhResult.OmhError -> { - toastMessage.postValue(result.toString()) + .addOnFailure { e -> + toastMessage.postValue(e.message) setState(FileViewerViewState.Content(emptyList())) } - } + .execute() + cancellableCollector.addCancellable(cancellable) } private fun swapLayoutManagerEvent() { @@ -80,7 +69,7 @@ class FileViewerViewModel @Inject constructor( setState(FileViewerViewState.SwapLayoutManager) } - private suspend fun fileClickedEvent(event: FileViewerViewEvent.FileClicked) { + private fun fileClickedEvent(event: FileViewerViewEvent.FileClicked) { val file = event.file if (file.isFolder()) { @@ -93,7 +82,7 @@ class FileViewerViewModel @Inject constructor( } } - private suspend fun backPressedEvent() { + private fun backPressedEvent() { if (parentIdStack.peek() == ID_ROOT) { setState(FileViewerViewState.Finish) } else { @@ -102,52 +91,41 @@ class FileViewerViewModel @Inject constructor( } } - private suspend fun createFileEvent(event: FileViewerViewEvent.CreateFile) { + private fun createFileEvent(event: FileViewerViewEvent.CreateFile) { setState(FileViewerViewState.Loading) val parentId = parentIdStack.peek() - val createFileUseCase: CreateFileUseCase = omhStorageClient.createFile() - - when ( - val result = - createFileUseCase(CreateFileUseCaseParams(event.name, event.mimeType, parentId)) - ) { - is OmhResult.OmhSuccess -> { + val cancellable = omhStorageClient.createFile(event.name, event.mimeType, parentId) + .addOnSuccess { refreshFileListEvent() } - - is OmhResult.OmhError -> { - toastMessage.postValue(result.toString()) + .addOnFailure { e -> + toastMessage.postValue(e.message) refreshFileListEvent() } - } + .execute() + cancellableCollector.addCancellable(cancellable) } - private suspend fun deleteFileEvent(event: FileViewerViewEvent.DeleteFile) { + private fun deleteFileEvent(event: FileViewerViewEvent.DeleteFile) { setState(FileViewerViewState.Loading) val file = event.file - val deleteFileUseCase: DeleteFileUseCase = omhStorageClient.deleteFile() - - when (val result = deleteFileUseCase(DeleteFileUseCaseParams(file.id))) { - is OmhResult.OmhSuccess -> { - handleDeleteSuccess(result, file) + val cancellable = omhStorageClient.deleteFile(file.id) + .addOnSuccess { data -> + handleDeleteSuccess(data.isSuccess, file) } - - is OmhResult.OmhError -> { + .addOnFailure { toastMessage.postValue("ERROR: ${file.name} was NOT deleted") } - } - + .execute() + cancellableCollector.addCancellable(cancellable) refreshFileListEvent() } - private fun handleDeleteSuccess( - result: OmhResult.OmhSuccess, - file: OmhFile - ) { - val toastText = if (result.data.isSuccess) { + private fun handleDeleteSuccess(isSuccessful: Boolean, file: OmhFile) { + val toastText = if (isSuccessful) { "${file.name} was successfully deleted" } else { "${file.name} was NOT deleted"