Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,7 @@ dependencies {
androidTestRuntimeOnly(libs.android.test.runner)

implementation(core.coil.gif)
implementation(kotlin("reflect"))

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding kotlin-reflect to the app increases APK size and can add startup/runtime overhead. Since the current usage is to enumerate sealed subclasses for DriveError, consider replacing the reflection-based registry with an explicit listOf(...)/mapOf(key -> DriveError) so this dependency can be avoided.

Suggested change
implementation(kotlin("reflect"))

Copilot uses AI. Check for mistakes.

// Compose
implementation(libs.androidx.ui.android)
Expand Down
9 changes: 7 additions & 2 deletions app/src/main/java/com/infomaniak/drive/data/api/UploadTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ class UploadTask(
okHttpClient = uploadFile.okHttpClient
)
if (!closedSessionResponse.isSuccess()) closedSessionResponse.manageUploadErrors()
else uploadFile.updateUploadErrorKey(null)
}
}

Expand Down Expand Up @@ -412,6 +413,8 @@ class UploadTask(
ApiResponse<Any>(error = ApiError(description = bodyResponse))
}
apiResponse.manageUploadErrors()
} else {
uploadFile.updateUploadErrorKey(null)
}
}

Expand Down Expand Up @@ -473,15 +476,17 @@ class UploadTask(
)

return ApiRepository.startUploadSession(driveId, sessionBody, okHttpClient).also {
if (it.isSuccess()) it.data?.token?.let { uploadToken ->
uploadFile.updateUploadToken(uploadToken, it.data!!.uploadHost)
if (it.isSuccess()) it.data?.run {
uploadFile.updateUploadToken(token, uploadHost)
uploadFile.updateUploadErrorKey(null)
} else {
it.manageUploadErrors()
Comment thread
benjaminVadon marked this conversation as resolved.
}
Comment thread
benjaminVadon marked this conversation as resolved.
}.data?.uploadHost
}

