Skip to content

Commit

Permalink
feat: Show local import errors
Browse files Browse the repository at this point in the history
  • Loading branch information
LouisCAD committed Jan 2, 2025
1 parent be6f357 commit 6b89b3d
Show file tree
Hide file tree
Showing 6 changed files with 33 additions and 39 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ dependencies {
implementation(libs.androidx.core.splashscreen)
implementation(libs.recaptcha)
implementation(libs.workmanager)
implementation(libs.splitties.toast)

// Test
testImplementation(libs.junit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,7 @@ import com.infomaniak.swisstransfer.ui.utils.GetSetCallbacks
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -78,7 +76,6 @@ class ImportFilesViewModel @Inject constructor(
initialValue = emptyList(),
)

val failedFiles = importationFilesManager.failedFiles // TODO ? (unused)
val filesToImportCount = importationFilesManager.filesToImportCount
val currentSessionFilesCount = importationFilesManager.currentSessionFilesCount

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package com.infomaniak.swisstransfer.ui.screen.newtransfer

import android.content.Context
import com.infomaniak.sentry.SentryLog
import dagger.hilt.android.qualifiers.ApplicationContext
import java.io.File
import java.io.InputStream
Expand All @@ -39,20 +38,12 @@ class ImportLocalStorage @Inject constructor(@ApplicationContext private val app

fun getLocalFiles(): Array<File>? = importFolder.listFiles()

fun copyUriDataLocally(inputStream: InputStream, fileName: String): File? {
val file = File(getImportFolderOrCreate(), fileName)

if (file.exists()) file.delete()
runCatching { file.createNewFile() }.onFailure { return null }

runCatching {
fun copyUriDataLocally(inputStream: InputStream, fileName: String): Result<File> = runCatching {
File(getImportFolderOrCreate(), fileName).also { file ->
if (file.exists()) file.delete()
file.createNewFile()
copyStreams(inputStream, file.outputStream())
}.onFailure {
SentryLog.w(TAG, "Caught an exception while copying file to local storage: $it")
return null
}

return file
}

private fun copyStreams(inputStream: InputStream, outputStream: OutputStream): Long {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,17 @@ import androidx.core.net.toUri
import com.infomaniak.library.filetypes.FileType
import com.infomaniak.multiplatform_swisstransfer.common.interfaces.ui.FileUi
import com.infomaniak.sentry.SentryLog
import com.infomaniak.swisstransfer.R
import com.infomaniak.swisstransfer.ui.utils.FileNameUtils
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.android.scopes.ViewModelScoped
import io.sentry.Sentry
import io.sentry.SentryLevel
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.invoke
import splitties.toast.UnreliableToastApi
import splitties.toast.longToast
import java.io.File
import java.io.InputStream
import javax.inject.Inject
Expand All @@ -51,9 +54,6 @@ class ImportationFilesManager @Inject constructor(
private val _importedFiles = FilesMutableStateFlow()
val importedFiles = _importedFiles.flow

private val _failedFiles = MutableSharedFlow<PickedFile>()
val failedFiles = _failedFiles.asSharedFlow()

// Importing a file locally can take up time. We can't base the list of already used names on _importedFiles's value because a
// new import with the same name could occur while the file is still importing. This would lead to a name collision.
// This list needs to mark a name as "taken" as soon as the file is queued to be imported and until the file is removed from
Expand Down Expand Up @@ -92,12 +92,11 @@ class ImportationFilesManager @Inject constructor(
suspend fun continuouslyCopyPickedFilesToLocalStorage() {
filesToImportChannel.consume { fileToImport ->
SentryLog.i(TAG, "Importing ${fileToImport.uri}")
val copiedFile = copyUriDataLocally(fileToImport.uri, fileToImport.fileName)

if (copiedFile == null) {
reportFailedImportation(fileToImport)
return@consume
}
val copiedFile = Dispatchers.IO {
copyUriDataLocally(fileToImport.uri, fileToImport.fileName).onFailure {
reportFailedImportation(fileToImport, it)
}.getOrNull()
} ?: return@consume

SentryLog.i(TAG, "Successfully imported ${fileToImport.uri}")

Expand All @@ -115,17 +114,17 @@ class ImportationFilesManager @Inject constructor(
}
}

private fun copyUriDataLocally(uri: Uri, fileName: String): File? {
val inputStream = openInputStream(uri) ?: return null
private fun copyUriDataLocally(
uri: Uri,
fileName: String
): Result<File> = openInputStream(uri).mapCatching { inputStream ->
return importLocalStorage.copyUriDataLocally(inputStream, fileName)
}

private fun openInputStream(uri: Uri): InputStream? {
return runCatching { appContext.contentResolver.openInputStream(uri) }
.onSuccess {
if (it == null) SentryLog.w(ImportLocalStorage.TAG, "During local copy of the file openInputStream returned null")
}
.getOrNull()
private fun openInputStream(uri: Uri): Result<InputStream> = runCatching {
appContext.contentResolver.openInputStream(uri)
}.mapCatching {
it ?: throw NullPointerException("The provider recently crashed")
}

private fun getRestoredFileUi(localFiles: Array<File>): List<FileUi> {
Expand Down Expand Up @@ -190,9 +189,13 @@ class ImportationFilesManager @Inject constructor(
return getColumnIndex(column).takeIf { it != -1 }
}

private suspend fun reportFailedImportation(file: PickedFile) {
SentryLog.e(TAG, "Failed importation of ${file.uri}")
_failedFiles.emit(file)
private fun reportFailedImportation(file: PickedFile, throwable: Throwable) {
SentryLog.e(TAG, "Failed importation of ${file.uri}", throwable)

//TODO: Make more precise error messages (especially for the low storage issue).
val errorMessage = appContext.getString(R.string.cant_import_file_x, file.fileName)
@OptIn(UnreliableToastApi::class)
longToast(errorMessage) //TODO: Find a better way to show the error in an actionable way.
}

data class PickedFile(val fileName: String, val fileSizeInBytes: Long, val uri: Uri)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<string name="buttonFinished">Terminer</string>
<string name="buttonShare">Partager</string>
<string name="buttonStart">Démarrer</string>
<string name="cant_import_file_x">Can\'t import the file %s</string>
<string name="contentDescriptionButtonBack">Retour</string>
<string name="contentDescriptionButtonClose">Fermer</string>
<string name="contentDescriptionButtonHidePassword">Cacher le mot de passe</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
<string name="buttonFinished">Finish</string>
<string name="buttonShare">Share</string>
<string name="buttonStart">Start</string>
<string name="cant_import_file_x">Can\'t import the file %s</string>
<string name="contentDescriptionButtonBack">Back</string>
<string name="contentDescriptionButtonClose">Close</string>
<string name="contentDescriptionButtonHidePassword">Hide password</string>
Expand Down

0 comments on commit 6b89b3d

Please sign in to comment.