private fun <T> ApiResponse<T>.manageUploadErrors() {
uploadFile.updateUploadErrorKey(error?.code)
if (error?.exception is ApiControllerNetworkException) throw NetworkException()
Comment thread
benjaminVadon marked this conversation as resolved.
Outdated
when (error?.code) {
"file_already_exists_error" -> Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import com.infomaniak.drive.data.api.ApiRepository
import com.infomaniak.drive.data.api.UploadTask
import com.infomaniak.drive.data.api.UploadTask.Companion.ConflictOption
import com.infomaniak.drive.data.cache.DriveInfosController
import com.infomaniak.drive.data.models.up.DriveError
import com.infomaniak.drive.data.sync.UploadMigration
import com.infomaniak.drive.utils.AccountUtils
import com.infomaniak.drive.utils.IOFile
Expand Down Expand Up @@ -59,6 +60,7 @@ open class UploadFile(
@PrimaryKey var uri: String = "",
var deletedAt: Date? = null,
var driveId: Int = -1,
private var _driveErrorKey: String? = null,
var fileCreatedAt: Date? = null,
var fileModifiedAt: Date = Date(),
var fileName: String = "",
Expand All @@ -72,6 +74,9 @@ open class UploadFile(
var userId: Int = -1,
) : RealmObject() {

val error: DriveError?
get() = DriveError.find(_driveErrorKey)

@delegate:Ignore
val okHttpClient: OkHttpClient by lazy { runBlocking { AccountUtils.getHttpClient(userId = userId, timeout = 120) } }

Expand Down Expand Up @@ -138,6 +143,10 @@ open class UploadFile(
}
}

fun updateUploadErrorKey(driveErrorKey: String?) {
update { it._driveErrorKey = driveErrorKey }
}

fun deleteIfExists(keepFile: Boolean = false, customRealm: Realm? = null) {
when (customRealm) {
null -> getRealmInstance().use { deleteIfExistsInternal(keepFile = keepFile, it) }
Expand Down
133 changes: 133 additions & 0 deletions app/src/main/java/com/infomaniak/drive/data/models/up/DriveError.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.infomaniak.drive.data.models.up

import androidx.annotation.PluralsRes
import androidx.annotation.StringRes
import com.infomaniak.drive.R
import com.infomaniak.drive.data.models.up.DriveError.Server.ServerError
import com.infomaniak.drive.data.models.up.DriveError.Server.ServerPlurals
import splitties.init.appCtx
import kotlin.reflect.KClass

sealed interface DriveError {
val key: String

fun interface Plurals {
fun message(quantity: Int): String
}

interface Message {
val message: String
}

sealed class Local(
override val key: String,
@field:StringRes private val id: Int = R.string.errorGeneric
) : DriveError, Message {
override val message: String
get() = appCtx.getString(id)

object CachingFailed : Local(key = "cachingFailed", id = R.string.errorCache)
object DownloadFailed : Local(key = "downloadFailed", id = R.string.errorDownload)
object ErrorDeviceStorage : Local(key = "errorDeviceStorage", id = R.string.errorDeviceStorage)
object FileNotFound : Local(key = "fileNotFound")
object LocalError : Local(key = "localError")
object MoveLocalError : Local(key = "moveLocalError", id = R.string.errorMove)
object PhotoAssetNoLongerExists : Local(key = "photoAssetNoLongerExists")
object PhotoLibraryWriteAccessDenied : Local(key = "writeAccessDenied", id = R.string.errorPhotoLibraryAccessLimited)
object SearchCancelled : Local(key = "searchCancelled")
object TaskCancelled : Local(key = "taskCancelled")
object TaskExpirationCancelled : Local(key = "taskExpirationCancelled")
object TaskRescheduled : Local(key = "taskRescheduled")
object UnknownError : Local(key = "unknownError")
object UnknownToken : Local(key = "unknownToken")
object UploadOverDataRestricted : Local(key = "uploadOverDataRestricted", id = R.string.uploadOverDataRestrictedError)
}

sealed interface Network : DriveError, Message {
override val key: String
get() = "networkError"
override val message: String
get() = appCtx.getString(R.string.errorNetwork)

object NetworkError : Network
}

sealed interface Server : DriveError {
sealed class ServerPlurals(
override val key: String,
@field:PluralsRes private val id: Int
) : Server, Plurals {
override fun message(quantity: Int): String = appCtx.resources.getQuantityString(id, quantity, quantity)
}

sealed class ServerError(
override val key: String,
@field:StringRes private val id: Int = R.string.errorGeneric
) : Server, Message {
override val message: String
get() = appCtx.getString(id)
}

object CategoryAlreadyExists : ServerError(key = "category_already_exist_error", id = R.string.errorCategoryAlreadyExists)
object Conflict : ServerError(key = "conflict_error", id = R.string.errorConflict)
object DestinationAlreadyExists : ServerError(key = "destination_already_exists", id = R.string.errorFileAlreadyExists)
object DownloadPermission : ServerError(key = "you_must_add_at_least_one_file", id = R.string.errorDownloadPermission)
object DriveMaintenance : ServerError(key = "drive_is_in_maintenance_error", id = R.string.driveMaintenanceDescription)
object FileAlreadyExistsError : ServerError(key = "file_already_exists_error", id = R.string.errorFileAlreadyExists)
object Forbidden : ServerError(key = "forbidden_error", id = R.string.accessDeniedTitle)
object InvalidCursorError : ServerError(key = "invalid_cursor_error")
object InvalidUploadTokenError : ServerError(key = "invalid_upload_token_error")
object LimitExceededError : ServerError(key = "limit_exceeded_error", id = R.string.errorLimitExceeded)
object LockError : ServerError(key = "lock_error", id = R.string.errorFileLocked)
object NoDrive : ServerError(key = "no_drive")
object NotAuthorized : ServerError(key = "not_authorized")
object ObjectNotFound : ServerError(key = "object_not_found", id = R.string.uploadFolderNotFoundError)
object ProductBlocked : ServerPlurals(key = "product_blocked", id = R.plurals.driveBlockedDescription)
object ProductMaintenance : ServerError(key = "product_maintenance", id = R.string.driveMaintenanceDescription)
object QuotaExceededError : ServerError(key = "quota_exceeded_error", id = R.string.notEnoughStorageDescription1)
object RefreshToken : ServerError(key = "refreshToken")
object Generic : ServerError(key = "serverError")
object ShareLinkAlreadyExists : ServerError(key = "file_share_link_already_exists", id = R.string.errorShareLink)
object StillUploadingError : ServerError(key = "still_uploading_error", id = R.string.errorStillUploading)
object UploadDestinationNotFoundError : ServerError(key = "upload_destination_not_found_error")
object UploadDestinationNotWritableError : ServerError(key = "upload_destination_not_writable_error")
object UploadError : ServerError(key = "upload_error")
object UploadFailedError : ServerError(key = "upload_failed_error")
object UploadNotTerminated : ServerError(key = "upload_not_terminated")
object UploadNotTerminatedError : ServerError(key = "upload_not_terminated_error")
object UploadTokenCanceled : ServerError(key = "upload_token_canceled")
object UploadTokenIsNotValid : ServerError(key = "upload_token_is_not_valid")
}


companion object {
fun find(key: String?): DriveError? = key?.let { allValues[key] }

private val allValues: Map<String, DriveError> by lazy { buildAllValuesList() }

private fun buildAllValuesList(): Map<String, DriveError> {
return Local::class.values() + Network::class.values() + ServerError::class.values() + ServerPlurals::class.values()
}

private inline fun <reified T : DriveError> KClass<T>.values(): Map<String, DriveError> =
sealedSubclasses.map { it.objectInstance as DriveError }.associateBy { it.key }

Comment on lines +120 to +131

Copilot AI Apr 22, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DriveError builds its registry via Kotlin reflection (sealedSubclasses/objectInstance) and then does a linear search in find(). This is brittle (will crash if a non-object subclass is added) and requires kotlin-reflect. Prefer an explicit Map<String, DriveError> registry (can be generated manually) to remove reflection and guarantee O(1) lookups. Also note that many server keys here duplicate data/api/ErrorCode.kt; reusing that source of truth would reduce drift.

Copilot uses AI. Check for mistakes.
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Infomaniak kDrive - Android
* Copyright (C) 2022-2024 Infomaniak Network SA
* Copyright (C) 2022-2026 Infomaniak Network SA
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
Expand Down Expand Up @@ -116,9 +116,13 @@ class UploadMigration : RealmMigration {
?.addField("onlyWifiSyncMedia", Boolean::class.java, FieldAttribute.REQUIRED)
}

if (oldVersion < 9L) { //stop use of oldVersionTemp as it is useless
schema.get(UploadFile::class.java.simpleName)
?.addField("_driveErrorKey", String::class.java)
}
}

companion object {
const val DB_VERSION = 8L // Must be bumped when the schema changes
const val DB_VERSION = 9L // Must be bumped when the schema changes
}
}
3 changes: 3 additions & 0 deletions app/src/main/res/values-da/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
<string name="errorFileAlreadyExists">Filen eller mappen findes allerede</string>
<string name="errorFileLocked">Filen er i øjeblikket åben af en anden bruger og kan ikke ændres</string>
<string name="errorFilesLimitExceeded">Grænsen for filer i mappen nået</string>
<string name="errorGeneric">Der er opstået en fejl</string>
<string name="errorGetBookmarkURL">Ikke-gendannelseslink</string>
<string name="errorLeaveShare">Fejl ved afslutning af deling</string>
<string name="errorLimitExceeded">Grænsen nået</string>
Expand All @@ -277,6 +278,7 @@
<string name="errorMove">Flytningsfejl</string>
<string name="errorNetwork">Netværksfejl</string>
<string name="errorNetworkDescription">Resultaterne kan være ufuldstændige</string>
<string name="errorPhotoLibraryAccessLimited">kDrive har ikke adgang til dit fotobibliotek</string>
<string name="errorPreviewDeleted">Denne fil er blevet slettet permanent</string>
<string name="errorPreviewTrash">Forhåndsvisning af en fil i papirkurven er ikke mulig</string>
<string name="errorQuotaExceeded">Ikke nok plads på din kDrive</string>
Expand Down Expand Up @@ -760,6 +762,7 @@
<string name="uploadNetworkErrorTitle">Upload suspenderet</string>
<string name="uploadNetworkErrorWifiRequired">Venter på WiFi-netværk</string>
<string name="uploadOutOfMemoryError">Utilstrækkelig tilgængelig RAM-hukommelse</string>
<string name="uploadOverDataRestrictedError">Venter på Wi-Fi</string>
<string name="uploadPausedDescription">Nogle uploads er stadig i gang i kDrive. Åbn appen og hold enheden tilsluttet for at fremskynde upload.</string>
<string name="uploadPausedTitle">Upload suspenderet</string>
<string name="uploadPermissionError">Yderligere tilladelser anmodet</string>
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
<string name="errorFileAlreadyExists">Datei oder Ordner existiert bereits</string>
<string name="errorFileLocked">Diese Datei ist bereits von einem Benutzer geöffnet und kann nicht verändert werden</string>
<string name="errorFilesLimitExceeded">Limit der Dateien im Ordner erreicht</string>
<string name="errorGeneric">Ein Fehler ist aufgetreten</string>
<string name="errorGetBookmarkURL">Link nicht wiederherstellbar</string>
<string name="errorLeaveShare">Fehler beim Abbrechen der Freigabe</string>
<string name="errorLimitExceeded">Limit erreicht</string>
Expand All @@ -277,6 +278,7 @@
<string name="errorMove">Fehler beim Verschieben</string>
<string name="errorNetwork">Verbindungsproblem</string>
<string name="errorNetworkDescription">Die Ergebnisse können unvollständig sein</string>
<string name="errorPhotoLibraryAccessLimited">kDrive hat keinen Zugriff auf Ihre Fotobibliothek</string>
<string name="errorPreviewDeleted">Diese Datei wurde endgültig gelöscht</string>
<string name="errorPreviewTrash">Die Vorschau auf eine Datei im Papierkorb ist nicht verfügbar</string>
<string name="errorQuotaExceeded">Unzureichender Platz auf Ihrem kDrive</string>
Expand Down Expand Up @@ -760,6 +762,7 @@
<string name="uploadNetworkErrorTitle">Import ausgesetzt</string>
<string name="uploadNetworkErrorWifiRequired">Warten auf WLAN</string>
<string name="uploadOutOfMemoryError">Unzureichend verfügbarer RAM</string>
<string name="uploadOverDataRestrictedError">Warten auf Wi-Fi</string>
<string name="uploadPausedDescription">Einige Uploads sind in kDrive noch im Gange. Öffnen Sie die App und lassen Sie das Gerät verbunden, um den Upload zu beschleunigen.</string>
<string name="uploadPausedTitle">Pause importieren</string>
<string name="uploadPermissionError">Zusätzlich beantragte Genehmigungen</string>
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values-el/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
<string name="errorFileAlreadyExists">Το αρχείο ή ο φάκελος υπάρχει ήδη</string>
<string name="errorFileLocked">Αυτό το αρχείο είναι αυτή τη στιγμή ανοιχτό από άλλον χρήστη και δεν μπορεί να τροποποιηθεί</string>
<string name="errorFilesLimitExceeded">Έχει επιτευχθεί το όριο αρχείων στον φάκελο</string>
<string name="errorGeneric">Παρουσιάστηκε σφάλμα</string>
<string name="errorGetBookmarkURL">Σύνδεσμος μη ανακτήσιμος</string>
<string name="errorLeaveShare">Σφάλμα κατά τη διακοπή της κοινής χρήσης</string>
<string name="errorLimitExceeded">Υπέρβαση ορίου</string>
Expand All @@ -277,6 +278,7 @@
<string name="errorMove">Σφάλμα μετακίνησης</string>
<string name="errorNetwork">Σφάλμα δικτύου</string>
<string name="errorNetworkDescription">Τα αποτελέσματα ενδέχεται να είναι ελλιπή</string>
<string name="errorPhotoLibraryAccessLimited">Το kDrive δεν έχει πρόσβαση στη βιβλιοθήκη φωτογραφιών σας</string>
<string name="errorPreviewDeleted">Αυτό το αρχείο διαγράφηκε οριστικά</string>
<string name="errorPreviewTrash">Η προεπισκόπηση αρχείου στον κάδο δεν είναι δυνατή</string>
<string name="errorQuotaExceeded">Δεν υπάρχει αρκετός χώρος στο kDrive σας</string>
Expand Down Expand Up @@ -760,6 +762,7 @@
<string name="uploadNetworkErrorTitle">Η μεταφόρτωση ανεστάλη</string>
<string name="uploadNetworkErrorWifiRequired">Αναμονή για δίκτυο Wi-Fi</string>
<string name="uploadOutOfMemoryError">Ανεπαρκής διαθέσιμη μνήμη RAM</string>
<string name="uploadOverDataRestrictedError">Αναμονή για Wi-Fi</string>
<string name="uploadPausedDescription">Κάποιες μεταφορτώσεις εξακολουθούν να βρίσκονται σε εξέλιξη στο kDrive. Ανοίξτε την εφαρμογή και κρατήστε τη συσκευή συνδεδεμένη για να επιταχύνετε τη μεταφόρτωση.</string>
<string name="uploadPausedTitle">Η μεταφόρτωση ανεστάλη</string>
<string name="uploadPermissionError">Ζητούνται πρόσθετες άδειες</string>
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@
<string name="errorFileAlreadyExists">Archivo o carpeta ya existente</string>
<string name="errorFileLocked">El archivo está actualmente abierto por un usuario y no puede ser modificado</string>
<string name="errorFilesLimitExceeded">Límite de archivos alcanzado en la carpeta</string>
<string name="errorGeneric">Se ha producido un error</string>
<string name="errorGetBookmarkURL">Enlace no recuperable</string>
<string name="errorLeaveShare">Error al salir del uso compartido</string>
<string name="errorLimitExceeded">Límite alcanzado</string>
Expand All @@ -277,6 +278,7 @@
<string name="errorMove">Error al moverlo</string>
<string name="errorNetwork">Problema de conexión</string>
<string name="errorNetworkDescription">Los resultados pueden ser incompletos</string>
<string name="errorPhotoLibraryAccessLimited">kDrive no tiene acceso a tu biblioteca de fotos</string>
<string name="errorPreviewDeleted">Se ha eliminado definitivamente este archivo</string>
<string name="errorPreviewTrash">La vista previa de un archivo en la papelera no está disponible</string>
<string name="errorQuotaExceeded">Espacio insuficiente en tu kDrive</string>
Expand Down Expand Up @@ -760,6 +762,7 @@
<string name="uploadNetworkErrorTitle">Importación suspendida</string>
<string name="uploadNetworkErrorWifiRequired">A la espera de red WiFi</string>
<string name="uploadOutOfMemoryError">Insuficiente memoria RAM disponible</string>
<string name="uploadOverDataRestrictedError">A la espera de Wi-Fi</string>
<string name="uploadPausedDescription">Algunas subidas siguen en curso en kDrive. Abre la aplicación y mantén el dispositivo conectado para acelerar la carga.</string>
<string name="uploadPausedTitle">Pausa de importación</string>
<string name="uploadPermissionError">Permisos adicionales solicitados</string>
Expand Down
Loading
Loading