From 9a491e92bc41702d75800a0d669b1be38e0ef310 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 4 Feb 2026 14:57:49 +0100 Subject: [PATCH 01/83] chore: version bump to v3.36.0 rc1 Signed-off-by: alperozturk96 --- app/build.gradle.kts | 2 +- fastlane/metadata/android/en-US/changelogs/30360051.txt | 9 +++++++++ .../android/en-US/changelogs/30360051.txt.license | 2 ++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/30360051.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30360051.txt.license diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8e3717619d62..2c3f654276c7 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -65,7 +65,7 @@ configurations.configureEach { val versionMajor = 3 val versionMinor = 36 val versionPatch = 0 -val versionBuild = 0 // 0-50=Alpha / 51-98=RC / 90-99=stable +val versionBuild = 51 // 0-50=Alpha / 51-98=RC / 90-99=stable val ndkEnv = buildMap { file("${project.rootDir}/ndk.env").readLines().forEach { diff --git a/fastlane/metadata/android/en-US/changelogs/30360051.txt b/fastlane/metadata/android/en-US/changelogs/30360051.txt new file mode 100644 index 000000000000..cec3850930db --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30360051.txt @@ -0,0 +1,9 @@ +## 3.36.0 RC1 (February 4, 2026) + +- Streamlined sharing flow and refined UI +- Enhanced Nextcloud Assistant with chat and translation support +- Bug fixes and performance improvements + +Minimum: NC 20 Server, Android 9 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/120 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30360051.txt.license b/fastlane/metadata/android/en-US/changelogs/30360051.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30360051.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file From b94e76cfe000dc683378f50e7c2f03d682af226a Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 4 Feb 2026 15:19:13 +0100 Subject: [PATCH 02/83] chore: version bump rc-2.23.0 android library Signed-off-by: alperozturk96 --- gradle/libs.versions.toml | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8618391638e9..847f6925246a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ androidCommonLibraryVersion = "4fc0f29981" androidGifDrawableVersion = "1.2.30" androidImageCropperVersion = "4.7.0" -androidLibraryVersion ="38328f338c00870c6a062188974f8f30a61fb450" +androidLibraryVersion ="rc-2.23.0" androidPluginVersion = "9.0.0" androidsvgVersion = "1.4" androidxMediaVersion = "1.5.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 37b90dc159d7..637fe2a94f9a 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -21748,6 +21748,14 @@ + + + + + + + + From 55a53c815a8128d93a215536c03f0a3a567c6d36 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 4 Feb 2026 16:39:24 +0100 Subject: [PATCH 03/83] fix(db): migration 97 to 98 Signed-off-by: alperozturk96 --- .../client/database/NextcloudDatabase.kt | 6 ++-- .../database/migrations/Migration97to98.kt | 34 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/database/migrations/Migration97to98.kt diff --git a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt index 3001ca445c57..c82192516324 100644 --- a/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt +++ b/app/src/main/java/com/nextcloud/client/database/NextcloudDatabase.kt @@ -38,6 +38,7 @@ import com.nextcloud.client.database.entity.UploadEntity import com.nextcloud.client.database.entity.VirtualEntity import com.nextcloud.client.database.migrations.DatabaseMigrationUtil import com.nextcloud.client.database.migrations.MIGRATION_88_89 +import com.nextcloud.client.database.migrations.MIGRATION_97_98 import com.nextcloud.client.database.migrations.Migration67to68 import com.nextcloud.client.database.migrations.RoomMigration import com.nextcloud.client.database.migrations.addLegacyMigrations @@ -92,8 +93,8 @@ import com.owncloud.android.db.ProviderMeta AutoMigration(from = 93, to = 94, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 94, to = 95, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), AutoMigration(from = 95, to = 96), - AutoMigration(from = 96, to = 97, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class), - AutoMigration(from = 97, to = 98) + AutoMigration(from = 96, to = 97, spec = DatabaseMigrationUtil.ResetCapabilitiesPostMigration::class) + // manual migration used for 97 to 98 ], exportSchema = true ) @@ -131,6 +132,7 @@ abstract class NextcloudDatabase : RoomDatabase() { .addMigrations(RoomMigration()) .addMigrations(Migration67to68()) .addMigrations(MIGRATION_88_89) + .addMigrations(MIGRATION_97_98) .build() } return instance!! diff --git a/app/src/main/java/com/nextcloud/client/database/migrations/Migration97to98.kt b/app/src/main/java/com/nextcloud/client/database/migrations/Migration97to98.kt new file mode 100644 index 000000000000..6a35c00ccabd --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/database/migrations/Migration97to98.kt @@ -0,0 +1,34 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.database.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.db.SupportSQLiteDatabase +import com.nextcloud.client.database.migrations.model.SQLiteColumnType +import com.owncloud.android.db.ProviderMeta + +@Suppress("MagicNumber") +val MIGRATION_97_98 = object : Migration(97, 98) { + override fun migrate(db: SupportSQLiteDatabase) { + DatabaseMigrationUtil.addColumnIfNotExists( + db, + ProviderMeta.ProviderTableMeta.UPLOADS_TABLE_NAME, + ProviderMeta.ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP_LONG, + SQLiteColumnType.INTEGER_DEFAULT_NULL + ) + + DatabaseMigrationUtil.addColumnIfNotExists( + db, + ProviderMeta.ProviderTableMeta.CAPABILITIES_TABLE_NAME, + ProviderMeta.ProviderTableMeta.CAPABILITIES_CLIENT_INTEGRATION_JSON, + SQLiteColumnType.TEXT_DEFAULT_NULL + ) + + DatabaseMigrationUtil.resetCapabilities(db) + } +} From 891944bbb4c90d0bc5099f7e679ff5de8b9f4ccf Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 5 Feb 2026 03:10:24 +0000 Subject: [PATCH 04/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-ar/strings.xml | 2 -- app/src/main/res/values-ast/strings.xml | 1 - app/src/main/res/values-b+en+001/strings.xml | 2 -- app/src/main/res/values-bg-rBG/strings.xml | 1 - app/src/main/res/values-ca/strings.xml | 1 - app/src/main/res/values-cs-rCZ/strings.xml | 2 -- app/src/main/res/values-da/strings.xml | 2 -- app/src/main/res/values-de/strings.xml | 2 -- app/src/main/res/values-el/strings.xml | 1 - app/src/main/res/values-eo/strings.xml | 1 - app/src/main/res/values-es-rAR/strings.xml | 1 - app/src/main/res/values-es-rCO/strings.xml | 1 - app/src/main/res/values-es-rEC/strings.xml | 1 - app/src/main/res/values-es-rMX/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 2 -- app/src/main/res/values-et-rEE/strings.xml | 2 -- app/src/main/res/values-eu/strings.xml | 2 -- app/src/main/res/values-fa/strings.xml | 1 - app/src/main/res/values-fi-rFI/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 2 -- app/src/main/res/values-ga/strings.xml | 2 -- app/src/main/res/values-gl/strings.xml | 2 -- app/src/main/res/values-hr/strings.xml | 2 +- app/src/main/res/values-hu-rHU/strings.xml | 2 -- app/src/main/res/values-in/strings.xml | 2 -- app/src/main/res/values-is/strings.xml | 2 -- app/src/main/res/values-it/strings.xml | 2 -- app/src/main/res/values-iw/strings.xml | 1 - app/src/main/res/values-ja-rJP/strings.xml | 2 -- app/src/main/res/values-ko/strings.xml | 2 -- app/src/main/res/values-lo/strings.xml | 2 -- app/src/main/res/values-lt-rLT/strings.xml | 2 -- app/src/main/res/values-lv/strings.xml | 2 -- app/src/main/res/values-mk/strings.xml | 1 - app/src/main/res/values-nb-rNO/strings.xml | 2 -- app/src/main/res/values-nl/strings.xml | 2 -- app/src/main/res/values-pl/strings.xml | 2 -- app/src/main/res/values-pt-rBR/strings.xml | 2 -- app/src/main/res/values-pt-rPT/strings.xml | 1 - app/src/main/res/values-ro/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 2 -- app/src/main/res/values-sk-rSK/strings.xml | 2 -- app/src/main/res/values-sl/strings.xml | 1 - app/src/main/res/values-sq/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 2 -- app/src/main/res/values-sv/strings.xml | 2 -- app/src/main/res/values-sw/strings.xml | 2 -- app/src/main/res/values-th-rTH/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 2 -- app/src/main/res/values-ug/strings.xml | 2 -- app/src/main/res/values-uk/strings.xml | 4 +--- app/src/main/res/values-vi/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 2 -- app/src/main/res/values-zh-rHK/strings.xml | 2 -- app/src/main/res/values-zh-rTW/strings.xml | 2 -- 55 files changed, 2 insertions(+), 91 deletions(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index c6e590e08397..fbf3ced8709e 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -702,7 +702,6 @@ السمة الفسحة الزمنية إدارة المجلدات الداخلية للمزامنة في الاتجاهين - تمكين المزامنة في الاتجاهين غامق فاتح اتبع النظام @@ -990,7 +989,6 @@ حمل الملفات كل عمليات الرفع مجمدة زر إجراء رفع العنصر - إلغاء الرفع حذف لا توجد تحميلات قم بتحميل بعض الملفات أو قم بتفعيل التحميل التلقائي. diff --git a/app/src/main/res/values-ast/strings.xml b/app/src/main/res/values-ast/strings.xml index 4d9cc07ca1a9..e93297c10552 100644 --- a/app/src/main/res/values-ast/strings.xml +++ b/app/src/main/res/values-ast/strings.xml @@ -584,7 +584,6 @@ Nome de ficheru Triba de ficheru Toles descargues tán posaes - Encaboxar xuba Desaniciar Nun hai xubes disponibles L\'usuariu anuló la xuba diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index 7d953b5f1232..525ad816554a 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -767,7 +767,6 @@ Theme Interval Manage internal folders for two way sync - Enable two way sync Two way sync Dark Light @@ -1071,7 +1070,6 @@ Upload files All uploads are paused Upload item action button - Cancel upload Delete No uploads available Upload some content or activate auto upload. diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index 60730b04cb53..e023afebbafe 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -816,7 +816,6 @@ Въведете име на файл и тип на файл за качване Качи файлове Бутон за действие, за качване на елемент - Откажи качването Изтриване Няма файлове Качете съдържание или активирайте автоматичното качване. diff --git a/app/src/main/res/values-ca/strings.xml b/app/src/main/res/values-ca/strings.xml index 130cb4106388..e90d223ee438 100644 --- a/app/src/main/res/values-ca/strings.xml +++ b/app/src/main/res/values-ca/strings.xml @@ -683,7 +683,6 @@ Tema Interval Gestioneu les carpetes internes per a la sincronització bidireccional - Habilita la sincronització bidireccional Fosc Clar Seguir al sistema diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 3bb4b8a76ffb..296bf562de15 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -767,7 +767,6 @@ Motiv vzhledu Interval Spravovat interní složky pro dvoucestnou synchronizaci - Zapnout obousměrnou synchronizaci Obousměrná synchronizace Tmavý Světlý @@ -1071,7 +1070,6 @@ Nahrát soubory Veškerá nahrávání jsou pozastavena Tlačítko akce Nahrát položku - Zrušit nahrávání Smazat Nic k nahrání Nahrajte nějaký obsah nebo zapněte automatické nahrávání. diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index 9e50d67eebb4..ff6190b60700 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -731,7 +731,6 @@ Tema Interval Styr interne mapper for to-vejs synkronisering - Aktivér to-vejs synkronisering Mørk Lys Følg system @@ -1025,7 +1024,6 @@ Send filer Alle uploads er pauserede Send artikel knap - Annullér upload Slet Ingen uploads tilgængelige Upload noget indhold eller aktivér auto upload. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 6552cfeab12d..ce810888ba31 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -767,7 +767,6 @@ Design Intervall Interne Ordner für 2-Wege-Synchronisierung verwalten - 2-Wege-Synchronisierung aktivieren 2-Wege-Synchronisierung Dunkel Hell @@ -1071,7 +1070,6 @@ Dateien hochladen Alle Uploads wurden pausiert Button, um Objekt hochzuladen - Hochladen abbrechen Löschen Keine Uploads verfügbar Laden Sie Inhalte hoch oder aktivieren Sie den Auto Upload. diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml index 7bed658c0196..3e6be3b43641 100644 --- a/app/src/main/res/values-el/strings.xml +++ b/app/src/main/res/values-el/strings.xml @@ -824,7 +824,6 @@ Εισαγωγή ονόματος και τύπου αρχείου μεταφόρτωσης Μεταφόρτωση αρχείων Κουμπί μεταφόρτωσης αντικειμένου - Ακύρωση μεταφόρτωσης Διαγραφή Μη διαθέσιμες μεταφορτώσεις Μεταφορτώστε περιεχόμενο ή ενεργοποιήστε την αυτόματη μεταφόρτωση. diff --git a/app/src/main/res/values-eo/strings.xml b/app/src/main/res/values-eo/strings.xml index fed7ff75ea89..f609f31b1a02 100644 --- a/app/src/main/res/values-eo/strings.xml +++ b/app/src/main/res/values-eo/strings.xml @@ -572,7 +572,6 @@ Entajpu la alŝutotan dosiernomon kaj ĝian tipon Alŝuti dosierojn Butono pri alŝuto - Nuligi alŝuton Forigi Neniu alŝuto disponeblas Alŝutu ion aŭ aktivigu aŭtomatan alŝuton. diff --git a/app/src/main/res/values-es-rAR/strings.xml b/app/src/main/res/values-es-rAR/strings.xml index aa373ac17397..2c4838a1f968 100644 --- a/app/src/main/res/values-es-rAR/strings.xml +++ b/app/src/main/res/values-es-rAR/strings.xml @@ -873,7 +873,6 @@ Subiendo archivos Todas las cargas están pausadas Subir botón de acción del elemento - Cancelar carga Eliminar No hay cargas disponibles Cargue algún contenido o active la carga automática diff --git a/app/src/main/res/values-es-rCO/strings.xml b/app/src/main/res/values-es-rCO/strings.xml index 9dea1687926f..0afcfaa7f12b 100644 --- a/app/src/main/res/values-es-rCO/strings.xml +++ b/app/src/main/res/values-es-rCO/strings.xml @@ -658,7 +658,6 @@ Ingresa el nombre y el tipo del archivo a cargar Cargar archivos Botón de cargar elemento - Cancelar carga Borrar No hay cargas disponibles Carga algún contenido o activa la carga automática diff --git a/app/src/main/res/values-es-rEC/strings.xml b/app/src/main/res/values-es-rEC/strings.xml index 80e8902fc444..205f4edff568 100644 --- a/app/src/main/res/values-es-rEC/strings.xml +++ b/app/src/main/res/values-es-rEC/strings.xml @@ -810,7 +810,6 @@ Ingresa el nombre y el tipo del archivo a cargar Cargar archivos Botón de cargar elemento - Cancelar carga Borrar No hay cargas disponibles Carga algún contenido o activa la carga automática diff --git a/app/src/main/res/values-es-rMX/strings.xml b/app/src/main/res/values-es-rMX/strings.xml index 82ce268c8ed1..027ec545980c 100644 --- a/app/src/main/res/values-es-rMX/strings.xml +++ b/app/src/main/res/values-es-rMX/strings.xml @@ -889,7 +889,6 @@ Cargar archivos Todas las cargas están pausadas Botón de cargar elemento - Cancelar carga Eliminar No hay cargas disponibles Carga algún contenido o activa la carga automática diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 91036514847e..a8f4fda604ce 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -729,7 +729,6 @@ Tema Intervalo Administrar carpetas internas para sincronización bidireccional - Activar sincronización bidireccional Oscuro Claro Seguir el del sistema @@ -1024,7 +1023,6 @@ Subir archivos Todas las cargas están pausadas Botón de acción de subida de objeto - Cancelar la subida Eliminar No hay subidas disponibles Sube algún contenido o active la subida automática. diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 48618ed0cb59..92e17b38c408 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -767,7 +767,6 @@ Teema Välp Halda sisemisi kaustu kahepoolse sünkroonimise jaoks - Luba kahepoolne sünkroonimine Kahepoolne sünkroonimine Hele Tume @@ -1071,7 +1070,6 @@ Laadi failid üles Kõik üleslaadimised on peatatud Tegevusnupp üleslaadimisel - Tühista üleslaadimine Kustuta Üleslaadimisi pole saadaval Laadi üles mõned failid või lülita sisse automaatne üleslaadimine. diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml index c900e5d952fc..55ea73e60929 100644 --- a/app/src/main/res/values-eu/strings.xml +++ b/app/src/main/res/values-eu/strings.xml @@ -700,7 +700,6 @@ Gaia Tarteak Kudeatu barruko karpetak bi norabideko sinkronizaziorako - Gaitu bi norabideko sinkronizazioa Iluna Argia Sistemaren berdina @@ -986,7 +985,6 @@ Igo fitxategiak Igoera guztiak pausatuta daude Elementua igotzeko ekintza botoia - Ezeztatu kargatzea Ezabatu Ezin dira fitxategiak igo Edukiak igo edo auto-igotzea aktiba ezazu. diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml index 1ae77810a6fb..c9d96d49c8d1 100644 --- a/app/src/main/res/values-fa/strings.xml +++ b/app/src/main/res/values-fa/strings.xml @@ -869,7 +869,6 @@ بارگذاری فایل ها همه آپلودها به طور موقت متوقف شدند دکمه فعال‌سازی موارد بارگذاری - متوقف کردن بار گذاری حذف هیچ بارگذاری وجود ندارد تعدادی عکس آپلود کنید یا آپلود خودکار را فعال کنید diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 73e725beb8be..804baf6398c0 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -941,7 +941,6 @@ GNU yleinen lisenssi, versio 2 Lähetä tiedostoja Kaikki lähetykset on tauotettu Lataa tiedosto nappi - Peruuta lähetys Poista Ei lähetettäviä saatavilla Lähetä sisältöä tai aktivoi automaattinen lähetys. diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index a21e2d1e9dc0..674a7f21dc1a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -755,7 +755,6 @@ Thème Intervalle Gestion les dossiers internes pour une synchronisation bidirectionnelle - Activer la synchronisation bidirectionnelle Synchronisation bidirectionnelle Sombre Clair @@ -1057,7 +1056,6 @@ Téléverser des fichiers Tous les téléversements sont suspendus Bouton d\'action d\'upload - Annuler l\'envoi Supprimer Aucun téléversement disponible Téléversez du contenu ou activez le téléversement automatique. diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index c188fd153d83..ae9dcbe5e826 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -767,7 +767,6 @@ Téama Eatramh Bainistigh fillteáin inmheánacha le haghaidh sioncronaithe dhá bhealach - Cumasaigh sioncronú dhá bhealach Sioncrónú dhá bhealach Dorcha Solas @@ -1071,7 +1070,6 @@ Uaslódáil comhaid Tá gach uaslódáil curtha ar sos Íoslódáil an cnaipe gníomh - Cealaigh an uaslódáil Scrios Níl aon uaslódála ar fáil Uaslódáil roinnt ábhar nó cuir an uaslódáil uathoibríoch i ngníomh. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index d23c82ec7ddb..782c8030e4b9 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -768,7 +768,6 @@ Tema Intervalo Xestionar os cartafoles internos para a sincronización bidireccional - Activar a sincronización bidireccional Sincronización bidireccional Escuro Claro @@ -1072,7 +1071,6 @@ Enviar ficheiros Todos os envíos están en pausa Botón da acción do envío de elemento - Cancelar o envío Eliminar Non hai envíos dispoñíbeis Envíe algún contido ou active o envío automático. diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index d5774bea2fa8..a0d43a294c35 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -767,6 +767,7 @@ Otpremi sadržaj iz drugih aplikacija Fotografija Otpremanje iz kamere + Videozapis Naziv datoteke Vrsta datoteke Datoteka prečaca za Google Maps (%s) @@ -775,7 +776,6 @@ Unesite naziv datoteke i vrstu datoteke za otpremanje Otpremi datoteke Gumb za otpremanje stavke - Otkaži otpremanje Izbriši Nema dostupnih otprema Otpremite neki sadržaj ili aktivirajte automatsko otpremanje. diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index e1bc2a2c3a30..a6d79317fc3b 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -767,7 +767,6 @@ Téma Időköz Belső mappák kezelése kétirányú szinkronizáláshoz - Kétirányú szinkronizálás engedélyezése Kétirányú szinkronizálás Sötét Világos @@ -1089,7 +1088,6 @@ A Nextcloud itt érhető el: https://nextcloud.com Fájlok feltöltése Összes feltöltés szüneteltetve Elem feltöltése műveletgomb - Feltöltés megszakítása Törlés Nincsenek feltöltések Töltsön fel valamilyen tartalmat, vagy aktiválja az automatikus feltöltést. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index b3986f09d1c6..14d8dee2003b 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -736,7 +736,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Tema Interval Kelola folder internal untuk sinkronisasi dua arah - Nyalakan sinkronisasi dua arah Gelap Terang Ikuti sistem @@ -1034,7 +1033,6 @@ Berikut ini adalah daftar berkas lokal, dan berkas jarak jauh di %5$s yang terhu Unggah berkas Semua unggahan dihentikan sebentar Tombol aksi unggah item - Batal unggah Hapus Tidak ada unggahan yang tersedia Unggah beberapa foto atau aktifkan unggah otomatis. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index d792f75412e4..c1374610ba5a 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -722,7 +722,6 @@ Þema Millibil Sýsla með möppur vegna tvíátta samstillingar - Virkja tvíátta samstillingu Dökkt Ljóst Fylgja kerfinu @@ -1015,7 +1014,6 @@ Hlaða inn skrám Allar innsendingar eru í bið Aðgerðarhnappur til að senda inn atriði - Hætta við innsendingu Eyða Engar innsendingar tiltækar Sendu inn einhverjar myndir eða virkjaðu sjálfvirka innsendingu. diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index befe0b60f620..d1c5b7247713 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -711,7 +711,6 @@ Tema Intervallo Gestisci le cartelle interne per la sincronizzazione bidirezionale - Abilita sincronizzazione bidirezionale Sincronizzazione bidirezionale Scuro Chiaro @@ -1005,7 +1004,6 @@ Carica file Tutti i caricamenti sono in pausa Pulsante azione Carica elemento - Annulla caricamento Elimina Nessun caricamento disponibile Carica dei contenuti o attiva il caricamento automatico. diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 15f33eb22c89..21c924ece773 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -695,7 +695,6 @@ נא לציין שם קובץ וסוג קובץ לשליחה העלאת קבצים כפתור פעולת העלאת פריט - ביטול העלאה מחיקה אין העלאות זמינות יש להעלות תוכן כלשהו או להפעיל העלאה אוטומטית. diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 0000b27928ba..103470958d97 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -737,7 +737,6 @@ テーマ 間隔 内部フォルダを管理して双方向同期を行う - 双方向同期を有効にする 暗い 明るい システムの設定に従う @@ -1029,7 +1028,6 @@ アップロードファイル すべてのアップロードが一時停止しています アイテムアクションのアップロードボタン - アップロードをキャンセル 削除 アップロードは利用できません 何かコンテンツをアップロードするか、自動アップロードを有効にしてください。 diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 0622603118ed..80383c68b5e7 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -689,7 +689,6 @@ 테마 간격 내부 폴더 양방향 동기화 관리 - 양방향 동기화 활성화 어둡게 밝게 시스템을 따르기 @@ -965,7 +964,6 @@ 파일 업로드 모든 업로드가 일시 정지됨 항목 업로드 동작 단추 - 업로드 취소 삭제 업로드 없음 콘텐츠를 업로드하거나 자동 업로드를 활성화하십시오. diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 14070c12dc94..b2e8ed90701a 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -744,7 +744,6 @@ ຫົວຂໍ້ Interval Manage internal folders for two way sync - Enable two way sync ມືດ ແຈ້ງ ຕາມລະບົບ @@ -1039,7 +1038,6 @@ ອັບໂຫຼດຟາຍ All uploads are paused ອັບໂຫຼດປຸ່ມ ໃນລາຍການການອັບໂຫຼດ - Cancel upload ລຶບ ບໍ່ມີການອັບໂຫຼດ ອັບໂຫລດເນື້ອຫາບາງຢ່າງ ຫຼື ເປີດການອັບໂຫລດອັດຕະໂນມັດ. diff --git a/app/src/main/res/values-lt-rLT/strings.xml b/app/src/main/res/values-lt-rLT/strings.xml index 9961f8449b2b..0a42dc8c7bd3 100644 --- a/app/src/main/res/values-lt-rLT/strings.xml +++ b/app/src/main/res/values-lt-rLT/strings.xml @@ -590,7 +590,6 @@ Apipavidalinimas Intervalas. Administruoti vidines dvipusio sinchronizavimo direktorijas - Įjungti dvipusį sinchronizavimą. Tamsus Šviesus Pagal sistemą @@ -837,7 +836,6 @@ Įrašykite failo pavadinimą ir tipą įkėlimui Įkelti failus Įkelti elemento veiksmo mygtuką - Atšaukti įkėlimą Ištrinti Atnaujinimų nerasta Įkelkite failus arba sinchronizuokite su savo įrenginiais diff --git a/app/src/main/res/values-lv/strings.xml b/app/src/main/res/values-lv/strings.xml index 27099f22a6fd..e267d806a069 100644 --- a/app/src/main/res/values-lv/strings.xml +++ b/app/src/main/res/values-lv/strings.xml @@ -457,7 +457,6 @@ Attālā mape Izskats Pārvaldīt iekšējās mapes divvirzienu sinhronizēšanai - Iespējot divvirzienu sinhronizēšanu Tumšs Gaišs Izmantot sistēmas @@ -605,7 +604,6 @@ Interneta saīsnes datne(%s) Maza teksta datne(.txt) Augšupielādēt datnes - Atcelt augšupielādi Izdzēst Augšupielādes nav pieejamas. Augšupielādē kaut ko vai ieslēdz automātisko augšupielādi! diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 72c2f87a6664..225fb9cabc7f 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -715,7 +715,6 @@ Внесете име на датотеката и видот на датотеката за да ја прикачите Прикачи датотеки Копче за прикачување - Откажи прикачување Избриши Нема прикачувања Прикачете некоја содржина или активирајте автоматско прикачување. diff --git a/app/src/main/res/values-nb-rNO/strings.xml b/app/src/main/res/values-nb-rNO/strings.xml index 582fd9b7f94f..d60398598ae7 100644 --- a/app/src/main/res/values-nb-rNO/strings.xml +++ b/app/src/main/res/values-nb-rNO/strings.xml @@ -661,7 +661,6 @@ Tema Intervall Administrer interne mapper for toveis synkronisering - Slå på toveis-synkronisering Mørk Lys Følg system @@ -931,7 +930,6 @@ Last opp filer Alle opplastinger er satt på pause Last opp element handlingsknapp - Avbryt opplasting Slett Ingen opplastinger tilgjengelig Last opp noe innhold eller aktiver automatisk opplasting! diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index 1b385a22a5a9..a1cbf9c9eba3 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -730,7 +730,6 @@ Thema Interval Interne mappen beheren voor tweeweg-synchronisatie - Tweeweg-synchronisatie inschakelen Tweeweg-synchronisatie Donker Licht @@ -1027,7 +1026,6 @@ Bestanden uploaden Alle uploads zijn gepauzeerd Upload object actie knop - Upload afbreken Verwijderen Geen uploads beschikbaar Upload enkele gegevens of activeer auto-upload. diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 3dc585cb6fed..d9d6331b324d 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -767,7 +767,6 @@ Motyw Interwał Zarządzaj wewnętrznymi katalogami w celu synchronizacji dwukierunkowej - Włącz synchronizację dwukierunkową Synchronizacja dwukierunkowa Ciemny Jasny @@ -1071,7 +1070,6 @@ Wyślij pliki Wszystkie przesyłania zostały wstrzymane Przycisk wysyłania elementu - Anuluj wysyłanie Usuń Brak wysłanych plików Wyślij pliki lub włącz automatyczne wysyłanie. diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 2e6a50258c29..c44aff16840d 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -767,7 +767,6 @@ Tema Intervalo Gerenciar pastas internas para sincronização bidirecional - Ativar sincronização bidirecional Sincronização bidirecional Escuro Claro @@ -1071,7 +1070,6 @@ Enviar arquivos Todos os uploads estão pausados Enviar botão de ação do item - Cancelar upload Excluir Nenhum upload disponível Faça upload de algum conteúdo ou ative o upload automático. diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index 1f0e686c12f4..0224d70fff33 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -871,7 +871,6 @@ Aproveite o novo e melhorado envio automático. Introduza o nome e tipo de ficheiro a enviar Carregar ficheiros Botão de acção de envio de item - Cancelar envio Eliminar Sem envios disponíveis Envie algum conteúdo ou ative o envio automático. diff --git a/app/src/main/res/values-ro/strings.xml b/app/src/main/res/values-ro/strings.xml index 9afbfe70d0e6..56d448e1442c 100644 --- a/app/src/main/res/values-ro/strings.xml +++ b/app/src/main/res/values-ro/strings.xml @@ -870,7 +870,6 @@ Introdu numele și tipul fișierului de încărcat Încarcă fișiere Butonul de încărcare - Anulează încărcarea Șterge Nu există încărcări disponibile Încărcați conținut sau activați încărcarea automată. diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index bec50872ce02..bd4331a1d3e7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -754,7 +754,6 @@ Оформление Интервал Управление внутренними папками для двусторонней синхронизации - Включить двустороннюю синхронизацию Двусторонняя синхронизация Тёмное Светлое @@ -1054,7 +1053,6 @@ Отправить файлы Все загрузки приостановлены Кнопка «выгрузить объект» - Отменить передачу Удалить Нет файлов, ожидающих передачу на сервер. Сохраните здесь что-нибудь или включите автоматическую передачу файлов! diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 2d6756478a23..76010039ab47 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -758,7 +758,6 @@ Motív vzhľadu Interval Spravovať interné adresáre pre obojsmernú synchronizáciu - Povoliť obojstrannú synchronizáciu Obojsmerná synchronizácia Tmavý Svetlý @@ -1062,7 +1061,6 @@ Nahrať súbory Všetky nahrávania sú pozastavené. Tlačítko akcie Nahrať položku - Zrušiť nahrávanie Zmazať Žiadne súbory na nahratie nie sú dostupné Nahrajte nejaký obsah alebo si zapnite automatické nahrávanie. diff --git a/app/src/main/res/values-sl/strings.xml b/app/src/main/res/values-sl/strings.xml index e8a7be916e66..56d3fb49dcfb 100644 --- a/app/src/main/res/values-sl/strings.xml +++ b/app/src/main/res/values-sl/strings.xml @@ -836,7 +836,6 @@ Ime in zvrst datoteke za pošiljanje Pošlji datoteke Gumb za pošiljanje - Prekliči pošiljanje Izbriši Ni pripravljenih datotek za pošiljanje Pošljite kakšno vsebino, ali pa omogočite samodejno nalaganje. diff --git a/app/src/main/res/values-sq/strings.xml b/app/src/main/res/values-sq/strings.xml index 05755bfaca80..7c334e28e944 100644 --- a/app/src/main/res/values-sq/strings.xml +++ b/app/src/main/res/values-sq/strings.xml @@ -629,7 +629,6 @@ Vendos emrin dhe tipin e dokumentit të aploduar Ngarko skedarët Butoni i veprimit të ngarkimit të një artikulli - Anullo ngarkimin Delete Nuk ka asnjë ngarkim në gjëndje Ngarko disa përmbajtje ose aktivizo ngarkimin direkt. diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index ff8777330dfc..8c83c6d84205 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -739,7 +739,6 @@ Тема Интервал Управљање интерним фолдерима за двосмерну синхронизацију - Укључи двосмерну синхронизацију тамна светла системска @@ -1034,7 +1033,6 @@ Отпреми фајлове Сва отпремања су паузирана Дугме за отпремање - Откажи отпремање Избриши Нема доступних отпремања Отпремите нешто или укључите аутоматско отпремање. diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 375cc2005b4c..ebc337f69b8a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -755,7 +755,6 @@ Tema Intervall Hantera interna mappar för tvåvägssynk - Aktivera tvåvägssynk Mörkt Ljust Följ systemet @@ -1054,7 +1053,6 @@ Ladda upp filer Alla uppladdningar är pausade Ladda upp objekthändelse-knapp - Avbryt uppladdning Ta bort Inga uppladdningar tillgängliga Ladda upp något innehåll eller aktivera automatisk uppladdning. diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 6c07b38b760e..f8b8c5688b66 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -767,7 +767,6 @@ Mandhari Muda Dhibiti folda za ndani kwa usawazishaji wa njia mbili - Washa usawazishaji wa njia mbili Usawazishaji wa njia mbili Giza Mwanga @@ -1071,7 +1070,6 @@ Pakia faili Upakiaji wote umesitishwa Kitufe cha kitendo cha kupakia - Ghairi upakiaji Futa Hakuna vipakiwa vinavyopatikana Pakia baadhi ya maudhui au uwashe upakiaji kiotomatiki. diff --git a/app/src/main/res/values-th-rTH/strings.xml b/app/src/main/res/values-th-rTH/strings.xml index 33e25a2a9c56..b0a165573e53 100644 --- a/app/src/main/res/values-th-rTH/strings.xml +++ b/app/src/main/res/values-th-rTH/strings.xml @@ -580,7 +580,6 @@ ชื่อไฟล์ ประเภทไฟล์ อัพโหลดไฟล์จากเครื่อง - ยกเลิกการอัพโหลด ลบ แสกนเอกสารโดยใช้กล้อง เกิดข้อขัดแย้งของการซิงค์ โปรดแก้ไขด้วยตนเอง diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index 96fc0829f413..b3e7999fab4b 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -759,7 +759,6 @@ Tema Aralık İç klasörleri iki yönlü eşitleme ile yönetin - İki yönlü eşitlemeyi aç İki yönlü eşitleme Koyu Açık @@ -1063,7 +1062,6 @@ Dosyaları yükle Tüm yüklemeler duraklatıldı Öge yükleme işlemi düğmesi - Yüklemeyi iptal et Sil Herhangi bir yükleme yok Bazı içerikler yükleyin ya da otomatik yükleme özelliğini açın. diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 02740fa01a22..7d3983af2c2f 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -756,7 +756,6 @@ باشتېما ئىنتىرۋال ئىككى خىل ماسقەدەملەش ئۈچۈن ئىچكى قىسقۇچلارنى باشقۇرۇڭ - ئىككى يۆنىلىشلىك ماس-قەدەملەشنى قوزغات قاراڭغۇ نۇر سىستېمىغا ئەگىشىڭ @@ -1056,7 +1055,6 @@ ھۆججەتلەرنى يۈكلەڭ بارلىق يۈكلەش توختىتىلدى تۈر ھەرىكەت كۇنۇپكىسىنى يۈكلەڭ - يۈكلەشتىن ۋاز كەچ ئۆچۈرۈش يوللاشقا بولمايدۇ بەزى مەزمۇنلارنى يۈكلەڭ ياكى ئاپتوماتىك يوللاشنى قوزغىتىڭ. diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index b20fe4cb321b..1e6d526165d5 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -104,7 +104,7 @@ Вузол не знайдено %1$s не підтримує одночасно декілька облікових записів Не вдалося встановити з\'єднання - Скасувати авторизацію + Скасувати вхід Зазначте дійсну адресу сервера Не вдалося отримати дані для входу. Спробуйте ще раз. Помилка під час обробки запиту на вхід. Спробуйте пізніше. @@ -767,7 +767,6 @@ Тема Інтервал Керувати внутрішніми каталогами для двосторонньої синхронізації - Увімкнути двосторонню синхронізацію Двостороння синхронізація Темна Світла @@ -1071,7 +1070,6 @@ Завантажити файл Всі завантаження призупинено Кнопка завантаження - Скасувати завантаження Вилучити Ви ще нічого не завантажили Завантажте вміст або увімкніть автоматичне завантаження diff --git a/app/src/main/res/values-vi/strings.xml b/app/src/main/res/values-vi/strings.xml index b7ad0fef2bc2..f369e9ee82b0 100644 --- a/app/src/main/res/values-vi/strings.xml +++ b/app/src/main/res/values-vi/strings.xml @@ -767,7 +767,6 @@ Nhập tên tệp và loại tệp để tải lên Tải lên tập tin Nút hành động tải mục lên - Hủy tải lên Xóa Không có tải lên nào có sẵn Tải lên một vài nội dung hoặc kích hoạt tải lên tự động diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index b5e185a44656..7298203014dd 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -767,7 +767,6 @@ 主题 间隔 管理内部文件夹以实现双向同步 - 启用双向同步 双向同步 深色 浅色 @@ -1074,7 +1073,6 @@ 上传文件 所有上传已暂停 上传项操作按钮 - 取消上传 删除 没有可以上传的 上传一些内容或启用自动上传。 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 71f1f49283dc..f9e145297777 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -767,7 +767,6 @@ 佈景主題 間距 管理內部資料夾以進行雙向同步 - 啟用雙向同步 雙向同步 深色 淺色 @@ -1071,7 +1070,6 @@ 上傳檔案 所有上傳皆已暫停 上傳操作按鈕 - 取消上戴 刪除 沒有上傳的檔案 上傳一些內容或者啟動自動上傳。 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 7e3e6f6b0c8c..221be7a19a27 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -767,7 +767,6 @@ 佈景主題 間隔 管理內部資料夾以進行雙向同步 - 啟用雙向同步 雙向同步 深色 淺色 @@ -1071,7 +1070,6 @@ 上傳檔案 所有上傳皆已暫停 上傳項目動作按鈕 - 取消上傳 刪除 沒有上傳的檔案 上傳一些內容或者啟用自動上傳。 From ef106a8fde367f2312569ced137861c2785565a8 Mon Sep 17 00:00:00 2001 From: Yaroslav98214 Date: Wed, 4 Feb 2026 22:22:18 +0000 Subject: [PATCH 05/83] fix: enforce E2EE metadata validation Fail fast when metadata verification detects counter, signature, or checksum issues. Signed-off-by: Yaroslav98214 --- .../com/owncloud/android/utils/EncryptionUtilsV2.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt index 6f8fdc011f09..3f1dc86424ef 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt @@ -250,7 +250,9 @@ class EncryptionUtilsV2 { ) } - verifyMetadata(metadataFile, decryptedFolderMetadataFile, oldCounter, signature) + if (!verifyMetadata(metadataFile, decryptedFolderMetadataFile, oldCounter, signature)) { + throw IllegalStateException("Metadata is corrupt!") + } val transferredFiledrop = filesDropCountBefore > 0 && decryptedFolderMetadataFile.metadata.files.size == filesBefore + filesDropCountBefore @@ -953,10 +955,10 @@ class EncryptionUtilsV2 { decryptedFolderMetadataFile: DecryptedFolderMetadataFile, oldCounter: Long, signature: String - ) { + ): Boolean { if (decryptedFolderMetadataFile.metadata.counter < oldCounter) { MainApp.showMessage(R.string.e2e_counter_too_old) - return + return false } val message = EncryptionUtils.serializeJSON(encryptedFolderMetadataFile, true) @@ -965,14 +967,15 @@ class EncryptionUtilsV2 { if (certs.isNotEmpty() && !verifySignedData(signedData, certs)) { MainApp.showMessage(R.string.e2e_signature_does_not_match) - return + return false } val hashedMetadataKey = hashMetadataKey(decryptedFolderMetadataFile.metadata.metadataKey) if (!decryptedFolderMetadataFile.metadata.keyChecksums.contains(hashedMetadataKey)) { MainApp.showMessage(R.string.e2e_hash_not_found) - return + return false } + return true } private fun getSignedData(base64encodedSignature: String, message: String): CMSSignedData { From dd6b001aa1881a9a066f0cb55bbfab4c382e2bde Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Fri, 6 Feb 2026 03:29:16 +0000 Subject: [PATCH 06/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-b+en+001/strings.xml | 8 ++++++++ app/src/main/res/values-cs-rCZ/strings.xml | 3 +++ app/src/main/res/values-de/strings.xml | 8 ++++++++ app/src/main/res/values-et-rEE/strings.xml | 7 +++++++ app/src/main/res/values-gl/strings.xml | 8 ++++++++ app/src/main/res/values-hr/strings.xml | 6 ++++++ app/src/main/res/values-pt-rBR/strings.xml | 8 ++++++++ 7 files changed, 48 insertions(+) diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index 525ad816554a..3abf7744a1bf 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -53,6 +53,7 @@ Delete task Try sending a message to spark a conversation. Hello there! What can I help you with today? + Please select task Send message Open conversation list An error occurred while creating the task @@ -1015,6 +1016,12 @@ Thumbnail for new file Loading is taking longer than expected Today + Enter text to translate… + Translate from: + Translate to: + Press the button to translate + Translation is taking longer than expected. + Translating… Deleted files No deleted files You will be able to recover deleted files from here. @@ -1083,6 +1090,7 @@ App permissions Your files cannot be uploaded without access to local storage. Tap to grant permission. Upload Stopped – Storage Permission Required + You can remove or resume it from Uploads %1$d / %2$d - %3$s Encryption is only possible with >= Android 5.0 Insufficient space prevents copying the selected files into the %1$s folder. Would you like to move them there instead? diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 296bf562de15..d770f9cc1705 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -1015,6 +1015,9 @@ Náhled pro nový soubor Načítání trvá déle než očekáváno Dnes + Přeložit z: + Přeložit do: + Překládá se… Smazané soubory Žádné smazané soubory Odsud je možné obnovit smazané soubory. diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index ce810888ba31..221c93e909ce 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -53,6 +53,7 @@ Aufgabe löschen Versuchen Sie, eine Nachricht zu senden, um eine Unterhaltung anzustoßen. Hallo! Womit kann ich Ihnen heute helfen? + Bitte eine Aufgabe auswählen Nachricht senden Liste der Unterhaltungen öffnen Es ist ein Fehler beim Erstellen der Aufgabe aufgetreten @@ -1015,6 +1016,12 @@ Miniaturbild für neue Datei Laden dauert länger als erwartet Heute + Text zum Übersetzen eingeben … + Übersetzen von: + Übersetzen nach: + Zum Übersetzen den Knopf drücken + Die Übersetzung dauert länger als erwartet. + Übersetze … Gelöschte Dateien Keine gelöschten Dateien Hier können Sie gelöschte Dateien wiederherstellen. @@ -1083,6 +1090,7 @@ App-Berechtigungen Ohne Zugriff auf den lokalen Speicher können Ihre Dateien nicht hochgeladen werden. Tippen Sie, um die Berechtigung zu erteilen. Hochladen gestoppt – Speicherberechtigung erforderlich + Sie können es entfernen oder aus den Uploads fortsetzen %1$d / %2$d - %3$s Verschlüsselung ist nur möglich mit >= Android 5.0 Es steht nicht genügend Speicherplatz zur Verfügung, um die ausgewählten Dateien in das Verzeichnis %1$s zu kopieren. Sollen diese stattdessen verschoben werden? diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 92e17b38c408..5fce75aa7c0a 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -53,6 +53,7 @@ Kustuta ülesanne Vestluse algatamiseks proovi sata üks sõnum. Hei! Kuidas saan sind täna aidata? + Palun vali ülesanne Saada sõnum Ava vestluste loend Ülesande loomisel tekkis viga @@ -1015,6 +1016,12 @@ Uue faili pisipilt Laadimiseks kulub rohkem aega, kui peaks Täna + Sisesta tõlgitav tekst… + Lähtekeel tõlkimisel: + Tõlke sihtkeel: + Tõlkimiseks klõpsa nuppu + Tõlkimiseks kulub rohkem aega, kui peaks… + Tõlgin… Kustutatud failid Kustutatud faile pole Siit saad kustutatud faile taastada. diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 782c8030e4b9..63bd70ff0ebc 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -53,6 +53,7 @@ Eliminar tarefa Probe a enviar unha mensaxe para iniciar unha conversa. Ola! En que podo axudarlle hoxe? + Seleccione a tarefa Enviar a mensaxe Lista de conversas abertas Produciuse un erro ao crear a tarefa @@ -1016,6 +1017,12 @@ Miniatura do novo ficheiro A carga está a levar máis tempo do agardado Hoxe + Introduza o texto a traducir + Traducir desde: + Traducir a: + Prema no botón para traducir + A tradución está a levar máis tempo do agardado. + Traducindo… Ficheiros eliminados Non hai ficheiros eliminados Poderá recuperar ficheiros eliminados de aquí. @@ -1084,6 +1091,7 @@ Permisos da aplicación Non é posíbel enviar os ficheiros cargar sen acceso ao almacenamento local. Toque para conceder permiso. Envío detido — É necesario permiso de almacenamento + Pode retiralo ou retomalo desde Envíos %1$d / %2$d - %3$s A cifraxe só é posíbel con >= Android 5.0 Non hai espazo abondo para copiar os ficheiros seleccionados no cartafol %1$s. En troques, gustaríalle movelos? diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index a0d43a294c35..12ba0b0079aa 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -34,6 +34,7 @@ Traži u %s Prikaži se kao izvan mreže Izbriši zadatak + Asistent radi nepoznato Pripadajući račun nije pronađen! @@ -81,6 +82,7 @@ Onemogući Možda je omogućena optimizacija rada baterije na uređaju. Mogućnost automatskog otpremanja AutoUpload djeluje samo ako izuzmete ovu aplikaciju iz optimizacije baterije. Optimizacija baterije + Asistent Favoriti Sve datoteke Medij @@ -144,6 +146,7 @@ Pronašli ste nedostatak? Pogrešku u radu? Pomozite nam testiranjem Prijavite problem na GitHubu + Nextcloud Asistent Konfiguriraj Želite li zaista izbrisati %1$s? Želite li zaista izbrisati odabrane stavke? @@ -180,6 +183,7 @@ Nova mapa Nova prezentacija Nova proračunska tablica + Dodaj opis mape Vjerodajnice onemogućene Svakodnevno sigurnosno kopiranje Podaci za sigurnosno kopiranje @@ -219,6 +223,7 @@ Pozadinska slika zaglavlja ladice Aktivnosti Sve datoteke + Asistent Favoriti Medij Početna @@ -234,6 +239,7 @@ Iskorišteno %1$s od %2$s Upotrijebljeno %1$s Automatsko otpremanje + Asistent Više Postavi kao šifrirano Postavi šifriranje diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index c44aff16840d..3833e61b1e4b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -53,6 +53,7 @@ Excluir tarefa Tente enviar uma mensagem para iniciar uma conversa. Oi! Em que posso ajudá-lo hoje? + Selecione uma tarefa Enviar mensagem Abrir lista de conversas Ocorreu um erro ao criar a tarefa @@ -1015,6 +1016,12 @@ Miniatura para arquivo novo O carregamento está demorando mais do que o esperado Hoje + Insira o texto a ser traduzido… + Traduzir do: + Traduzir para: + Pressione o botão para traduzir + A tradução está demorando mais do que o esperado. + Traduzindo… Arquivos excluídos Nenhum arquivo excluído Você poderá recuperar arquivos excluídos aqui. @@ -1083,6 +1090,7 @@ Permissões do aplicativo Seus arquivos não podem ser enviados sem acesso ao armazenamento local. Toque para conceder permissão. Upload Interrompido – Permissão de Armazenamento Necessária + Você pode removê-lo ou retomá-lo em Uploads. %1$d / %2$d - %3$s A criptografia só é possível com Android >= 5.0 O espaço insuficiente está impedindo a cópia dos arquivos selecionados para a pasta %1$s. Em vez de copiá-los, gostaria de movê-los para lá? From b4ad4ec8962181e4d5c3ce6b09b69f9d5067eba9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 5 Feb 2026 16:33:31 +0100 Subject: [PATCH 07/83] feat(settings): m3 theme selection dialog Signed-off-by: alperozturk96 --- app/src/main/AndroidManifest.xml | 2 +- .../nextcloud/client/di/ComponentsModule.java | 4 + .../activity/ChooseStorageLocationActivity.kt | 38 ----- .../ui/activity/ExtendedSettingsActivity.kt | 111 +++++++++++++++ .../android/ui/activity/SettingsActivity.java | 72 ++++++---- .../android/ui/dialog/ThemeSelectionDialog.kt | 133 ++++++++++++++++++ .../model/ExtendedSettingsActivityDialog.kt | 16 +++ app/src/main/res/xml/preferences.xml | 2 +- 8 files changed, 309 insertions(+), 69 deletions(-) delete mode 100644 app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt create mode 100644 app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee6e7fd2bc80..20c10607b707 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -358,7 +358,7 @@ android:exported="false" android:launchMode="singleInstance" /> diff --git a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java index dc7194f78b0a..954fb16f8c3b 100644 --- a/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/app/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -99,6 +99,7 @@ import com.owncloud.android.ui.dialog.SyncFileNotEnoughSpaceDialogFragment; import com.owncloud.android.ui.dialog.SyncedFolderPreferencesDialogFragment; import com.owncloud.android.ui.dialog.TermsOfServiceDialog; +import com.owncloud.android.ui.dialog.ThemeSelectionDialog; import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.fragment.ExtendedListFragment; import com.owncloud.android.ui.fragment.FeatureFragment; @@ -423,6 +424,9 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract ChooseStorageLocationDialogFragment chooseStorageLocationDialogFragment(); + @ContributesAndroidInjector + abstract ThemeSelectionDialog themeSelectionDialog(); + @ContributesAndroidInjector abstract SharePasswordDialogFragment sharePasswordDialogFragment(); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt deleted file mode 100644 index 8d13d53e7ddf..000000000000 --- a/app/src/main/java/com/owncloud/android/ui/activity/ChooseStorageLocationActivity.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2024 ZetaTom <70907959+ZetaTom@users.noreply.github.com> - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.owncloud.android.ui.activity - -import android.content.Intent -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import com.nextcloud.ui.ChooseStorageLocationDialogFragment - -class ChooseStorageLocationActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val chooseStorageLocationDialogFragment = ChooseStorageLocationDialogFragment.newInstance() - supportFragmentManager.setFragmentResultListener( - KEY_RESULT_STORAGE_LOCATION, - this - ) { _, result -> - setResult( - ChooseStorageLocationDialogFragment.STORAGE_LOCATION_RESULT_CODE, - Intent().putExtra( - KEY_RESULT_STORAGE_LOCATION, - result.getString(KEY_RESULT_STORAGE_LOCATION) - ) - ) - } - chooseStorageLocationDialogFragment.show(supportFragmentManager, "choose_storage_location") - } - - companion object { - const val KEY_RESULT_STORAGE_LOCATION = ChooseStorageLocationDialogFragment.KEY_RESULT_STORAGE_LOCATION - } -} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt new file mode 100644 index 000000000000..ca35dec46378 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt @@ -0,0 +1,111 @@ +package com.owncloud.android.ui.activity + +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.nextcloud.ui.ChooseStorageLocationDialogFragment +import com.owncloud.android.ui.dialog.ThemeSelectionDialog +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog + +class ExtendedSettingsActivity : AppCompatActivity() { + + private var dialogShown = false + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + if (savedInstanceState != null) { + dialogShown = savedInstanceState.getBoolean(KEY_DIALOG_SHOWN, false) + } + + if (dialogShown) { + return + } + + val dialogType = intent.getStringExtra(EXTRA_DIALOG_TYPE) ?: run { + finish() + return + } + + when (dialogType) { + ExtendedSettingsActivityDialog.StorageLocation.key -> { + setupStorageLocationDialog() + } + ExtendedSettingsActivityDialog.Theme.key -> { + setupThemeDialog() + } + else -> { + finish() + } + } + + dialogShown = true + } + + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) + outState.putBoolean(KEY_DIALOG_SHOWN, dialogShown) + } + + private fun setupStorageLocationDialog() { + if (supportFragmentManager.findFragmentByTag(TAG_STORAGE_DIALOG) != null) { + return + } + + val dialog = ChooseStorageLocationDialogFragment.newInstance() + + supportFragmentManager.setFragmentResultListener( + ExtendedSettingsActivityDialog.StorageLocation.key, + this + ) { _, result -> + setResult( + ChooseStorageLocationDialogFragment.STORAGE_LOCATION_RESULT_CODE, + Intent().putExtra( + ExtendedSettingsActivityDialog.StorageLocation.key, + result.getString(ExtendedSettingsActivityDialog.StorageLocation.key) + ) + ) + finish() + } + + dialog.show(supportFragmentManager, TAG_STORAGE_DIALOG) + } + + private fun setupThemeDialog() { + if (supportFragmentManager.findFragmentByTag(TAG_THEME_DIALOG) != null) { + return + } + + val dialog = ThemeSelectionDialog.newInstance() + + supportFragmentManager.setFragmentResultListener( + ThemeSelectionDialog.RESULT_KEY, + this + ) { _, result -> + setResult( + RESULT_OK, + Intent().putExtra( + ThemeSelectionDialog.RESULT_KEY, + result.getString(ThemeSelectionDialog.RESULT_KEY) + ) + ) + finish() + } + + dialog.show(supportFragmentManager, TAG_THEME_DIALOG) + } + + companion object { + private const val EXTRA_DIALOG_TYPE = "dialog_type" + private const val KEY_DIALOG_SHOWN = "dialog_shown" + private const val TAG_STORAGE_DIALOG = "choose_storage_location" + private const val TAG_THEME_DIALOG = "theme_selection" + + fun createIntent(context: Context, dialogType: ExtendedSettingsActivityDialog): Intent { + return Intent(context, ExtendedSettingsActivity::class.java).apply { + putExtra(EXTRA_DIALOG_TYPE, dialogType.key) + } + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index ba6db7333a81..02fad9543aa4 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -68,8 +68,10 @@ import com.owncloud.android.ui.ListPreferenceDialog; import com.owncloud.android.ui.ThemeableSwitchPreference; import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; +import com.owncloud.android.ui.dialog.ThemeSelectionDialog; import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog; import com.owncloud.android.utils.ClipboardUtil; import com.owncloud.android.utils.DeviceCredentialUtils; import com.owncloud.android.utils.DisplayUtils; @@ -80,7 +82,6 @@ import com.owncloud.android.utils.theme.ViewThemeUtils; import java.util.ArrayList; -import java.util.List; import java.util.Objects; import javax.inject.Inject; @@ -121,7 +122,6 @@ public class SettingsActivity extends PreferenceActivity private static final int ACTION_REQUEST_CODE_DAVDROID_SETUP = 10; private static final int ACTION_SHOW_MNEMONIC = 11; private static final int ACTION_E2E = 12; - private static final int ACTION_SET_STORAGE_LOCATION = 13; private static final int TRUE_VALUE = 1; private static final String DAV_PATH = "/remote.php/dav"; @@ -879,41 +879,50 @@ private void setupGeneralCategory() { prefDataLoc = findPreference(AppPreferencesImpl.DATA_STORAGE_LOCATION); if (prefDataLoc != null) { prefDataLoc.setOnPreferenceClickListener(p -> { - Intent intent = new Intent(MainApp.getAppContext(), ChooseStorageLocationActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - startActivityForResult(intent, ACTION_SET_STORAGE_LOCATION); + Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.StorageLocation); + startActivityForResult(intent, ExtendedSettingsActivityDialog.StorageLocation.getResultId()); return true; }); } - ListPreference themePref = (ListPreference) findPreference("darkMode"); + final var themePref = findPreference("darkMode"); + if (themePref != null) { + updateThemePreferenceSummary(preferences.getDarkThemeMode().name()); - List themeEntries = new ArrayList<>(3); - themeEntries.add(getString(R.string.prefs_value_theme_light)); - themeEntries.add(getString(R.string.prefs_value_theme_dark)); - themeEntries.add(getString(R.string.prefs_value_theme_system)); - - List themeValues = new ArrayList<>(3); - themeValues.add(DarkMode.LIGHT.name()); - themeValues.add(DarkMode.DARK.name()); - themeValues.add(DarkMode.SYSTEM.name()); + themePref.setOnPreferenceClickListener(preference -> { + Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.Theme); + startActivityForResult(intent, ExtendedSettingsActivityDialog.Theme.getResultId()); + return true; + }); + } + } - themePref.setEntries(themeEntries.toArray(new String[0])); - themePref.setEntryValues(themeValues.toArray(new String[0])); + private void updateThemePreferenceSummary(String themeValue) { + Preference themePref = findPreference("darkMode"); + if (themePref == null) return; - if (TextUtils.isEmpty(themePref.getEntry())) { - themePref.setValue(DarkMode.SYSTEM.name()); - themePref.setSummary(TextUtils.isEmpty(themePref.getEntry()) ? DarkMode.SYSTEM.name() : themePref.getEntry()); + DarkMode mode; + try { + mode = DarkMode.valueOf(themeValue); + } catch (IllegalArgumentException e) { + mode = DarkMode.SYSTEM; } - themePref.setOnPreferenceChangeListener((preference, newValue) -> { - DarkMode mode = DarkMode.valueOf((String) newValue); - preferences.setDarkThemeMode(mode); - MainApp.setAppTheme(mode); - setListBackground(); + String summary; + switch (mode) { + case LIGHT: + summary = getString(R.string.prefs_value_theme_light); + break; + case DARK: + summary = getString(R.string.prefs_value_theme_dark); + break; + case SYSTEM: + default: + summary = getString(R.string.prefs_value_theme_system); + break; + } - return true; - }); + themePref.setSummary(summary); } private void setListBackground() { @@ -1052,8 +1061,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == ACTION_E2E && data != null && data.getBooleanExtra(SetupEncryptionDialogFragment.SUCCESS, false)) { Intent i = new Intent(this, SettingsActivity.class); startActivity(i); - } else if (requestCode == ACTION_SET_STORAGE_LOCATION && data != null) { - String newPath = data.getStringExtra(ChooseStorageLocationActivity.KEY_RESULT_STORAGE_LOCATION); + } else if (requestCode == ExtendedSettingsActivityDialog.StorageLocation.getResultId() && data != null) { + String newPath = data.getStringExtra(ExtendedSettingsActivityDialog.StorageLocation.getKey()); if (storagePath != null && !storagePath.equals(newPath)) { StorageMigration storageMigration = new StorageMigration(this, user, storagePath, newPath, viewThemeUtils); @@ -1063,6 +1072,11 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == REQ_ALL_FILES_ACCESS) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); setupAllFilesAccessPreference(preferenceCategorySync); + } else if (requestCode == ExtendedSettingsActivityDialog.StorageLocation.getResultId() && data != null) { + String selectedTheme = data.getStringExtra(ThemeSelectionDialog.RESULT_KEY); + if (selectedTheme != null) { + updateThemePreferenceSummary(selectedTheme); + } } } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt new file mode 100644 index 000000000000..15d276eee269 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt @@ -0,0 +1,133 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.dialog + +import android.app.Dialog +import android.os.Bundle +import android.view.View +import android.view.ViewGroup +import android.widget.RadioButton +import androidx.fragment.app.DialogFragment +import androidx.appcompat.app.AlertDialog +import androidx.core.os.bundleOf +import androidx.fragment.app.setFragmentResult +import com.google.android.material.button.MaterialButton +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.preferences.AppPreferences +import com.nextcloud.client.preferences.AppPreferencesImpl +import com.nextcloud.client.preferences.DarkMode +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.nextcloud.client.di.Injectable +import com.owncloud.android.utils.theme.ViewThemeUtils +import javax.inject.Inject + +class ThemeSelectionDialog : DialogFragment(), Injectable { + + @Inject + lateinit var preferences: AppPreferences + + @Inject + lateinit var viewThemeUtils: ViewThemeUtils + + override fun onStart() { + super.onStart() + colorButtons() + colorRadioButtons() + } + + private fun colorButtons() { + val dialog = dialog + + if (dialog is AlertDialog) { + val positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton + positiveButton?.let { + viewThemeUtils.material.colorMaterialButtonPrimaryTonal(it) + } + } + } + + private fun colorRadioButtons() { + val dialog = dialog + if (dialog is AlertDialog) { + val listView = dialog.listView ?: return + + for (i in 0 until listView.childCount) { + val row = listView.getChildAt(i) + + val radioButton = findRadioButtonInView(row) + radioButton?.let { + viewThemeUtils.platform.themeRadioButton(it) + } + } + } + } + + private fun findRadioButtonInView(view: View): RadioButton? { + if (view is RadioButton) return view + if (view is ViewGroup) { + for (i in 0 until view.childCount) { + findRadioButtonInView(view.getChildAt(i))?.let { return it } + } + } + return null + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + preferences = AppPreferencesImpl.fromContext(context) + + val themeEntries = arrayOf( + getString(R.string.prefs_value_theme_light), + getString(R.string.prefs_value_theme_dark), + getString(R.string.prefs_value_theme_system) + ) + + val themeValues = arrayOf( + DarkMode.LIGHT.name, + DarkMode.DARK.name, + DarkMode.SYSTEM.name + ) + + val currentTheme = preferences.getDarkThemeMode()?.name ?: DarkMode.SYSTEM.name + val selectedIndex = themeValues.indexOf(currentTheme) + + val builder = MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.prefs_theme_title) + .setSingleChoiceItems(themeEntries, selectedIndex) { dialog, which -> + if (which >= 0 && which < themeValues.size) { + val selectedValue = themeValues[which] + val mode = DarkMode.valueOf(selectedValue) + + preferences.setDarkThemeMode(mode) + MainApp.setAppTheme(mode) + + setFragmentResult( + RESULT_KEY, + bundleOf(RESULT_KEY to selectedValue) + ) + + dialog.dismiss() + } + } + .setPositiveButton(R.string.common_cancel) { _, _ -> + dismiss() + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(requireContext(), builder) + + return builder.create() + } + + companion object { + const val RESULT_KEY = "theme_selection_result" + + fun newInstance(): ThemeSelectionDialog { + return ThemeSelectionDialog() + } + } +} diff --git a/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt b/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt new file mode 100644 index 000000000000..e456b098e996 --- /dev/null +++ b/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt @@ -0,0 +1,16 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.owncloud.android.ui.model + +import com.nextcloud.ui.ChooseStorageLocationDialogFragment +import com.owncloud.android.ui.dialog.ThemeSelectionDialog + +enum class ExtendedSettingsActivityDialog(val key: String, val resultId: Int) { + StorageLocation(ChooseStorageLocationDialogFragment.KEY_RESULT_STORAGE_LOCATION, 13), + Theme(ThemeSelectionDialog.RESULT_KEY, 14) +} diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index adada5180474..ff17922ff1f8 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -17,7 +17,7 @@ android:title="@string/prefs_data_storage_location" android:key="data_storage_location" android:summary="@string/prefs_data_storage_location_summary" /> - From 051068b3e09f95481414073617a62df1348bde08 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 5 Feb 2026 16:34:53 +0100 Subject: [PATCH 08/83] feat(settings): m3 theme selection dialog Signed-off-by: alperozturk96 --- .../android/ui/activity/SettingsActivity.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index 02fad9543aa4..66e3f851dc50 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -908,19 +908,11 @@ private void updateThemePreferenceSummary(String themeValue) { mode = DarkMode.SYSTEM; } - String summary; - switch (mode) { - case LIGHT: - summary = getString(R.string.prefs_value_theme_light); - break; - case DARK: - summary = getString(R.string.prefs_value_theme_dark); - break; - case SYSTEM: - default: - summary = getString(R.string.prefs_value_theme_system); - break; - } + String summary = switch (mode) { + case LIGHT -> getString(R.string.prefs_value_theme_light); + case DARK -> getString(R.string.prefs_value_theme_dark); + default -> getString(R.string.prefs_value_theme_system); + }; themePref.setSummary(summary); } From 73fc15c9d5287e6b06bc28069888c45a6aa37ec9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 5 Feb 2026 16:37:14 +0100 Subject: [PATCH 09/83] feat(settings): m3 theme selection dialog Signed-off-by: alperozturk96 --- .../java/com/owncloud/android/ui/activity/SettingsActivity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index 66e3f851dc50..cbd7746c3dd2 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -1064,7 +1064,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } else if (requestCode == REQ_ALL_FILES_ACCESS) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); setupAllFilesAccessPreference(preferenceCategorySync); - } else if (requestCode == ExtendedSettingsActivityDialog.StorageLocation.getResultId() && data != null) { + } else if (requestCode == ExtendedSettingsActivityDialog.Theme.getResultId() && data != null) { String selectedTheme = data.getStringExtra(ThemeSelectionDialog.RESULT_KEY); if (selectedTheme != null) { updateThemePreferenceSummary(selectedTheme); From 8a61ba404631994f774103e1528bed250de6b86e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 09:04:56 +0100 Subject: [PATCH 10/83] simplify code Signed-off-by: alperozturk96 --- .../ui/ChooseStorageLocationDialogFragment.kt | 11 +-- .../ui/activity/ExtendedSettingsActivity.kt | 81 ++++--------------- .../android/ui/activity/SettingsActivity.java | 16 ++-- .../android/ui/dialog/ThemeSelectionDialog.kt | 25 +++--- .../model/ExtendedSettingsActivityDialog.kt | 40 ++++++++- 5 files changed, 71 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt index fc9760fd92b9..5b95203c6900 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -25,6 +25,7 @@ import com.owncloud.android.datastorage.DataStorageProvider import com.owncloud.android.datastorage.StoragePoint import com.owncloud.android.datastorage.StoragePoint.PrivacyType import com.owncloud.android.datastorage.StoragePoint.StorageType +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog import com.owncloud.android.utils.DisplayUtils import com.owncloud.android.utils.theme.ViewThemeUtils import java.io.File @@ -151,19 +152,13 @@ class ChooseStorageLocationDialogFragment : ?: return val resultBundle = Bundle().apply { - putString(KEY_RESULT_STORAGE_LOCATION, newPath.path) + putString(ExtendedSettingsActivityDialog.StorageLocation.key, newPath.path) } - parentFragmentManager.setFragmentResult(KEY_RESULT_STORAGE_LOCATION, resultBundle) + parentFragmentManager.setFragmentResult(ExtendedSettingsActivityDialog.StorageLocation.key, resultBundle) } companion object { - const val KEY_RESULT_STORAGE_LOCATION = "KEY_RESULT_STORAGE_LOCATION" - const val STORAGE_LOCATION_RESULT_CODE = 100 - - @JvmStatic - fun newInstance() = ChooseStorageLocationDialogFragment() - @JvmStatic val TAG: String = Companion::class.java.simpleName } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt index ca35dec46378..d96c91f2c0c8 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/ExtendedSettingsActivity.kt @@ -1,17 +1,23 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + package com.owncloud.android.ui.activity import android.content.Context import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.nextcloud.ui.ChooseStorageLocationDialogFragment -import com.owncloud.android.ui.dialog.ThemeSelectionDialog import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog class ExtendedSettingsActivity : AppCompatActivity() { private var dialogShown = false + @Suppress("ReturnCount") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -23,23 +29,17 @@ class ExtendedSettingsActivity : AppCompatActivity() { return } - val dialogType = intent.getStringExtra(EXTRA_DIALOG_TYPE) ?: run { + val dialogKey = intent.getStringExtra(EXTRA_DIALOG_TYPE) ?: run { finish() return } - when (dialogType) { - ExtendedSettingsActivityDialog.StorageLocation.key -> { - setupStorageLocationDialog() - } - ExtendedSettingsActivityDialog.Theme.key -> { - setupThemeDialog() - } - else -> { - finish() - } + val dialogType = ExtendedSettingsActivityDialog.entries.find { it.key == dialogKey } ?: run { + finish() + return } + dialogType.showDialog(this) dialogShown = true } @@ -48,64 +48,13 @@ class ExtendedSettingsActivity : AppCompatActivity() { outState.putBoolean(KEY_DIALOG_SHOWN, dialogShown) } - private fun setupStorageLocationDialog() { - if (supportFragmentManager.findFragmentByTag(TAG_STORAGE_DIALOG) != null) { - return - } - - val dialog = ChooseStorageLocationDialogFragment.newInstance() - - supportFragmentManager.setFragmentResultListener( - ExtendedSettingsActivityDialog.StorageLocation.key, - this - ) { _, result -> - setResult( - ChooseStorageLocationDialogFragment.STORAGE_LOCATION_RESULT_CODE, - Intent().putExtra( - ExtendedSettingsActivityDialog.StorageLocation.key, - result.getString(ExtendedSettingsActivityDialog.StorageLocation.key) - ) - ) - finish() - } - - dialog.show(supportFragmentManager, TAG_STORAGE_DIALOG) - } - - private fun setupThemeDialog() { - if (supportFragmentManager.findFragmentByTag(TAG_THEME_DIALOG) != null) { - return - } - - val dialog = ThemeSelectionDialog.newInstance() - - supportFragmentManager.setFragmentResultListener( - ThemeSelectionDialog.RESULT_KEY, - this - ) { _, result -> - setResult( - RESULT_OK, - Intent().putExtra( - ThemeSelectionDialog.RESULT_KEY, - result.getString(ThemeSelectionDialog.RESULT_KEY) - ) - ) - finish() - } - - dialog.show(supportFragmentManager, TAG_THEME_DIALOG) - } - companion object { private const val EXTRA_DIALOG_TYPE = "dialog_type" private const val KEY_DIALOG_SHOWN = "dialog_shown" - private const val TAG_STORAGE_DIALOG = "choose_storage_location" - private const val TAG_THEME_DIALOG = "theme_selection" - fun createIntent(context: Context, dialogType: ExtendedSettingsActivityDialog): Intent { - return Intent(context, ExtendedSettingsActivity::class.java).apply { + fun createIntent(context: Context, dialogType: ExtendedSettingsActivityDialog): Intent = + Intent(context, ExtendedSettingsActivity::class.java).apply { putExtra(EXTRA_DIALOG_TYPE, dialogType.key) } - } } } diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index cbd7746c3dd2..ae070dbdd918 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -68,7 +68,6 @@ import com.owncloud.android.ui.ListPreferenceDialog; import com.owncloud.android.ui.ThemeableSwitchPreference; import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; -import com.owncloud.android.ui.dialog.ThemeSelectionDialog; import com.owncloud.android.ui.dialog.setupEncryption.SetupEncryptionDialogFragment; import com.owncloud.android.ui.helpers.FileOperationsHelper; import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog; @@ -890,8 +889,8 @@ private void setupGeneralCategory() { updateThemePreferenceSummary(preferences.getDarkThemeMode().name()); themePref.setOnPreferenceClickListener(preference -> { - Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.Theme); - startActivityForResult(intent, ExtendedSettingsActivityDialog.Theme.getResultId()); + Intent intent = ExtendedSettingsActivity.Companion.createIntent(this, ExtendedSettingsActivityDialog.ThemeSelection); + startActivityForResult(intent, ExtendedSettingsActivityDialog.ThemeSelection.getResultId()); return true; }); } @@ -1055,20 +1054,19 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { startActivity(i); } else if (requestCode == ExtendedSettingsActivityDialog.StorageLocation.getResultId() && data != null) { String newPath = data.getStringExtra(ExtendedSettingsActivityDialog.StorageLocation.getKey()); - if (storagePath != null && !storagePath.equals(newPath)) { StorageMigration storageMigration = new StorageMigration(this, user, storagePath, newPath, viewThemeUtils); storageMigration.setStorageMigrationProgressListener(this); storageMigration.migrate(); } - } else if (requestCode == REQ_ALL_FILES_ACCESS) { - final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); - setupAllFilesAccessPreference(preferenceCategorySync); - } else if (requestCode == ExtendedSettingsActivityDialog.Theme.getResultId() && data != null) { - String selectedTheme = data.getStringExtra(ThemeSelectionDialog.RESULT_KEY); + } else if (requestCode == ExtendedSettingsActivityDialog.ThemeSelection.getResultId() && data != null) { + String selectedTheme = data.getStringExtra(ExtendedSettingsActivityDialog.ThemeSelection.getKey()); if (selectedTheme != null) { updateThemePreferenceSummary(selectedTheme); } + } else if (requestCode == REQ_ALL_FILES_ACCESS) { + final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); + setupAllFilesAccessPreference(preferenceCategorySync); } } diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt index 15d276eee269..276385ad5de4 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt @@ -12,22 +12,24 @@ import android.os.Bundle import android.view.View import android.view.ViewGroup import android.widget.RadioButton -import androidx.fragment.app.DialogFragment import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf +import androidx.fragment.app.DialogFragment import androidx.fragment.app.setFragmentResult import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.AppPreferences -import com.nextcloud.client.preferences.AppPreferencesImpl import com.nextcloud.client.preferences.DarkMode import com.owncloud.android.MainApp import com.owncloud.android.R -import com.nextcloud.client.di.Injectable +import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject -class ThemeSelectionDialog : DialogFragment(), Injectable { +class ThemeSelectionDialog : + DialogFragment(), + Injectable { @Inject lateinit var preferences: AppPreferences @@ -68,6 +70,7 @@ class ThemeSelectionDialog : DialogFragment(), Injectable { } } + @Suppress("ReturnCount") private fun findRadioButtonInView(view: View): RadioButton? { if (view is RadioButton) return view if (view is ViewGroup) { @@ -79,8 +82,6 @@ class ThemeSelectionDialog : DialogFragment(), Injectable { } override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - preferences = AppPreferencesImpl.fromContext(context) - val themeEntries = arrayOf( getString(R.string.prefs_value_theme_light), getString(R.string.prefs_value_theme_dark), @@ -107,8 +108,8 @@ class ThemeSelectionDialog : DialogFragment(), Injectable { MainApp.setAppTheme(mode) setFragmentResult( - RESULT_KEY, - bundleOf(RESULT_KEY to selectedValue) + ExtendedSettingsActivityDialog.ThemeSelection.key, + bundleOf(ExtendedSettingsActivityDialog.ThemeSelection.key to selectedValue) ) dialog.dismiss() @@ -122,12 +123,4 @@ class ThemeSelectionDialog : DialogFragment(), Injectable { return builder.create() } - - companion object { - const val RESULT_KEY = "theme_selection_result" - - fun newInstance(): ThemeSelectionDialog { - return ThemeSelectionDialog() - } - } } diff --git a/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt b/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt index e456b098e996..4316d78f7755 100644 --- a/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/model/ExtendedSettingsActivityDialog.kt @@ -7,10 +7,44 @@ package com.owncloud.android.ui.model +import android.app.Activity.RESULT_OK +import android.content.Intent import com.nextcloud.ui.ChooseStorageLocationDialogFragment +import com.owncloud.android.ui.activity.ExtendedSettingsActivity import com.owncloud.android.ui.dialog.ThemeSelectionDialog -enum class ExtendedSettingsActivityDialog(val key: String, val resultId: Int) { - StorageLocation(ChooseStorageLocationDialogFragment.KEY_RESULT_STORAGE_LOCATION, 13), - Theme(ThemeSelectionDialog.RESULT_KEY, 14) +@Suppress("MagicNumber") +enum class ExtendedSettingsActivityDialog(val tag: String, val key: String, val resultId: Int) { + StorageLocation("choose_storage_location", "storage_selection_result", 13), + ThemeSelection("theme_selection", "theme_selection_result", 14); + + fun showDialog(activity: ExtendedSettingsActivity) { + activity.run { + if (supportFragmentManager.findFragmentByTag(tag) != null) { + return + } + + supportFragmentManager.setFragmentResultListener( + key, + this + ) { _, result -> + setResult( + RESULT_OK, + Intent().putExtra( + key, + result.getString(key) + ) + ) + finish() + } + + if (this@ExtendedSettingsActivityDialog == StorageLocation) { + ChooseStorageLocationDialogFragment() + } else { + ThemeSelectionDialog() + }.run { + show(supportFragmentManager, tag) + } + } + } } From 490e63357a9253d9d1f6974d521e5cddb2154a11 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 09:08:43 +0100 Subject: [PATCH 11/83] simplify code Signed-off-by: alperozturk96 --- .../java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt index 276385ad5de4..1d7f619bf1f6 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt @@ -115,7 +115,7 @@ class ThemeSelectionDialog : dialog.dismiss() } } - .setPositiveButton(R.string.common_cancel) { _, _ -> + .setPositiveButton(R.string.common_ok) { _, _ -> dismiss() } From af8cdb0c3e404af6f652a282aae62d6839f967a4 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 09:26:09 +0100 Subject: [PATCH 12/83] color dialog Signed-off-by: alperozturk96 --- .../android/ui/activity/SettingsActivity.java | 1 + .../android/ui/dialog/ThemeSelectionDialog.kt | 106 +++++++----------- .../res/layout/dialog_theme_selection.xml | 65 +++++++++++ 3 files changed, 106 insertions(+), 66 deletions(-) create mode 100644 app/src/main/res/layout/dialog_theme_selection.xml diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index ae070dbdd918..c29605b62d07 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -1063,6 +1063,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { String selectedTheme = data.getStringExtra(ExtendedSettingsActivityDialog.ThemeSelection.getKey()); if (selectedTheme != null) { updateThemePreferenceSummary(selectedTheme); + recreate(); } } else if (requestCode == REQ_ALL_FILES_ACCESS) { final PreferenceCategory preferenceCategorySync = (PreferenceCategory) findPreference("sync"); diff --git a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt index 1d7f619bf1f6..1d282c6943ab 100644 --- a/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt +++ b/app/src/main/java/com/owncloud/android/ui/dialog/ThemeSelectionDialog.kt @@ -9,9 +9,6 @@ package com.owncloud.android.ui.dialog import android.app.Dialog import android.os.Bundle -import android.view.View -import android.view.ViewGroup -import android.widget.RadioButton import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf import androidx.fragment.app.DialogFragment @@ -23,6 +20,7 @@ import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.client.preferences.DarkMode import com.owncloud.android.MainApp import com.owncloud.android.R +import com.owncloud.android.databinding.DialogThemeSelectionBinding import com.owncloud.android.ui.model.ExtendedSettingsActivityDialog import com.owncloud.android.utils.theme.ViewThemeUtils import javax.inject.Inject @@ -37,84 +35,50 @@ class ThemeSelectionDialog : @Inject lateinit var viewThemeUtils: ViewThemeUtils + private lateinit var binding: DialogThemeSelectionBinding + override fun onStart() { super.onStart() - colorButtons() - colorRadioButtons() - } - - private fun colorButtons() { - val dialog = dialog + val alertDialog = dialog as AlertDialog - if (dialog is AlertDialog) { - val positiveButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton - positiveButton?.let { - viewThemeUtils.material.colorMaterialButtonPrimaryTonal(it) - } + val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton + positiveButton?.let { + viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton) } } - private fun colorRadioButtons() { - val dialog = dialog - if (dialog is AlertDialog) { - val listView = dialog.listView ?: return + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + binding = DialogThemeSelectionBinding.inflate(layoutInflater) - for (i in 0 until listView.childCount) { - val row = listView.getChildAt(i) + val currentTheme = preferences.getDarkThemeMode() ?: DarkMode.SYSTEM + val radioGroup = binding.themeRadioGroup - val radioButton = findRadioButtonInView(row) - radioButton?.let { - viewThemeUtils.platform.themeRadioButton(it) - } - } + viewThemeUtils.platform.run { + colorTextView(binding.dialogTitle) + themeRadioButton(binding.themeDark) + themeRadioButton(binding.themeLight) + themeRadioButton(binding.themeSystem) } - } - @Suppress("ReturnCount") - private fun findRadioButtonInView(view: View): RadioButton? { - if (view is RadioButton) return view - if (view is ViewGroup) { - for (i in 0 until view.childCount) { - findRadioButtonInView(view.getChildAt(i))?.let { return it } - } + when (currentTheme) { + DarkMode.LIGHT -> radioGroup.check(R.id.theme_light) + DarkMode.DARK -> radioGroup.check(R.id.theme_dark) + DarkMode.SYSTEM -> radioGroup.check(R.id.theme_system) } - return null - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val themeEntries = arrayOf( - getString(R.string.prefs_value_theme_light), - getString(R.string.prefs_value_theme_dark), - getString(R.string.prefs_value_theme_system) - ) - val themeValues = arrayOf( - DarkMode.LIGHT.name, - DarkMode.DARK.name, - DarkMode.SYSTEM.name - ) + radioGroup.setOnCheckedChangeListener { _, checkedId -> + val selectedMode = when (checkedId) { + R.id.theme_light -> DarkMode.LIGHT + R.id.theme_dark -> DarkMode.DARK + R.id.theme_system -> DarkMode.SYSTEM + else -> DarkMode.SYSTEM + } - val currentTheme = preferences.getDarkThemeMode()?.name ?: DarkMode.SYSTEM.name - val selectedIndex = themeValues.indexOf(currentTheme) + applyTheme(selectedMode) + } val builder = MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.prefs_theme_title) - .setSingleChoiceItems(themeEntries, selectedIndex) { dialog, which -> - if (which >= 0 && which < themeValues.size) { - val selectedValue = themeValues[which] - val mode = DarkMode.valueOf(selectedValue) - - preferences.setDarkThemeMode(mode) - MainApp.setAppTheme(mode) - - setFragmentResult( - ExtendedSettingsActivityDialog.ThemeSelection.key, - bundleOf(ExtendedSettingsActivityDialog.ThemeSelection.key to selectedValue) - ) - - dialog.dismiss() - } - } + .setView(binding.root) .setPositiveButton(R.string.common_ok) { _, _ -> dismiss() } @@ -123,4 +87,14 @@ class ThemeSelectionDialog : return builder.create() } + + private fun applyTheme(mode: DarkMode) { + preferences.setDarkThemeMode(mode) + MainApp.setAppTheme(mode) + + setFragmentResult( + ExtendedSettingsActivityDialog.ThemeSelection.key, + bundleOf(ExtendedSettingsActivityDialog.ThemeSelection.key to mode.name) + ) + } } diff --git a/app/src/main/res/layout/dialog_theme_selection.xml b/app/src/main/res/layout/dialog_theme_selection.xml new file mode 100644 index 000000000000..447ccc7c83eb --- /dev/null +++ b/app/src/main/res/layout/dialog_theme_selection.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + From b5ff7c6d7d8a3ad2a449a2048068e31dbb0f2559 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 09:34:01 +0100 Subject: [PATCH 13/83] color dialog Signed-off-by: alperozturk96 --- .../java/com/owncloud/android/ui/activity/SettingsActivity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index c29605b62d07..65e70c062977 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/app/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -1063,6 +1063,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { String selectedTheme = data.getStringExtra(ExtendedSettingsActivityDialog.ThemeSelection.getKey()); if (selectedTheme != null) { updateThemePreferenceSummary(selectedTheme); + + // needed for to change status bar color recreate(); } } else if (requestCode == REQ_ALL_FILES_ACCESS) { From 537d7850a99e0be8fee0dae7820d40db3833dba2 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sat, 7 Feb 2026 03:10:15 +0000 Subject: [PATCH 14/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-cs-rCZ/strings.xml | 5 +++++ app/src/main/res/values-ga/strings.xml | 16 ++++++++++++---- app/src/main/res/values-hr/strings.xml | 6 ++++++ app/src/main/res/values-uk/strings.xml | 8 ++++++++ 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index d770f9cc1705..d9dbcaf97446 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -53,6 +53,7 @@ Smazat úkol Zkuste poslat zprávu pro rozproudění konverzace. Zdravím! Jak vám mohu pomoci? + Vyberte úkol Poslat zprávu Otevřít seznam konverzací Při vytváření úlohy se vyskytla chyba @@ -1015,8 +1016,11 @@ Náhled pro nový soubor Načítání trvá déle než očekáváno Dnes + Zadejte text který přeložit… Přeložit z: Přeložit do: + Přeložíte kliknutím na tlačítko + Překládání trvá déle než očekáváno. Překládá se… Smazané soubory Žádné smazané soubory @@ -1086,6 +1090,7 @@ Oprávnění aplikace Bez přístupu k lokálnímu úložišti není možné vaše soubory nahrávat. Klepnutím udělte oprávnění. Nahrávání zastaveno – je zapotřebí oprávnění pro úložiště + Je možné odebrat nebo pokračovat z Nahrávání %1$d / %2$d - %3$s Šifrování je možné pouze na systému Android verze 5.0 a novějším Pro zkopírování vybraných souborů do složky %1$s není dostatek volného místa. Chcete je tam namísto toho přesunout? diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index ae9dcbe5e826..f1c52ab4b857 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -53,6 +53,7 @@ Scrios tasc Bain triail as teachtaireacht a sheoladh chun tús a chur le comhrá. Dia duit ann! Cad is féidir liom cabhrú leat inniu? + Roghnaigh tasc le do thoil Seol teachtaireacht Oscail an liosta comhrá Tharla earráid agus an tasc á chruthú @@ -123,7 +124,7 @@ Socraigh fillteán saincheaptha Díchumasaigh seiceáil sábhála cumhachta Folaigh fillteán - Avatar + Abhatár Amach Socruithe cúltaca Teagmhálacha agus cúltaca féilire @@ -588,7 +589,7 @@ Logchomhaid: %1$d kB, freagraíodh an cheist %2$d / %3$d i %4$d ms Á lódáil… Logchomhaid: %1$d kB, gan scagaire - Logs + Logaí Tá an freastalaí i mód cothabhála Sonraí soiléire Scriosfar socruithe, bunachar sonraí agus teastais an fhreastalaí ó shonraí %1$s go buan. \n\nCoimeádfar comhaid íosluchtaithe gan teagmháil.\n\nTógfaidh an próiseas seo tamall. @@ -719,7 +720,7 @@ Féilire agus sioncrónú teagmhálaithe socraithe Faoi Sonraí - Dev + Forbróir Comhaid Ginearálta Tuilleadh @@ -982,7 +983,7 @@ Sioncronaigh dúbailt Níorbh fhéidir %1$s a shioncronú Pasfhocal mícheart le haghaidh %1$s - Kept-in-sync files failed + Theip ar chomhaid a choinneáil sioncrónaithe Theip ar shioncronú Theip ar shioncronú, logáil isteach arís Tá ábhar an chomhaid sioncronaithe cheana féin @@ -1015,6 +1016,12 @@ Mionsamhail don chomhad nua Tógann an luchtú níos faide ná mar a ceapadh Inniu + Cuir isteach téacs le haistriú… + Aistrigh ó: + Aistrigh go: + Brúigh an cnaipe chun aistriúchán a dhéanamh + Tá an t-aistriúchán ag glacadh níos faide ná mar a bhíothas ag súil leis. + Ag aistriú… Comhaid scriosta Níl aon chomhaid scriosta Beidh tú in ann comhaid a scriosadh a ghnóthú ó anseo. @@ -1083,6 +1090,7 @@ Ceadanna aipeanna Ní féidir do chuid comhad a uaslódáil gan rochtain ar stóras áitiúil. Tapáil chun cead a dheonú. Uaslódáil Stoptha – Cead Stórála Riachtanach + Is féidir leat é a bhaint nó a atosú ó Uaslódálacha %1$d / %2$d - %3$s Ní féidir criptiúchán ach amháin le 1>= Android 5.0 Ní bheidh dóthain spáis ann na comhaid roghnaithe a chóipeáil isteach san fhillteán %1$s. Ar mhaith leat iad a bhogadh ansin ina n-ionad? diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 12ba0b0079aa..0705ea2e7c5c 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -118,6 +118,7 @@ Učitavanje… Dalje Ne + Sada U redu Na čekanju Izbriši @@ -869,6 +870,11 @@ Pošalji poruku e-pošte Put pohrane ne postoji! To može biti zbog vraćanja sigurnosne kopije na drugom uređaju. Provjerite postavke i prilagodite put pohrane. + + %d sat + %d sata + %d sati + Nije moguće sinkronizirati datoteku %1$d (nepodudaranja: %2$d) Nije moguće sinkronizirati datoteke %1$d (nepodudaranja: %2$d) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 1e6d526165d5..b7df9be85353 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -53,6 +53,7 @@ Вилучити завдання Спробуйте надіслати повідомлення, щоб розпочати розмову. Привіт! Чим я можу вам сьогодні допомогти? + Виберіть завдання Надіслати повідомлення Відкрити список розмов Помилка під час додавання завдання @@ -1015,6 +1016,12 @@ Ескіз нового файлу Завантаження займає більше часу, ніж очікувалося Сьогодні + Додайте текст для перекладу... + Перекласти з: + Перекласти мовою: + Натисніть кнопку, щоб перекласти + Переклад триває довше очікуваного часу. + Триває переклад... Кошик Кошик порожній Тут можна відновити файли, які було вилучено. @@ -1083,6 +1090,7 @@ Дозволи застосунків Неможливо завантажити ваші файли без доступу до місця зберігання файлів на вашому пристрої. Натисніть, щоби надати доступ. Завантаження зупинено - потрібний доступ до місця зберігання файлів + Ви можете вилучити або повторити це із завантажень %1$d/%2$d-%3$s Шифрування можливе лише на пристроях з версію Android 5.0+ Нестача місця не дозволяє скопіювати файли до каталогу%1$s. Чи бажаєте перемістити їх замість копіювання? From 00c7e69dd84b0910c1b89aa8b2294578b28c6f64 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sun, 8 Feb 2026 03:10:59 +0000 Subject: [PATCH 15/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-et-rEE/strings.xml | 3 ++- app/src/main/res/values-zh-rHK/strings.xml | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 5fce75aa7c0a..74912753d0ca 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -51,7 +51,7 @@ Tagasi Abilise lehele Kas sa oled kindel, et soovid selle ülesande kustutada? Kustuta ülesanne - Vestluse algatamiseks proovi sata üks sõnum. + Vestluse algatamiseks proovi saata üks sõnum. Hei! Kuidas saan sind täna aidata? Palun vali ülesanne Saada sõnum @@ -1090,6 +1090,7 @@ Rakenduse õigused Sinu faile ei saa kohalikust andmeruumist üles laadida. Klõpsa õiguste lubamiseks. Üleslaadimine on peatunud - vajalikud on andmeruumi kasutamise õigused + Sa saad ta eemaldada või jätkata Üleslaadimiste kaustast %1$d / %2$d - %3$s Krüptimine on võimalik vaid >= Android 5.0 puhul Piisava ruumi puudumisel pole võimalik kopeerida valitud faile „%1$s“ kausta. Kas pigem tahaksid nad sinna teisaldada? diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index f9e145297777..3d86a40a78b3 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -53,6 +53,7 @@ 刪除任務 試著發送一條訊息來引發對話。 你好!今天我能幫你什麼忙呢? + 請選擇任務 傳送訊息 打開對話清單 建立任務項目時發生錯誤 @@ -1015,6 +1016,12 @@ 新增檔案的縮圖 載入時間比預期久 今日 + 輸入要翻譯的文字 ... + 翻譯從: + 翻譯至: + 按下按鈕翻譯 + 翻譯時間比預期長。 + 正在翻譯 ... 已刪除檔案 無已刪除檔案 您可以從這裡還原已刪除的檔案 @@ -1083,6 +1090,7 @@ 應用程式權限 您的檔案無法上傳,因為沒有訪問本地存儲的權限。請點擊以授予權限。 上傳已停止 – 需要儲存權限 + 您可以從上傳清單中刪除或還原它 %1$d / %2$d - %3$s 加密功能只適用於 Android 5.0 及以上版本 空間不足導致無法將所選檔案複製到 %1$s 資料夾中。您想改為將檔案移到那裡嗎? From 94e00bd71a4aa021b1758270d0b9143a2c35900f Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sun, 8 Feb 2026 11:17:27 +0100 Subject: [PATCH 16/83] style: Theme button Resolves #16470 Resolves #16471 Signed-off-by: Andy Scherzinger --- .../android/ui/fragment/FileDetailSharingFragment.java | 2 ++ app/src/main/res/layout/file_details_sharing_fragment.xml | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 3fc30c1057bf..6be0df6ba90d 100644 --- a/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/app/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -299,6 +299,8 @@ private void setupView() { viewThemeUtils.platform.colorImageView(binding.searchViewIcon, ColorRole.ON_SURFACE_VARIANT); viewThemeUtils.platform.colorImageView(binding.pickContactEmailBtn, ColorRole.ON_SURFACE_VARIANT); + viewThemeUtils.material.colorMaterialButtonPrimaryOutlined(binding.sendCopyBtn); + viewThemeUtils.material.colorMaterialButtonPrimaryBorderless(binding.sharesListInternalShowAll); viewThemeUtils.material.colorMaterialTextButton(binding.sharesListInternalShowAll); binding.sharesListInternalShowAll.setOnClickListener(view -> { diff --git a/app/src/main/res/layout/file_details_sharing_fragment.xml b/app/src/main/res/layout/file_details_sharing_fragment.xml index 14a903dcd4ef..4ee05b28248a 100644 --- a/app/src/main/res/layout/file_details_sharing_fragment.xml +++ b/app/src/main/res/layout/file_details_sharing_fragment.xml @@ -136,8 +136,7 @@ style="@style/Widget.Material3.Button.OutlinedButton" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginStart="@dimen/standard_double_margin" - android:layout_marginEnd="@dimen/standard_double_margin" + android:layout_marginHorizontal="@dimen/standard_margin" android:layout_marginTop="@dimen/standard_half_padding" android:layout_marginBottom="@dimen/standard_half_padding" app:icon="@drawable/file_link" From 65e1eab616df5ecdbca19e2453df948f2ed3e6b1 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sun, 8 Feb 2026 11:43:40 +0100 Subject: [PATCH 17/83] style: theme OK button for storage chooser Signed-off-by: Andy Scherzinger --- .../ui/ChooseStorageLocationDialogFragment.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt index 5b95203c6900..6a67415c0168 100644 --- a/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/ui/ChooseStorageLocationDialogFragment.kt @@ -14,7 +14,9 @@ import android.preference.PreferenceManager import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.appcompat.app.AlertDialog import androidx.fragment.app.DialogFragment +import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.nextcloud.client.di.Injectable import com.nextcloud.client.preferences.AppPreferencesImpl @@ -47,6 +49,16 @@ class ChooseStorageLocationDialogFragment : private val selectedPrivacyType get() = if (binding.allowMediaIndexSwitch.isChecked) PrivacyType.PUBLIC else PrivacyType.PRIVATE + override fun onStart() { + super.onStart() + val alertDialog = dialog as AlertDialog + + val positiveButton = alertDialog.getButton(AlertDialog.BUTTON_POSITIVE) as? MaterialButton + positiveButton?.let { + viewThemeUtils.material.colorMaterialButtonPrimaryTonal(positiveButton) + } + } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogDataStorageLocationBinding.inflate(layoutInflater) From 14f773b20436c0b6f3c33dde6a83d22c2c63e0f8 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sun, 8 Feb 2026 11:45:08 +0100 Subject: [PATCH 18/83] docs: Add screenshot block Signed-off-by: Andy Scherzinger --- .github/pull_request_template.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 3e4388b6488d..7e0d414786ac 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -8,4 +8,10 @@ Unit tests: https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md#uni Instrumented tests: https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md#instrumented-tests UI tests: https://github.com/nextcloud/android/blob/master/CONTRIBUTING.md#ui-tests --> +### 🖼️ Screenshots + +🏚️ Before | 🏡 After +---|--- +B | A + - [ ] Tests written, or not not needed From 33f75d292794ffa52684b3746c057d6a1ee0e11c Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sun, 8 Feb 2026 19:07:48 +0000 Subject: [PATCH 19/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-sw/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index f8b8c5688b66..3f3c1c82cb6d 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -53,6 +53,7 @@ Futa jukumu Jaribu kutuma ujumbe ili kuzua mazungumzo. Hujambo! Nikusaidie nini leo? + Tafadhali chagua kazi Tuma ujumbe Fungua orodha ya mazungumzo Hitilafu ilitokea wakati wa kuunda jukumu @@ -1015,6 +1016,12 @@ Kijipicha cha faili mpya Upakiaji unachukua muda mrefu kuliko ilivyotarajiwa Leo + Weka maandishi ili kutafsiri... + Tafsiri kutoka: + Tafsiri kwenye: + Bonyeza kitufe ili kutafsiri + Tafsiri inachukua muda mrefu kuliko ilivyotarajiwa. + Inatafsiri... Faili zilizofutwa Hakuna faili zilizofutwa Utaweza kurejesha faili zilizofutwa kutoka hapa. @@ -1083,6 +1090,7 @@ Ruhusa za programu Faili zako haziwezi kupakiwa bila ufikiaji wa hifadhi ya ndani. Gusa ili kutoa ruhusa. Upakiaji Umesimamishwa - Ruhusa ya Kuhifadhi Inahitajika + Unaweza kuiondoa au kuirejesha kutoka kwa Vipakiwa %1$d / %2$d - %3$s Usimbaji fiche unawezekana tu kwa >= Android 5.0 Nafasi haitoshi huzuia kunakili faili zilizochaguliwa kwenye folda ya %1$s. Je, ungependa kuzihamishia hapo badala yake? From 17a2b6aec789c9d020d4f7ed713800a86024352d Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 9 Feb 2026 03:07:09 +0000 Subject: [PATCH 20/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-zh-rTW/strings.xml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 221be7a19a27..d0200f8c4e63 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -53,6 +53,7 @@ 刪除工作項目 嘗試傳送訊息來引發對話。 嗨!我現在能怎麼協助您呢? + 請選取任務 傳送訊息 開啟對話清單 建立工作項目時發生錯誤 @@ -1015,6 +1016,12 @@ 新檔案的縮圖 載入時間比預期久 今天 + 輸入要翻譯的文字…… + 翻譯自: + 翻譯至: + 按下按鈕翻譯 + 翻譯時間比預期長。 + 正在翻譯…… 刪除的檔案 無刪除的檔案 您可以從這裡還原刪除的檔案。 @@ -1083,6 +1090,7 @@ 應用程式權限 若無法存取本機儲存空間,則無法上傳您的檔案。輕點即可授予權限。 上傳已停止 – 需要儲存空間權限 + 您可以從上傳清單中移除或還原它 %1$d / %2$d - %3$s 加密功能僅適用於 Android 5.0 及後續版本 空間不足以將選擇的檔案複製到 %1$s 資料夾。是否要改成移動它們? From 37f7f16f463a8bdfa6743af4faff3e57c03e77d7 Mon Sep 17 00:00:00 2001 From: Josh Date: Sat, 7 Feb 2026 10:49:20 -0500 Subject: [PATCH 21/83] fix: use positional formatter specifiers for `auto_upload_worker_start_text` Signed-off-by: Josh --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 97f04a8862af..9f682d40b69f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -986,7 +986,7 @@ Show notifications to interact result of background operations Show push notifications sent by the server: Mentions in comments, reception of new remote shares, announcements posted by an admin etc. Send button icon - Uploading files from %s to %s + Uploading files from %1$s to %2$s * Name Password From 8ab281f0f3f182ba46e1b1bd26947e3ab3da500c Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 9 Feb 2026 08:35:49 +0000 Subject: [PATCH 22/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-b+en+001/strings.xml | 1 - app/src/main/res/values-cs-rCZ/strings.xml | 1 - app/src/main/res/values-da/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 1 - app/src/main/res/values-et-rEE/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-ga/strings.xml | 1 - app/src/main/res/values-gl/strings.xml | 1 - app/src/main/res/values-hu-rHU/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-is/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja-rJP/strings.xml | 1 - app/src/main/res/values-lo/strings.xml | 1 - app/src/main/res/values-nl/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 1 - app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk-rSK/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-sw/strings.xml | 1 - app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-ug/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rHK/strings.xml | 1 - app/src/main/res/values-zh-rTW/strings.xml | 1 - 29 files changed, 29 deletions(-) diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index 3abf7744a1bf..4a1ffc76ac85 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -118,7 +118,6 @@ /AutoUpload This folder is already included in the parent folder’s sync, which may cause duplicate uploads Waiting for Wi-Fi to start uploading - Uploading files from %s to %s Configure Create new custom folder setup Set up a custom folder diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index d9dbcaf97446..2604bdec4e93 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -118,7 +118,6 @@ /AutoUpload Tato složka už je obsažena v synchronizaci nadřazené složky, což může způsobovat duplicitní nahrávání Čeká se na Wi-Fi, aby bylo možné spustit nahrávání - Jsou nahrávány soubory z %s do %s Nastavit Vytvořit nové uživatelsky určené nastavení složky Nastavit uživatelsky určenou složku diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index ff6190b60700..6dc2b6628854 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -101,7 +101,6 @@ /AutoUpload Denne mappe er allerede inkluderet i den overordnede mappes synkronisering, hvilket kan medføre dobbelt uploads Venter på Wi-Fi for at starte upload - Uploader filer fra %s til %s Konfigurer Opret en ny brugerdefineret mappeopsætning Opsæt en brugerdefineret mappe diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 221c93e909ce..81d1267e2de5 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -118,7 +118,6 @@ /AutoUpload Dieser Ordner ist bereits in der Synchronisierung des übergeordneten Ordners enthalten, was zu doppelten Uploads führen kann Warte auf WLAN für den Beginn des Hochladens - Lade Dateien von %s nach %s hoch Einrichten Erstellen Sie ein Setup für den eigenen Ordner Erstellen Sie einen eigenen Ordner diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a8f4fda604ce..e2fae5794077 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -100,7 +100,6 @@ Subir sólo con conexión Wi-Fi sin límite de datos /CargaAutomática Esta carpeta ya está incluida en la sincronización de la carpeta principal, lo que puede provocar subidas duplicadas. - Subiendo archivos desde %s a %s Configurar Crear nueva configuración de una carpeta especifica Configure una carpeta especifica diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 74912753d0ca..6bbd30c1b708 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -118,7 +118,6 @@ /AutoUpload Kuna ülalpool asuv kaust kuulub sünkroonimisele, siis see kaust on juba kaasatud ning nii võivad tekkida topelt üleslaadimised Ootan sünkroonimiseks WiFi ühenduse loomist - Laadin faile üles: %s kuni %s Seadista Loo uus kohandatud kausta seadistus Seadista kohandatud kaust diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 674a7f21dc1a..e42372a6d733 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -109,7 +109,6 @@ /TéléversementAuto Ce dossier fait partie de la synchronisation du dossier parent, ce qui peut créer des doublons. En attente du Wi-Fi pour commencer le téléversement - Téléversement des fichiers de %s vers %s Configurer Créer une nouvelle configuration de dossier personnalisé Définir un dossier personnalisé diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index f1c52ab4b857..20226400cd43 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -118,7 +118,6 @@ /Uaslódáil Uathoibríoch Tá an fillteán seo san áireamh cheana féin i gcomhshioncronú an fhillteáin tuismitheora, rud a d’fhéadfadh uaslódálacha dúblacha a chruthú Ag fanacht go dtosóidh Wi-Fi ag uaslódáil - Ag uaslódáil comhad ó %s go %s Cumraigh Cruthaigh socrú fillteán saincheaptha nua Socraigh fillteán saincheaptha diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 63bd70ff0ebc..327cc5299635 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -118,7 +118,6 @@ /EnvíoAutomático Este cartafol xa está incluído na sincronización do cartafol principal, iso pode provocar envíos duplicados Agardando pola wifi para iniciar o envío - Enviar ficheiros de %s a %s Configurar Crear un cartafol personalizado novo Configurar un cartafol personalizado diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index a6d79317fc3b..1ed29a1c93d8 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -117,7 +117,6 @@ /AutoUpload Ez a mappa már szerepel a szülőmappája szinkronizálásában, ami ismételt feltöltéseket okozhat Várakozás a Wi-Fire a feltöltés megkezdéséhez - Fájlok feltöltése innen: %s, ide: %s Beállítás Új egyéni mappabeállítás létrehozása Egyéni mappa beállítása diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 14d8dee2003b..9aa2f940fd93 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -102,7 +102,6 @@ /AutoUpload Folder ini sudah disertakan dalam sinkronisasi folder induk, sehingga dapat menyebabkan unggahan ganda. Menunggu Wi-Fi untuk mulai mengunggah - Mengunggah berkas dari %s ke %s Konfigurasi Buat kostum folder baru. Pasang folder kostum. diff --git a/app/src/main/res/values-is/strings.xml b/app/src/main/res/values-is/strings.xml index c1374610ba5a..da38ee7e98b4 100644 --- a/app/src/main/res/values-is/strings.xml +++ b/app/src/main/res/values-is/strings.xml @@ -97,7 +97,6 @@ Aðeins senda inn á gjaldfrjálsu WiFi-neti /SjálfvirkInnsending Þessa möppu er nú þegar verið að samstilla úr yfirmöppu, sem getur valdið margföldum innsendingum - Sendi inn skrár frá %s til %s Stilla Búa til nýja nýja sérsniðna uppsetningu á möppum Settu upp sérsniðna möppu diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index d1c5b7247713..ff6b2b854a14 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -107,7 +107,6 @@ /AutoUpload Questa cartella è già inclusa nella sincronizzazione della cartella principale, il che potrebbe causare caricamenti duplicati. In attesa del Wi-Fi per avviare il caricamento - Caricando i file da %s a %s Configura Crea nuova configurazione di cartella personalizzata Configura una cartella personalizzata diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 103470958d97..8a109738a20b 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -103,7 +103,6 @@ /AutoUpload このフォルダは既に親の同期に含まれているため、重複してアップロードされる可能性があります アップロードのためWi-Fiに接続されるのを待っています… - ファイルを%sから%sへアップロードしています。 設定 新しいカスタムフォルダセットアップを作成する カスタムフォルダを設定する diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index b2e8ed90701a..630fb36edbc5 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -105,7 +105,6 @@ /ອັບໂຫຼດອັດຕະໂນມັດ This folder is already included in the parent folder’s sync, which may cause duplicate uploads Waiting for Wi-Fi to start uploading - Uploading files from %s to %s ຕັ້ງຄ່າ Config ສ້າງການຕັ້ງຄ່າໂຟນເດີດ້ວຍຕົນເອງ ກຳໜົດການຕັ້ງຄ່າໂຟນເດີດ້ວຍຕົນເອງ diff --git a/app/src/main/res/values-nl/strings.xml b/app/src/main/res/values-nl/strings.xml index a1cbf9c9eba3..13ea6de9c973 100644 --- a/app/src/main/res/values-nl/strings.xml +++ b/app/src/main/res/values-nl/strings.xml @@ -96,7 +96,6 @@ Upload alleen met Wi-Fi /AutomatischUploaden Deze map is al opgenomen in de synchronisatie van de bovenliggende map, wat kan leiden tot dubbele uploads. - Bestanden aan het uploaden van %s naar %s Configureren Maak aangepaste nieuwe map aan Instellen aangepaste map diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index d9d6331b324d..9d6026c020d3 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -117,7 +117,6 @@ /AutoUpload Ten katalog jest już uwzględniony w synchronizacji katalogu nadrzędnego, co może powodować duplikowanie wysyłanych plików Oczekiwanie na Wi-Fi, aby zacząć aktualizowanie - Przesyłanie plików z %s do %s Konfiguruj Utwórz nową niestandardową konfigurację katalogu Ustaw własny katalog diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 3833e61b1e4b..77a4f49c6f31 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -118,7 +118,6 @@ /UploadAutomático Esta pasta já está incluída na sincronização da pasta pai, o que pode causar uploads duplicados Aguardando o Wi-Fi para iniciar o upload - Fazendo upload de arquivos de %s a %s Configurar Criar nova configuração de pasta personalizada Configurar uma pasta personalizada diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index bd4331a1d3e7..99b972920872 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -115,7 +115,6 @@ /Автозагрузка Эта папка уже включена в синхронизацию родительской папки, что может привести к дублированию загрузок Ожидание Wi-Fi для начала загрузки - Загрузка файлов из %s в %s Настроить Создать новую настройку пользовательского каталога Задать пользовательский каталог diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 76010039ab47..b68a492f0a70 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -109,7 +109,6 @@ /AutomatickéNahrávanie Tento adresár je už zahrnutý v synchronizácii nadradeného adresára, čo môže spôsobiť duplicitné nahrávania Čaká sa na Wi-Fi pre spustenie nahrávania. - Nahrávanie súborov z %s do %s Nastaviť Nastavenie vytvorenia vlastného priečinku Nastaviť vlastný priečinok diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 8c83c6d84205..240f3c831e35 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -103,7 +103,6 @@ /Аутоматска отпремања Овај фолдер је већ укључен у синхронизацију фолдера родитеља, па се могу јавити двострука отпремања Чека се на Wi-Fi за почетак отпремања - Отпремају се фајлови са %s на %s Подеси Направи нову поставку за посебну фасциклу Подесите произвољну фасциклу diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index ebc337f69b8a..1d82fea06619 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -106,7 +106,6 @@ /AutomatiskUppladdning Denna mapp ingår redan i synkroniseringen av den överordnade mappen, vilket kan orsaka dubbla uppladdningar Väntar på Wi-Fi för att starta uppladdning - Laddar upp filer från %s till %s Konfigurera Skapa en ny anpassad mappkonfiguration Skapa en anpassad mapp diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 3f3c1c82cb6d..94619ac025a8 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -118,7 +118,6 @@ /Pakia Kiotomatiki Folda hii tayari imejumuishwa katika usawazishaji wa folda kuu, ambayo inaweza kusababisha upakiaji unaorudiwa Inasubiri Wi-Fi kuanza kupakia - Inapakia faili kutoka %s hadi %s Sanidi Unda usanidi mpya wa folda maalum Sanidi folda maalum diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index b3e7999fab4b..e75d5753c2f4 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -110,7 +110,6 @@ /OtomatikYükleme Bu klasör, üst klasörün eşitlemesine zaten katılmış olduğundan yinelenen yüklemelere neden olabilir. Yüklemenin başlatılması için Wi-Fi bağlantısı bekleniyor - %s üzerindeki dosyalar %s üzerine yükleniyor Yapılandır Özel klasör kurulumu ekle Bir özel klasör kurun diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 7d3983af2c2f..6db09d590200 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -108,7 +108,6 @@ / ئۆزلىكىدىن چىقىرىش بۇ قىسقۇچ بولشا باش قىسقۇچنىڭ ئىچىدە بار، شۇڭلاشقا قوشلاپ چىقىرىلىشنى كەلتۈرۈپ چىقىرىشى مۇمكىن چىقىرىش ئۈچۈن ۋايف - ھۆججەتلەرنى %s دىن %s سەپلەڭ يېڭى خاس ھۆججەت قىسقۇچ قۇرۇش ئىختىيارى ھۆججەت قىسقۇچ ئورنىتىڭ diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index b7df9be85353..82b0fb0ffa9a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -118,7 +118,6 @@ /AutoUpload Цей каталог вже включено до синхронізації каталогу вищого рівня, що може призвести до задвоєння завантаження Очікується з\'єднання з WiFi для початку завантаження - Завантаження файлів з %s до %s Налаштування Власний каталог користувача Каталог користувача diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 7298203014dd..f8c129ace7a1 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -117,7 +117,6 @@ /自动上传 此文件夹已包含在上级文件夹的同步中,这可能会导致重复上传 正在等待 Wi-Fi 开始上传 - 正在将文件从 %s 上传到 %s 配置 创建新的自定义文件夹设定 设置一个自定义文件夹 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 3d86a40a78b3..f742c891c42d 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -118,7 +118,6 @@ 自動上傳 此資料夾已包含在父資料夾的同步中,這可能會導致重複上傳 等待 Wi-Fi 開始上傳 - 從 %s 上傳檔案至 %s 設定 新增自訂資料夾 設定自訂資料夾 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index d0200f8c4e63..bfbbb6415dde 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -118,7 +118,6 @@ /AutoUpload 此資料夾已包含在上層資料夾的同步中,這可能會導致重複上傳 等待 Wi-Fi 開始上傳 - 從 %s 上傳檔案至 %s 設定 新增自訂資料夾 設置自訂資料夾 From 9e4956137c6e4034a46cdd54a9dc92df72faa1a4 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 10 Feb 2026 03:14:59 +0000 Subject: [PATCH 23/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-cs-rCZ/strings.xml | 1 + app/src/main/res/values-ga/strings.xml | 1 + app/src/main/res/values-gl/strings.xml | 1 + app/src/main/res/values-pt-rBR/strings.xml | 1 + app/src/main/res/values-zh-rTW/strings.xml | 1 + 5 files changed, 5 insertions(+) diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 2604bdec4e93..11270088bf41 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -118,6 +118,7 @@ /AutoUpload Tato složka už je obsažena v synchronizaci nadřazené složky, což může způsobovat duplicitní nahrávání Čeká se na Wi-Fi, aby bylo možné spustit nahrávání + Nahrávání souborů z %1$s na %2$s Nastavit Vytvořit nové uživatelsky určené nastavení složky Nastavit uživatelsky určenou složku diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 20226400cd43..08053360eb1f 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -118,6 +118,7 @@ /Uaslódáil Uathoibríoch Tá an fillteán seo san áireamh cheana féin i gcomhshioncronú an fhillteáin tuismitheora, rud a d’fhéadfadh uaslódálacha dúblacha a chruthú Ag fanacht go dtosóidh Wi-Fi ag uaslódáil + Ag uaslódáil comhad ó %1$s go %2$s Cumraigh Cruthaigh socrú fillteán saincheaptha nua Socraigh fillteán saincheaptha diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 327cc5299635..af7f03bde94a 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -118,6 +118,7 @@ /EnvíoAutomático Este cartafol xa está incluído na sincronización do cartafol principal, iso pode provocar envíos duplicados Agardando pola wifi para iniciar o envío + Enviar ficheiros desde %1$s cara a %2$s Configurar Crear un cartafol personalizado novo Configurar un cartafol personalizado diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index 77a4f49c6f31..b131fcb9e65b 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -118,6 +118,7 @@ /UploadAutomático Esta pasta já está incluída na sincronização da pasta pai, o que pode causar uploads duplicados Aguardando o Wi-Fi para iniciar o upload + Fazendo upload de arquivos de %1$s para %2$s Configurar Criar nova configuração de pasta personalizada Configurar uma pasta personalizada diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index bfbbb6415dde..49e5cbdaf82e 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -118,6 +118,7 @@ /AutoUpload 此資料夾已包含在上層資料夾的同步中,這可能會導致重複上傳 等待 Wi-Fi 開始上傳 + 正在從 %1$s 上傳檔案到 %2$s 設定 新增自訂資料夾 設置自訂資料夾 From 180cb0f7c9e3b8ae921430fe5f384ae45c255d45 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 10 Feb 2026 08:26:25 +0100 Subject: [PATCH 24/83] cleanup Signed-off-by: alperozturk96 --- .../main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt index 3f1dc86424ef..e87ef399d635 100644 --- a/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt +++ b/app/src/main/java/com/owncloud/android/utils/EncryptionUtilsV2.kt @@ -997,7 +997,7 @@ class EncryptionUtilsV2 { return certs.any { cert -> runCatching { - signer.verify(verifierBuilder.build(cert)) + signer.verify(verifierBuilder.build(cert.publicKey)) }.getOrElse { Log_OC.e(TAG, "Exception verifySignedData: $it") false From 1a4ad6243c83547c2552342f548c08986eb6d52e Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 11 Feb 2026 03:08:14 +0000 Subject: [PATCH 25/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-b+en+001/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-hr/strings.xml | 6 ++++++ app/src/main/res/values-uk/strings.xml | 1 + 4 files changed, 9 insertions(+) diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index 4a1ffc76ac85..31114fb42401 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -118,6 +118,7 @@ /AutoUpload This folder is already included in the parent folder’s sync, which may cause duplicate uploads Waiting for Wi-Fi to start uploading + Uploading files from %1$s to %2$s Configure Create new custom folder setup Set up a custom folder diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 81d1267e2de5..f0f8bcb1bcf0 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -118,6 +118,7 @@ /AutoUpload Dieser Ordner ist bereits in der Synchronisierung des übergeordneten Ordners enthalten, was zu doppelten Uploads führen kann Warte auf WLAN für den Beginn des Hochladens + Lade Dateien von %1$s nach %2$s hoch Einrichten Erstellen Sie ein Setup für den eigenen Ordner Erstellen Sie einen eigenen Ordner diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 0705ea2e7c5c..b5bec22877cb 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -34,6 +34,7 @@ Traži u %s Prikaži se kao izvan mreže Izbriši zadatak + Zadatak je stvoren Asistent radi nepoznato @@ -875,6 +876,11 @@ %d sata %d sati + + %d minuta + %d minute + %d minuta + Nije moguće sinkronizirati datoteku %1$d (nepodudaranja: %2$d) Nije moguće sinkronizirati datoteke %1$d (nepodudaranja: %2$d) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 82b0fb0ffa9a..af48d1c80ce3 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -118,6 +118,7 @@ /AutoUpload Цей каталог вже включено до синхронізації каталогу вищого рівня, що може призвести до задвоєння завантаження Очікується з\'єднання з WiFi для початку завантаження + Завантаження файлів з %1$s до %2$s Налаштування Власний каталог користувача Каталог користувача From 3a3648323873ce8b9f3ffc6baba905b315525ccd Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Jan 2026 15:41:13 +0100 Subject: [PATCH 26/83] fix(share): internal link alignment Signed-off-by: alperozturk96 --- .../main/res/layout/file_details_share_internal_share_link.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/file_details_share_internal_share_link.xml b/app/src/main/res/layout/file_details_share_internal_share_link.xml index 8bc5eb8be1de..ddafa2b559d4 100644 --- a/app/src/main/res/layout/file_details_share_internal_share_link.xml +++ b/app/src/main/res/layout/file_details_share_internal_share_link.xml @@ -22,7 +22,7 @@ android:background="@drawable/round_bgnd" android:contentDescription="@string/share" android:id="@+id/copy_internal_link_icon" - android:layout_gravity="top" + android:layout_gravity="center" android:layout_height="@dimen/share_icon_size" android:layout_marginEnd="@dimen/standard_margin" android:layout_marginStart="@dimen/standard_margin" From 7124252eaf49bab4aef83ceae9bdb51a02a3eb5c Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Jan 2026 16:09:28 +0100 Subject: [PATCH 27/83] fix(share): drop down icon visibility Signed-off-by: alperozturk96 --- .../com/owncloud/android/ui/adapter/LinkShareViewHolder.kt | 6 +++--- .../main/res/layout/file_details_share_link_share_item.xml | 7 ++++--- app/src/main/res/layout/file_details_share_share_item.xml | 4 +++- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt index 3ebe6b08d380..83b9d152a4dd 100644 --- a/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt +++ b/app/src/main/java/com/owncloud/android/ui/adapter/LinkShareViewHolder.kt @@ -134,11 +134,11 @@ internal class LinkShareViewHolder(itemView: View) : RecyclerView.ViewHolder(ite if (TextUtils.isEmpty(permissionName) || (isSecureFileDrop(publicShare) && encrypted)) { binding.permissionName.visibility = View.GONE - return + } else { + binding.permissionName.visibility = View.VISIBLE + binding.permissionName.text = permissionName } - binding.permissionName.text = permissionName - binding.permissionName.visibility = View.VISIBLE viewThemeUtils?.androidx?.colorPrimaryTextViewElement(binding.permissionName) } diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index e7cf42512914..12ab0a9a85c0 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -63,14 +63,15 @@ diff --git a/app/src/main/res/layout/file_details_share_share_item.xml b/app/src/main/res/layout/file_details_share_share_item.xml index facdb0cdaa42..5f3003c0d53c 100644 --- a/app/src/main/res/layout/file_details_share_share_item.xml +++ b/app/src/main/res/layout/file_details_share_share_item.xml @@ -51,14 +51,16 @@ From 2c0ca6f615914aadcaf5e5b2b9ee31f6d752b872 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Jan 2026 16:30:44 +0100 Subject: [PATCH 28/83] make imageview material icon button Signed-off-by: alperozturk96 --- .../file_details_share_link_share_item.xml | 23 ++++++++++++------- .../layout/file_details_share_share_item.xml | 12 ++++++---- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index 12ab0a9a85c0..5625d31f2382 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -76,10 +76,11 @@ android:textSize="@dimen/two_line_secondary_text_size" /> - + app:iconTint="@color/text_color" + app:icon="@drawable/ic_content_copy" + app:iconGravity="textStart" /> - + app:icon="@drawable/ic_dots_vertical" + app:iconTint="@color/text_color" + app:iconGravity="textStart" /> diff --git a/app/src/main/res/layout/file_details_share_share_item.xml b/app/src/main/res/layout/file_details_share_share_item.xml index 5f3003c0d53c..3ee12388b13b 100644 --- a/app/src/main/res/layout/file_details_share_share_item.xml +++ b/app/src/main/res/layout/file_details_share_share_item.xml @@ -65,12 +65,16 @@ tools:text="@string/placeholder_view_only" /> - + app:icon="@drawable/ic_dots_vertical" + app:iconTint="@color/text_color" + app:iconGravity="textStart" /> From 5cef6930bed5855bec0159291e390db650189280 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 30 Jan 2026 16:52:03 +0100 Subject: [PATCH 29/83] make imageview material icon button Signed-off-by: alperozturk96 --- .../main/res/layout/file_details_share_link_share_item.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index 5625d31f2382..7663b67f341f 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -70,8 +70,9 @@ android:gravity="center_vertical" android:singleLine="true" tools:text="@string/placeholder_view_only" - android:drawablePadding="4dp" - app:drawableEndCompat="@drawable/ic_baseline_arrow_drop_down_24" app:drawableTint="@color/primary" + android:drawablePadding="@dimen/standard_quarter_margin" + app:drawableEndCompat="@drawable/ic_baseline_arrow_drop_down_24" + app:drawableTint="@color/primary" android:textColor="@color/primary" android:textSize="@dimen/two_line_secondary_text_size" /> From 474cd8043192f3cb0854dfdf333926562f0442fe Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 4 Feb 2026 15:30:28 +0100 Subject: [PATCH 30/83] color list item color Signed-off-by: alperozturk96 --- app/src/main/res/layout/grid_item.xml | 3 +++ app/src/main/res/layout/list_item.xml | 4 ++++ app/src/main/res/layout/recommended_file_item.xml | 1 + 3 files changed, 8 insertions(+) diff --git a/app/src/main/res/layout/grid_item.xml b/app/src/main/res/layout/grid_item.xml index ddbbc8c627c3..4ce95c7cb92c 100644 --- a/app/src/main/res/layout/grid_item.xml +++ b/app/src/main/res/layout/grid_item.xml @@ -139,6 +139,7 @@ android:src="@android:drawable/checkbox_off_background" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + app:tint="@color/text_color" tools:visibility="visible" /> @@ -229,6 +231,7 @@ app:layout_constraintTop_toBottomOf="@id/thumbnail" app:srcCompat="@drawable/ic_dots_vertical" tools:ignore="TouchTargetSizeCheck" + app:tint="@color/text_color" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index 8717f2ffc7aa..de2bacafc86a 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -246,6 +246,7 @@ android:paddingEnd="@dimen/list_item_share_right_margin" android:src="@drawable/ic_comment" android:visibility="gone" + app:tint="@color/text_color" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/recommended_file_item.xml b/app/src/main/res/layout/recommended_file_item.xml index 0d91762076a2..e8e5bbd6ebeb 100644 --- a/app/src/main/res/layout/recommended_file_item.xml +++ b/app/src/main/res/layout/recommended_file_item.xml @@ -199,6 +199,7 @@ android:id="@+id/more" android:layout_width="@dimen/grid_bottom_view_height" android:layout_height="@dimen/grid_bottom_view_height" + app:tint="@color/text_color" android:layout_marginHorizontal="@dimen/standard_half_margin" android:contentDescription="@string/overflow_menu" app:layout_constraintStart_toEndOf="@+id/filename_and_reason_layout" From ca5706280f6c3b59375ff8549ca4040456f38018 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 5 Feb 2026 10:51:42 +0100 Subject: [PATCH 31/83] revert colors Signed-off-by: alperozturk96 --- .../main/res/layout/file_details_share_link_share_item.xml | 4 ++-- app/src/main/res/layout/file_details_share_share_item.xml | 2 +- app/src/main/res/layout/grid_item.xml | 3 --- app/src/main/res/layout/list_item.xml | 4 ---- app/src/main/res/layout/recommended_file_item.xml | 1 - 5 files changed, 3 insertions(+), 11 deletions(-) diff --git a/app/src/main/res/layout/file_details_share_link_share_item.xml b/app/src/main/res/layout/file_details_share_link_share_item.xml index 7663b67f341f..4e063e1a1ad2 100644 --- a/app/src/main/res/layout/file_details_share_link_share_item.xml +++ b/app/src/main/res/layout/file_details_share_link_share_item.xml @@ -89,7 +89,7 @@ android:paddingEnd="@dimen/standard_padding" android:paddingBottom="@dimen/standard_quarter_margin" android:scaleType="fitCenter" - app:iconTint="@color/text_color" + app:iconTint="@color/secondary_text_color" app:icon="@drawable/ic_content_copy" app:iconGravity="textStart" /> @@ -103,7 +103,7 @@ android:paddingStart="@dimen/standard_padding" android:paddingEnd="@dimen/standard_padding" app:icon="@drawable/ic_dots_vertical" - app:iconTint="@color/text_color" + app:iconTint="@color/secondary_text_color" app:iconGravity="textStart" /> diff --git a/app/src/main/res/layout/file_details_share_share_item.xml b/app/src/main/res/layout/file_details_share_share_item.xml index 3ee12388b13b..0ae9e82c0588 100644 --- a/app/src/main/res/layout/file_details_share_share_item.xml +++ b/app/src/main/res/layout/file_details_share_share_item.xml @@ -75,6 +75,6 @@ android:paddingStart="@dimen/standard_padding" android:paddingEnd="@dimen/standard_padding" app:icon="@drawable/ic_dots_vertical" - app:iconTint="@color/text_color" + app:iconTint="@color/secondary_text_color" app:iconGravity="textStart" /> diff --git a/app/src/main/res/layout/grid_item.xml b/app/src/main/res/layout/grid_item.xml index 4ce95c7cb92c..ddbbc8c627c3 100644 --- a/app/src/main/res/layout/grid_item.xml +++ b/app/src/main/res/layout/grid_item.xml @@ -139,7 +139,6 @@ android:src="@android:drawable/checkbox_off_background" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:tint="@color/text_color" tools:visibility="visible" /> @@ -231,7 +229,6 @@ app:layout_constraintTop_toBottomOf="@id/thumbnail" app:srcCompat="@drawable/ic_dots_vertical" tools:ignore="TouchTargetSizeCheck" - app:tint="@color/text_color" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/list_item.xml b/app/src/main/res/layout/list_item.xml index de2bacafc86a..8717f2ffc7aa 100644 --- a/app/src/main/res/layout/list_item.xml +++ b/app/src/main/res/layout/list_item.xml @@ -246,7 +246,6 @@ android:paddingEnd="@dimen/list_item_share_right_margin" android:src="@drawable/ic_comment" android:visibility="gone" - app:tint="@color/text_color" tools:visibility="visible" /> diff --git a/app/src/main/res/layout/recommended_file_item.xml b/app/src/main/res/layout/recommended_file_item.xml index e8e5bbd6ebeb..0d91762076a2 100644 --- a/app/src/main/res/layout/recommended_file_item.xml +++ b/app/src/main/res/layout/recommended_file_item.xml @@ -199,7 +199,6 @@ android:id="@+id/more" android:layout_width="@dimen/grid_bottom_view_height" android:layout_height="@dimen/grid_bottom_view_height" - app:tint="@color/text_color" android:layout_marginHorizontal="@dimen/standard_half_margin" android:contentDescription="@string/overflow_menu" app:layout_constraintStart_toEndOf="@+id/filename_and_reason_layout" From 85ea1059124fb77b92dd992cb3f7fc4058d0038f Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 26 Jan 2026 09:29:57 +0100 Subject: [PATCH 32/83] fix(auto-upload): handle uploaded files Signed-off-by: alperozturk96 --- .../jobs/autoUpload/AutoUploadEntityResult.kt | 18 +++++++++++ .../jobs/autoUpload/AutoUploadWorker.kt | 30 +++++++++++-------- 2 files changed, 35 insertions(+), 13 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt new file mode 100644 index 000000000000..41610b7f5617 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt @@ -0,0 +1,18 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.autoUpload + +import com.nextcloud.client.database.entity.UploadEntity +import com.owncloud.android.db.OCUpload + +sealed class AutoUploadEntityResult { + data object SyncConflict : AutoUploadEntityResult() + data object CreationError : AutoUploadEntityResult() + data object Uploaded : AutoUploadEntityResult() + data class Success(val data: Pair) : AutoUploadEntityResult() +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index ac0aa3769d39..2faa30fa8024 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -17,7 +17,6 @@ import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager -import com.nextcloud.client.database.entity.UploadEntity import com.nextcloud.client.database.entity.toOCUpload import com.nextcloud.client.database.entity.toUploadEntity import com.nextcloud.client.device.PowerManagementService @@ -37,6 +36,7 @@ import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.db.OCUpload import com.owncloud.android.db.UploadResult +import com.owncloud.android.files.services.NameCollisionPolicy import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.lib.common.OwnCloudClientManagerFactory import com.owncloud.android.lib.common.operations.RemoteOperationResult @@ -309,14 +309,14 @@ class AutoUploadWorker( ) try { - val result = createEntityAndUpload(user, localPath, remotePath) - if (result == null) { + val entityResult = getEntityResult(user, localPath, remotePath) + if (entityResult !is AutoUploadEntityResult.Success) { repository.markFileAsHandled(localPath, syncedFolder) - Log_OC.d(TAG, "Marked file as handled due to existing conflict: $localPath") + Log_OC.d(TAG, "marked file as handled: $localPath") continue } - var (uploadEntity, upload) = result + var (uploadEntity, upload) = entityResult.data // if local file deleted, upload process cannot be started or retriable thus needs to be removed if (path.isEmpty() || !file.exists()) { @@ -403,11 +403,7 @@ class AutoUploadWorker( } @Suppress("ReturnCount") - private fun createEntityAndUpload( - user: User, - localPath: String, - remotePath: String - ): Pair? { + private fun getEntityResult(user: User, localPath: String, remotePath: String): AutoUploadEntityResult { val (needsCharging, needsWifi, uploadAction) = getUploadSettings(syncedFolder) Log_OC.d(TAG, "creating oc upload for ${user.accountName}") @@ -421,14 +417,22 @@ class AutoUploadWorker( val lastUploadResult = uploadEntity?.lastResult?.let { UploadResult.fromValue(it) } if (lastUploadResult == UploadResult.SYNC_CONFLICT) { Log_OC.w(TAG, "Conflict already exists, skipping auto-upload: $localPath") - return null + return AutoUploadEntityResult.SyncConflict } val upload = try { uploadEntity?.toOCUpload(null) ?: OCUpload(localPath, remotePath, user.accountName) } catch (_: IllegalArgumentException) { Log_OC.e(TAG, "cannot construct oc upload") - return null + return AutoUploadEntityResult.CreationError + } + + // only valid for skip collision policy other scenarios will be handled in UploadFileOperation.java + if (upload.lastResult == UploadResult.UPLOADED && + syncedFolder.nameCollisionPolicy == NameCollisionPolicy.SKIP + ) { + Log_OC.d(TAG, "no need to create and process this entity file is already uploaded") + return AutoUploadEntityResult.Uploaded } upload.apply { @@ -445,7 +449,7 @@ class AutoUploadWorker( } } - return upload.toUploadEntity() to upload + return AutoUploadEntityResult.Success(upload.toUploadEntity() to upload) } private fun createUploadFileOperation(upload: OCUpload, user: User): UploadFileOperation = UploadFileOperation( From 6bffcc761cbd84b092cba7d4c43e9e462738b30d Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 26 Jan 2026 14:10:07 +0100 Subject: [PATCH 33/83] fix(auto-upload): handle upload results Signed-off-by: alperozturk96 --- .../jobs/autoUpload/AutoUploadEntityResult.kt | 2 +- .../jobs/autoUpload/AutoUploadWorker.kt | 11 +++++-- .../RemoteOperationResultExtensions.kt | 20 ------------ .../extensions/UploadResultExtensions.kt | 31 +++++++++++++++++++ 4 files changed, 40 insertions(+), 24 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt index 41610b7f5617..99805cea1177 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt @@ -11,7 +11,7 @@ import com.nextcloud.client.database.entity.UploadEntity import com.owncloud.android.db.OCUpload sealed class AutoUploadEntityResult { - data object SyncConflict : AutoUploadEntityResult() + data object PermanentFailure : AutoUploadEntityResult() data object CreationError : AutoUploadEntityResult() data object Uploaded : AutoUploadEntityResult() data class Success(val data: Pair) : AutoUploadEntityResult() diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 2faa30fa8024..5389ab1b4727 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -26,6 +26,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.SubFolderRule +import com.nextcloud.utils.extensions.isPermanentFailure import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl @@ -415,9 +416,13 @@ class AutoUploadWorker( ) val lastUploadResult = uploadEntity?.lastResult?.let { UploadResult.fromValue(it) } - if (lastUploadResult == UploadResult.SYNC_CONFLICT) { - Log_OC.w(TAG, "Conflict already exists, skipping auto-upload: $localPath") - return AutoUploadEntityResult.SyncConflict + if (lastUploadResult?.isPermanentFailure() == true) { + Log_OC.w( + TAG, + "last upload failed with permanent failure, skipping auto-upload: $localPath," + + " failure: ${lastUploadResult.value}" + ) + return AutoUploadEntityResult.PermanentFailure } val upload = try { diff --git a/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt index caf0ad8b99f8..3a9f73c81f5f 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/RemoteOperationResultExtensions.kt @@ -9,13 +9,10 @@ package com.nextcloud.utils.extensions import com.owncloud.android.MainApp import com.owncloud.android.R -import com.owncloud.android.datamodel.OCFile import com.owncloud.android.lib.common.operations.RemoteOperation import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode -import com.owncloud.android.lib.resources.files.model.RemoteFile import com.owncloud.android.utils.ErrorMessageAdapter -import com.owncloud.android.utils.FileStorageUtils @Suppress("ReturnCount") fun Pair?, RemoteOperation<*>?>?.getErrorMessage(): String { @@ -45,20 +42,3 @@ fun ResultCode.isFileSpecificError(): Boolean { return !errorCodes.contains(this) } - -@Suppress("Deprecation") -fun RemoteOperationResult<*>?.toOCFile(): List? = if (this?.isSuccess == true) { - data?.toOCFileList() -} else { - null -} - -private fun ArrayList.toOCFileList(): List = this.mapNotNull { - val remoteFile = (it as? RemoteFile) - - remoteFile?.let { - remoteFile.toOCFile() - } -} - -private fun RemoteFile?.toOCFile(): OCFile = FileStorageUtils.fillOCFile(this) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt new file mode 100644 index 000000000000..a93892c0ce7a --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt @@ -0,0 +1,31 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.owncloud.android.db.UploadResult + +fun UploadResult.isPermanentFailure(): Boolean = when (this) { + UploadResult.FILE_NOT_FOUND, + UploadResult.FILE_ERROR, + UploadResult.FOLDER_ERROR, + UploadResult.CANNOT_CREATE_FILE, + UploadResult.SYNC_CONFLICT, + UploadResult.LOCAL_STORAGE_NOT_COPIED, + UploadResult.VIRUS_DETECTED, + UploadResult.QUOTA_EXCEEDED, + UploadResult.SAME_FILE_CONFLICT, + UploadResult.PRIVILEGES_ERROR, + UploadResult.CREDENTIAL_ERROR, + UploadResult.UNKNOWN, + + // user's choice + UploadResult.CANCELLED -> true + + // Everything else may succeed after retry + else -> false +} From be7dee86b709f17f6e3f5ab32f8a4b77568830d5 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 26 Jan 2026 14:47:44 +0100 Subject: [PATCH 34/83] fix(auto-upload): handle upload results Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt | 7 +++---- .../nextcloud/utils/extensions/UploadResultExtensions.kt | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 5389ab1b4727..6696b39a420f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -26,7 +26,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.SubFolderRule -import com.nextcloud.utils.extensions.isPermanentFailure +import com.nextcloud.utils.extensions.isNonRetryable import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl @@ -416,11 +416,10 @@ class AutoUploadWorker( ) val lastUploadResult = uploadEntity?.lastResult?.let { UploadResult.fromValue(it) } - if (lastUploadResult?.isPermanentFailure() == true) { + if (lastUploadResult?.isNonRetryable() == true) { Log_OC.w( TAG, - "last upload failed with permanent failure, skipping auto-upload: $localPath," + - " failure: ${lastUploadResult.value}" + "last upload failed with ${lastUploadResult.value}, skipping auto-upload: $localPath" ) return AutoUploadEntityResult.PermanentFailure } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt index a93892c0ce7a..510c45098502 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt @@ -9,7 +9,7 @@ package com.nextcloud.utils.extensions import com.owncloud.android.db.UploadResult -fun UploadResult.isPermanentFailure(): Boolean = when (this) { +fun UploadResult.isNonRetryable(): Boolean = when (this) { UploadResult.FILE_NOT_FOUND, UploadResult.FILE_ERROR, UploadResult.FOLDER_ERROR, @@ -26,6 +26,6 @@ fun UploadResult.isPermanentFailure(): Boolean = when (this) { // user's choice UploadResult.CANCELLED -> true - // Everything else may succeed after retry + // everything else may succeed after retry else -> false } From 035e9bf22808f14b19b20f1908168a33cd3eef76 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 26 Jan 2026 14:48:12 +0100 Subject: [PATCH 35/83] fix(auto-upload): handle upload results Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt | 2 +- .../com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt index 99805cea1177..bd6b8a208d5c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadEntityResult.kt @@ -11,7 +11,7 @@ import com.nextcloud.client.database.entity.UploadEntity import com.owncloud.android.db.OCUpload sealed class AutoUploadEntityResult { - data object PermanentFailure : AutoUploadEntityResult() + data object NonRetryable : AutoUploadEntityResult() data object CreationError : AutoUploadEntityResult() data object Uploaded : AutoUploadEntityResult() data class Success(val data: Pair) : AutoUploadEntityResult() diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 6696b39a420f..b6a407cd2fd4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -421,7 +421,7 @@ class AutoUploadWorker( TAG, "last upload failed with ${lastUploadResult.value}, skipping auto-upload: $localPath" ) - return AutoUploadEntityResult.PermanentFailure + return AutoUploadEntityResult.NonRetryable } val upload = try { From 0436d220161c7490a13aea542778325a0f65b439 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Mon, 26 Jan 2026 14:51:04 +0100 Subject: [PATCH 36/83] fix(auto-upload): handle upload results Signed-off-by: alperozturk96 --- .../com/nextcloud/utils/extensions/UploadResultExtensions.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt index 510c45098502..9c1f1218e57a 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/UploadResultExtensions.kt @@ -21,6 +21,9 @@ fun UploadResult.isNonRetryable(): Boolean = when (this) { UploadResult.SAME_FILE_CONFLICT, UploadResult.PRIVILEGES_ERROR, UploadResult.CREDENTIAL_ERROR, + + // most cases covered and mapped from RemoteOperationResult. Most likely UploadResult.UNKNOWN this error will + // occur again UploadResult.UNKNOWN, // user's choice From 1d9602b171993698782c18692c4d6e93d7157910 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Jan 2026 12:32:40 +0100 Subject: [PATCH 37/83] Rename .java to .kt Signed-off-by: alperozturk96 --- .../android/utils/{FilesSyncHelper.java => FilesSyncHelper.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/owncloud/android/utils/{FilesSyncHelper.java => FilesSyncHelper.kt} (100%) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.kt similarity index 100% rename from app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java rename to app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.kt From 5c18e7e2650b10433ba7d279a19ce264e8fb621a Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Jan 2026 12:32:41 +0100 Subject: [PATCH 38/83] fix(auto-upload): calls Signed-off-by: alperozturk96 --- .../client/jobs/BackgroundJobManager.kt | 4 +- .../client/jobs/BackgroundJobManagerImpl.kt | 40 +------- .../client/jobs/ContentObserverWork.kt | 55 ++--------- .../jobs/autoUpload/AutoUploadWorker.kt | 11 +-- .../notification/WorkerNotificationManager.kt | 11 +++ .../java/com/owncloud/android/MainApp.java | 34 ++++--- .../files/BootupBroadcastReceiver.java | 8 +- .../ui/activity/SyncedFoldersActivity.kt | 6 +- .../owncloud/android/utils/FilesSyncHelper.kt | 99 +++++++------------ 9 files changed, 90 insertions(+), 178 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt index abde68ad4040..0dfef42aba27 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManager.kt @@ -120,9 +120,7 @@ interface BackgroundJobManager { fun startImmediateFilesExportJob(files: Collection): LiveData - fun schedulePeriodicFilesSyncJob(syncedFolder: SyncedFolder) - - fun startAutoUploadImmediately( + fun startAutoUpload( syncedFolder: SyncedFolder, overridePowerSaving: Boolean = false, contentUris: Array = arrayOf() diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index 0ad01e66c7ad..2a8d94c11e99 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -278,11 +278,11 @@ internal class BackgroundJobManagerImpl( .setTriggerContentMaxDelay(MAX_CONTENT_TRIGGER_DELAY_MS, TimeUnit.MILLISECONDS) .build() - val request = oneTimeRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER) + val request = periodicRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER) .setConstraints(constrains) .build() - workManager.enqueueUniqueWork(JOB_CONTENT_OBSERVER, ExistingWorkPolicy.REPLACE, request) + workManager.enqueueUniquePeriodicWork(JOB_CONTENT_OBSERVER, ExistingPeriodicWorkPolicy.KEEP, request) } override fun schedulePeriodicContactsBackup(user: User) { @@ -477,40 +477,7 @@ internal class BackgroundJobManagerImpl( ) } - override fun schedulePeriodicFilesSyncJob(syncedFolder: SyncedFolder) { - val syncedFolderID = syncedFolder.id - - val arguments = Data.Builder() - .putLong(AutoUploadWorker.SYNCED_FOLDER_ID, syncedFolderID) - .build() - - val constraints = Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .setRequiresCharging(syncedFolder.isChargingOnly) - .build() - - val request = periodicRequestBuilder( - jobClass = AutoUploadWorker::class, - jobName = JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID, - intervalMins = DEFAULT_PERIODIC_JOB_INTERVAL_MINUTES, - constraints = constraints - ) - .setBackoffCriteria( - BackoffPolicy.LINEAR, - DEFAULT_BACKOFF_CRITERIA_DELAY_SEC, - TimeUnit.SECONDS - ) - .setInputData(arguments) - .build() - - workManager.enqueueUniquePeriodicWork( - JOB_PERIODIC_FILES_SYNC + "_" + syncedFolderID, - ExistingPeriodicWorkPolicy.KEEP, - request - ) - } - - override fun startAutoUploadImmediately( + override fun startAutoUpload( syncedFolder: SyncedFolder, overridePowerSaving: Boolean, contentUris: Array @@ -533,6 +500,7 @@ internal class BackgroundJobManagerImpl( jobName = JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID ) .setInputData(arguments) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) .setBackoffCriteria( BackoffPolicy.LINEAR, diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index 6d421d23a9d4..ea03c5806b67 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -7,18 +7,12 @@ */ package com.nextcloud.client.jobs -import android.app.Notification import android.content.Context -import androidx.core.app.NotificationCompat import androidx.work.CoroutineWorker import androidx.work.WorkerParameters import com.nextcloud.client.device.PowerManagementService -import com.nextcloud.utils.ForegroundServiceHelper -import com.owncloud.android.R -import com.owncloud.android.datamodel.ForegroundServiceType import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.lib.common.utils.Log_OC -import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FilesSyncHelper import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -26,13 +20,12 @@ import kotlinx.coroutines.withContext /** * This work is triggered when OS detects change in media folders. * - * It fires media detection job and sync job and finishes immediately. + * It fires media detection worker and auto upload worker and finishes immediately. * - * This job must not be started on API < 24. */ @Suppress("TooGenericExceptionCaught") class ContentObserverWork( - private val context: Context, + context: Context, private val params: WorkerParameters, private val syncedFolderProvider: SyncedFolderProvider, private val powerManagementService: PowerManagementService, @@ -41,8 +34,6 @@ class ContentObserverWork( companion object { private const val TAG = "🔍" + "ContentObserverWork" - private const val CHANNEL_ID = NotificationUtils.NOTIFICATION_CHANNEL_CONTENT_OBSERVER - private const val NOTIFICATION_ID = 774 } override suspend fun doWork(): Result = withContext(Dispatchers.IO) { @@ -53,10 +44,6 @@ class ContentObserverWork( try { if (params.triggeredContentUris.isNotEmpty()) { Log_OC.d(TAG, "📸 content observer detected file changes.") - - val notificationTitle = context.getString(R.string.content_observer_work_notification_title) - val notification = createNotification(notificationTitle) - updateForegroundInfo(notification) checkAndTriggerAutoUpload() // prevent worker fail because of another worker @@ -69,8 +56,6 @@ class ContentObserverWork( Log_OC.d(TAG, "⚠️ triggeredContentUris is empty — nothing to sync.") } - rescheduleSelf() - val result = Result.success() backgroundJobManager.logEndOfWorker(workerName, result) Log_OC.d(TAG, "finished") @@ -81,34 +66,6 @@ class ContentObserverWork( } } - private suspend fun updateForegroundInfo(notification: Notification) { - val foregroundInfo = ForegroundServiceHelper.createWorkerForegroundInfo( - NOTIFICATION_ID, - notification, - ForegroundServiceType.DataSync - ) - setForeground(foregroundInfo) - } - - private fun createNotification(title: String): Notification = NotificationCompat.Builder(context, CHANNEL_ID) - .setContentTitle(title) - .setSmallIcon(R.drawable.ic_find_in_page) - .setOngoing(true) - .setSound(null) - .setVibrate(null) - .setOnlyAlertOnce(true) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setSilent(true) - .build() - - /** - * Re-schedules this observer to ensure continuous monitoring of media changes. - */ - private fun rescheduleSelf() { - Log_OC.d(TAG, "🔁 Rescheduling ContentObserverWork for continued observation.") - backgroundJobManager.scheduleContentObserverJob() - } - private suspend fun checkAndTriggerAutoUpload() = withContext(Dispatchers.IO) { if (powerManagementService.isPowerSavingEnabled) { Log_OC.w(TAG, "⚡ Power saving mode active — skipping file sync.") @@ -124,15 +81,15 @@ class ContentObserverWork( val contentUris = params.triggeredContentUris.map { uri -> // adds uri strings e.g. content://media/external/images/media/2281 uri.toString() - }.toTypedArray() + }.toTypedArray() Log_OC.d(TAG, "📄 Content uris detected") try { - FilesSyncHelper.startAutoUploadImmediatelyWithContentUris( + FilesSyncHelper.startAutoUploadForEnabledSyncedFolders( syncedFolderProvider, backgroundJobManager, - false, - contentUris + contentUris, + false ) Log_OC.d(TAG, "✅ auto upload triggered successfully for ${contentUris.size} file(s).") } catch (e: Exception) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index b6a407cd2fd4..af38b417a2b5 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -191,11 +191,6 @@ class AutoUploadWorker( val currentTime = System.currentTimeMillis() val passedScanInterval = totalScanInterval <= currentTime - Log_OC.d(TAG, "lastScanTimestampMs: " + syncedFolder.lastScanTimestampMs) - Log_OC.d(TAG, "totalScanInterval: $totalScanInterval") - Log_OC.d(TAG, "currentTime: $currentTime") - Log_OC.d(TAG, "passedScanInterval: $passedScanInterval") - if (!passedScanInterval && contentUris.isNullOrEmpty() && !overridePowerSaving) { Log_OC.w( TAG, @@ -204,6 +199,8 @@ class AutoUploadWorker( return true } + Log_OC.d(TAG, "starting ...") + return false } @@ -213,6 +210,8 @@ class AutoUploadWorker( */ @Suppress("MagicNumber", "TooGenericExceptionCaught") private suspend fun collectFileChangesFromContentObserverWork(contentUris: Array?) = try { + Log_OC.d(TAG, "collecting file changes") + withContext(Dispatchers.IO) { if (contentUris.isNullOrEmpty()) { helper.insertEntries(syncedFolder, repository) @@ -295,7 +294,7 @@ class AutoUploadWorker( Log_OC.w(TAG, "no more files to upload at lastId: $lastId") break } - Log_OC.d(TAG, "Processing batch: lastId=$lastId, count=${filePathsWithIds.size}") + Log_OC.d(TAG, "started, processing batch: lastId=$lastId, count=${filePathsWithIds.size}") filePathsWithIds.forEachIndexed { batchIndex, (path, id) -> val file = File(path) diff --git a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt index 195a11b72a34..daf5614faf55 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt @@ -89,4 +89,15 @@ open class WorkerNotificationManager( notification, ForegroundServiceType.DataSync ) + + fun createNotification(title: String, iconId: Int): Notification = notificationBuilder + .setContentTitle(title) + .setSmallIcon(iconId) + .setOngoing(true) + .setSound(null) + .setVibrate(null) + .setOnlyAlertOnce(true) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setSilent(true) + .build() } diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index 5f564a23ac5a..7fdbf8a49e51 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -125,8 +125,8 @@ /** - * Main Application of the project. - * Contains methods to build the "static" strings. These strings were before constants in different classes. + * Main Application of the project. Contains methods to build the "static" strings. These strings were before constants + * in different classes. */ public class MainApp extends Application implements HasAndroidInjector, NetworkChangeListener { public static final OwnCloudVersion OUTDATED_SERVER_VERSION = NextcloudVersion.nextcloud_30; @@ -142,7 +142,7 @@ public class MainApp extends Application implements HasAndroidInjector, NetworkC private static boolean mOnlyOnDevice; private static boolean mOnlyPersonalFiles; - + @Inject protected AppPreferences preferences; @@ -338,6 +338,10 @@ public void onCreate() { } catch (Exception e) { Log_OC.d("Debug", "Failed to disable uri exposure"); } + + Log_OC.d(TAG, "scheduleContentObserverJob, called"); + backgroundJobManager.scheduleContentObserverJob(); + initSyncOperations(this, preferences, uploadsStorageManager, @@ -347,8 +351,7 @@ public void onCreate() { backgroundJobManager, clock, viewThemeUtils, - walledCheckCache, - syncedFolderProvider); + walledCheckCache); initContactsBackup(accountManager, backgroundJobManager); notificationChannels(); @@ -371,9 +374,9 @@ public void onCreate() { if (!MDMConfig.INSTANCE.sendFilesSupport(this)) { disableDocumentsStorageProvider(); } - - - } + + + } public void disableDocumentsStorageProvider() { String packageName = getPackageName(); @@ -386,6 +389,10 @@ public void disableDocumentsStorageProvider() { private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { if (event == Lifecycle.Event.ON_START) { Log_OC.d(TAG, "APP IN FOREGROUND"); + FilesSyncHelper.startAutoUploadForEnabledSyncedFolders(syncedFolderProvider, + backgroundJobManager, + new String[]{}, + true); } else if (event == Lifecycle.Event.ON_STOP) { passCodeManager.setCanAskPin(true); Log_OC.d(TAG, "APP IN BACKGROUND"); @@ -403,7 +410,7 @@ private void setProxyForNonBrandedPlusClients() { Log_OC.d(TAG, "Error caught at setProxyForNonBrandedPlusClients: " + e); } } - + public static boolean isClientBranded() { return getAppContext().getResources().getBoolean(R.bool.is_branded_client); } @@ -415,7 +422,8 @@ public static boolean isClientBrandedPlus() { private final IntentFilter restrictionsFilter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED); private final BroadcastReceiver restrictionsReceiver = new BroadcastReceiver() { - @Override public void onReceive(Context context, Intent intent) { + @Override + public void onReceive(Context context, Intent intent) { setProxyConfig(); } }; @@ -605,8 +613,7 @@ public static void initSyncOperations( final BackgroundJobManager backgroundJobManager, final Clock clock, final ViewThemeUtils viewThemeUtils, - final WalledCheckCache walledCheckCache, - final SyncedFolderProvider syncedFolderProvider) { + final WalledCheckCache walledCheckCache) { updateToAutoUpload(context); cleanOldEntries(clock); updateAutoUploadEntries(clock); @@ -620,11 +627,9 @@ public static void initSyncOperations( } if (!preferences.isAutoUploadInitialized()) { - FilesSyncHelper.startAutoUploadImmediately(syncedFolderProvider, backgroundJobManager, false); preferences.setAutoUploadInit(true); } - FilesSyncHelper.scheduleFilesSyncForAllFoldersIfNeeded(appContext.get(), syncedFolderProvider, backgroundJobManager); FilesSyncHelper.restartUploadsIfNeeded( uploadsStorageManager, accountManager, @@ -857,7 +862,6 @@ private static void updateToAutoUpload(Context context) { } } - private static void showAutoUploadAlertDialog(Context context) { new MaterialAlertDialogBuilder(context, R.style.Theme_ownCloud_Dialog) diff --git a/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java b/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java index 0e367ca7665e..8eb62f17a9c9 100644 --- a/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java +++ b/app/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java @@ -23,7 +23,6 @@ import com.nextcloud.client.network.WalledCheckCache; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; -import com.owncloud.android.datamodel.SyncedFolderProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.utils.theme.ViewThemeUtils; @@ -49,7 +48,6 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { @Inject Clock clock; @Inject ViewThemeUtils viewThemeUtils; @Inject WalledCheckCache walledCheckCache; - @Inject SyncedFolderProvider syncedFolderProvider; /** * Receives broadcast intent reporting that the system was just boot up. * @@ -71,9 +69,9 @@ public void onReceive(Context context, Intent intent) { backgroundJobManager, clock, viewThemeUtils, - walledCheckCache, - syncedFolderProvider - ); + walledCheckCache); + Log_OC.d(TAG, "scheduleContentObserverJob, called"); + backgroundJobManager.scheduleContentObserverJob(); MainApp.initContactsBackup(accountManager, backgroundJobManager); } else { Log_OC.d(TAG, "Getting wrong intent: " + intent.getAction()); diff --git a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt index c12acc61e2c5..d361e128f4c0 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.kt @@ -580,7 +580,7 @@ class SyncedFoldersActivity : } } if (syncedFolderDisplayItem.isEnabled) { - backgroundJobManager.startAutoUploadImmediately(syncedFolderDisplayItem, overridePowerSaving = false) + backgroundJobManager.startAutoUpload(syncedFolderDisplayItem, overridePowerSaving = false) showBatteryOptimizationDialogIfNeeded() } } @@ -743,7 +743,7 @@ class SyncedFoldersActivity : // existing synced folder setup to be updated syncedFolderProvider.updateSyncFolder(item) if (item.isEnabled) { - backgroundJobManager.startAutoUploadImmediately(item, overridePowerSaving = false) + backgroundJobManager.startAutoUpload(item, overridePowerSaving = false) } else { val syncedFolderInitiatedKey = KEY_SYNCED_FOLDER_INITIATED_PREFIX + item.id val arbitraryDataProvider = @@ -760,7 +760,7 @@ class SyncedFoldersActivity : if (storedId != -1L) { item.id = storedId if (item.isEnabled) { - backgroundJobManager.startAutoUploadImmediately(item, overridePowerSaving = false) + backgroundJobManager.startAutoUpload(item, overridePowerSaving = false) } else { val syncedFolderInitiatedKey = KEY_SYNCED_FOLDER_INITIATED_PREFIX + item.id arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey) diff --git a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.kt b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.kt index 60ac53201200..8a2e12274acd 100644 --- a/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.kt +++ b/app/src/main/java/com/owncloud/android/utils/FilesSyncHelper.kt @@ -7,72 +7,49 @@ * SPDX-FileCopyrightText: 2017 Nextcloud GmbH * SPDX-License-Identifier: AGPL-3.0-or-later OR GPL-2.0-only */ -package com.owncloud.android.utils; - -import android.content.Context; - -import com.nextcloud.client.account.UserAccountManager; -import com.nextcloud.client.device.PowerManagementService; -import com.nextcloud.client.jobs.BackgroundJobManager; -import com.nextcloud.client.jobs.upload.FileUploadHelper; -import com.nextcloud.client.network.ConnectivityService; -import com.owncloud.android.datamodel.SyncedFolder; -import com.owncloud.android.datamodel.SyncedFolderProvider; -import com.owncloud.android.datamodel.UploadsStorageManager; -import com.owncloud.android.lib.common.utils.Log_OC; - -/** - * Various utilities that make auto upload tick - */ -public final class FilesSyncHelper { - public static final String TAG = "FileSyncHelper"; - - public static final String GLOBAL = "global"; - - private FilesSyncHelper() { - // utility class -> private constructor - } - - public static void restartUploadsIfNeeded(final UploadsStorageManager uploadsStorageManager, - final UserAccountManager accountManager, - final ConnectivityService connectivityService, - final PowerManagementService powerManagementService) { - Log_OC.d(TAG, "restartUploadsIfNeeded, called"); - FileUploadHelper.Companion.instance().retryFailedUploads( +package com.owncloud.android.utils + +import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.device.PowerManagementService +import com.nextcloud.client.jobs.BackgroundJobManager +import com.nextcloud.client.jobs.upload.FileUploadHelper.Companion.instance +import com.nextcloud.client.network.ConnectivityService +import com.owncloud.android.datamodel.SyncedFolderProvider +import com.owncloud.android.datamodel.UploadsStorageManager +import com.owncloud.android.lib.common.utils.Log_OC + +object FilesSyncHelper { + private const val TAG: String = "FileSyncHelper" + const val GLOBAL: String = "global" + + @JvmStatic + fun restartUploadsIfNeeded( + uploadsStorageManager: UploadsStorageManager, + accountManager: UserAccountManager, + connectivityService: ConnectivityService, + powerManagementService: PowerManagementService + ) { + Log_OC.d(TAG, "restartUploadsIfNeeded, called") + instance().retryFailedUploads( uploadsStorageManager, connectivityService, accountManager, - powerManagementService); - } - - public static void scheduleFilesSyncForAllFoldersIfNeeded(Context context, SyncedFolderProvider syncedFolderProvider, BackgroundJobManager jobManager) { - Log_OC.d(TAG, "scheduleFilesSyncForAllFoldersIfNeeded, called"); - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled()) { - jobManager.schedulePeriodicFilesSyncJob(syncedFolder); - } - } - if (context != null) { - jobManager.scheduleContentObserverJob(); - } else { - Log_OC.w(TAG, "cant scheduleContentObserverJob, context is null"); - } - } - - public static void startAutoUploadImmediatelyWithContentUris(SyncedFolderProvider syncedFolderProvider, BackgroundJobManager jobManager, boolean overridePowerSaving, String[] contentUris) { - Log_OC.d(TAG, "startAutoUploadImmediatelyWithContentUris"); - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled()) { - jobManager.startAutoUploadImmediately(syncedFolder, overridePowerSaving, contentUris); - } - } + powerManagementService + ) } - public static void startAutoUploadImmediately(SyncedFolderProvider syncedFolderProvider, BackgroundJobManager jobManager, boolean overridePowerSaving) { - Log_OC.d(TAG, "startAutoUploadImmediately"); - for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled()) { - jobManager.startAutoUploadImmediately(syncedFolder, overridePowerSaving, new String[]{}); + @JvmStatic + fun startAutoUploadForEnabledSyncedFolders( + provider: SyncedFolderProvider, + manager: BackgroundJobManager, + uris: Array, + overridePowerSaving: Boolean + ) { + Log_OC.d(TAG, "start auto upload worker for each enabled folder") + + provider.syncedFolders.forEach { + if (it.isEnabled) { + manager.startAutoUpload(it, overridePowerSaving, uris) } } } From 884fd5e95dc10769cf7e4b1e79c9357f389f7e3a Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Jan 2026 13:13:04 +0100 Subject: [PATCH 39/83] fix(auto-upload): calls Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/BackgroundJobManagerImpl.kt | 3 --- .../client/jobs/autoUpload/AutoUploadHelper.kt | 10 ---------- 2 files changed, 13 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index 2a8d94c11e99..e8d2a79faffa 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -101,8 +101,6 @@ internal class BackgroundJobManagerImpl( const val JOB_TEST = "test_job" - const val MAX_CONTENT_TRIGGER_DELAY_MS = 10000L - const val TAG_PREFIX_NAME = "name" const val TAG_PREFIX_USER = "user" const val TAG_PREFIX_CLASS = "class" @@ -275,7 +273,6 @@ internal class BackgroundJobManagerImpl( .addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true) .addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true) .addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true) - .setTriggerContentMaxDelay(MAX_CONTENT_TRIGGER_DELAY_MS, TimeUnit.MILLISECONDS) .build() val request = periodicRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt index 15b23e20fbe7..fc1b9d7be6e9 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadHelper.kt @@ -31,16 +31,6 @@ class AutoUploadHelper { } fun insertEntries(folder: SyncedFolder, repository: FileSystemRepository) { - val enabledTimestampMs = folder.enabledTimestampMs - if (!folder.isEnabled || (!folder.isExisting && enabledTimestampMs < 0)) { - Log_OC.w( - TAG, - "Skipping insertDBEntries: enabled=${folder.isEnabled}, " + - "exists=${folder.isExisting}, enabledTs=$enabledTimestampMs" - ) - return - } - when (folder.type) { MediaFolderType.IMAGE -> { repository.insertFromUri(MediaStore.Images.Media.INTERNAL_CONTENT_URI, folder) From 366513c9a4fa99fe3b6803e3a6cf7178faacb2c2 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Jan 2026 13:37:59 +0100 Subject: [PATCH 40/83] fix(auto-upload): calls Signed-off-by: alperozturk96 --- .../com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt | 8 ++++++-- .../java/com/nextcloud/client/jobs/ContentObserverWork.kt | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index e8d2a79faffa..46ecb890017c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -43,6 +43,7 @@ import com.owncloud.android.operations.DownloadType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import java.time.Duration import java.util.Date import java.util.UUID import java.util.concurrent.TimeUnit @@ -267,19 +268,22 @@ internal class BackgroundJobManagerImpl( return workInfo.map { it -> it.map { fromWorkInfo(it) ?: JobInfo() }.sortedBy { it.started }.reversed() } } + @Suppress("MagicNumber") override fun scheduleContentObserverJob() { val constrains = Constraints.Builder() .addContentUriTrigger(MediaStore.Images.Media.INTERNAL_CONTENT_URI, true) .addContentUriTrigger(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true) .addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true) .addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true) + .setTriggerContentUpdateDelay(Duration.ofSeconds(5)) + .setTriggerContentUpdateDelay(Duration.ofSeconds(10)) .build() - val request = periodicRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER) + val request = oneTimeRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER) .setConstraints(constrains) .build() - workManager.enqueueUniquePeriodicWork(JOB_CONTENT_OBSERVER, ExistingPeriodicWorkPolicy.KEEP, request) + workManager.enqueueUniqueWork(JOB_CONTENT_OBSERVER, ExistingWorkPolicy.REPLACE, request) } override fun schedulePeriodicContactsBackup(user: User) { diff --git a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt index ea03c5806b67..e691aaf432d8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/ContentObserverWork.kt @@ -63,6 +63,9 @@ class ContentObserverWork( } catch (e: Exception) { Log_OC.e(TAG, "❌ Exception in ContentObserverWork: ${e.message}", e) Result.retry() + } finally { + Log_OC.d(TAG, "🔄" + "re-scheduling job") + backgroundJobManager.scheduleContentObserverJob() } } From 0a9b9dc8fd1e20da846cea9c0fe70642c13554b8 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Jan 2026 13:38:26 +0100 Subject: [PATCH 41/83] fix(auto-upload): calls Signed-off-by: alperozturk96 --- .../java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index 46ecb890017c..c5f6ceb021d4 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -276,7 +276,7 @@ internal class BackgroundJobManagerImpl( .addContentUriTrigger(MediaStore.Video.Media.INTERNAL_CONTENT_URI, true) .addContentUriTrigger(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true) .setTriggerContentUpdateDelay(Duration.ofSeconds(5)) - .setTriggerContentUpdateDelay(Duration.ofSeconds(10)) + .setTriggerContentMaxDelay(Duration.ofSeconds(10)) .build() val request = oneTimeRequestBuilder(ContentObserverWork::class, JOB_CONTENT_OBSERVER) From cdaf8df9002498dc0e02a52a2cd7762993c6cd56 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 28 Jan 2026 13:46:28 +0100 Subject: [PATCH 42/83] fix(auto-upload): calls Signed-off-by: alperozturk96 --- .../jobs/notification/WorkerNotificationManager.kt | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt index daf5614faf55..195a11b72a34 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/notification/WorkerNotificationManager.kt @@ -89,15 +89,4 @@ open class WorkerNotificationManager( notification, ForegroundServiceType.DataSync ) - - fun createNotification(title: String, iconId: Int): Notification = notificationBuilder - .setContentTitle(title) - .setSmallIcon(iconId) - .setOngoing(true) - .setSound(null) - .setVibrate(null) - .setOnlyAlertOnce(true) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setSilent(true) - .build() } From b058cc8504dc8fcb778b2d4cb3b0a851915e1845 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 29 Jan 2026 10:19:56 +0100 Subject: [PATCH 43/83] add debounce mechanism to not spam auto upload on start Signed-off-by: alperozturk96 --- .../client/preferences/AppPreferences.java | 3 +++ .../client/preferences/AppPreferencesImpl.java | 18 ++++++++++++++++++ .../java/com/owncloud/android/MainApp.java | 14 ++++++++------ app/src/main/res/values/strings.xml | 1 - 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java index 699b2d927e87..b7d6818ea5a1 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferences.java @@ -396,4 +396,7 @@ default void onDarkThemeModeChanged(DarkMode mode) { String getLastDisplayedAccountName(); void setLastDisplayedAccountName(String lastDisplayedAccountName); + + boolean startAutoUploadOnStart(); + void setLastAutoUploadOnStartTime(long timeInMillisecond); } diff --git a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index 682e211fcd5a..a16beaeaaad9 100644 --- a/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/app/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -111,6 +111,9 @@ public final class AppPreferencesImpl implements AppPreferences { private static final String PREF_LAST_DISPLAYED_ACCOUNT_NAME = "last_displayed_user"; + private static final String AUTO_PREF__LAST_AUTO_UPLOAD_ON_START = "last_auto_upload_on_start"; + + private static final String LOG_ENTRY = "log_entry"; private final Context context; @@ -118,6 +121,9 @@ public final class AppPreferencesImpl implements AppPreferences { private final UserAccountManager userAccountManager; private final ListenerRegistry listeners; + private static final int AUTO_UPLOAD_ON_START_DEBOUNCE_IN_MINUTES = 10; + private static final long AUTO_UPLOAD_ON_START_DEBOUNCE_MS = AUTO_UPLOAD_ON_START_DEBOUNCE_IN_MINUTES * 60 * 1000L; + /** * Adapter delegating raw {@link SharedPreferences.OnSharedPreferenceChangeListener} calls with key-value pairs to * respective {@link com.nextcloud.client.preferences.AppPreferences.Listener} method. @@ -849,4 +855,16 @@ public String getLastDisplayedAccountName() { public void setLastDisplayedAccountName(String lastDisplayedAccountName) { preferences.edit().putString(PREF_LAST_DISPLAYED_ACCOUNT_NAME, lastDisplayedAccountName).apply(); } + + @Override + public boolean startAutoUploadOnStart() { + long lastRunTime = preferences.getLong(AUTO_PREF__LAST_AUTO_UPLOAD_ON_START, 0L); + long now = System.currentTimeMillis(); + return lastRunTime == 0L || (now - lastRunTime) >= AUTO_UPLOAD_ON_START_DEBOUNCE_MS; + } + + @Override + public void setLastAutoUploadOnStartTime(long timeInMillisecond) { + preferences.edit().putLong(AUTO_PREF__LAST_AUTO_UPLOAD_ON_START, timeInMillisecond).apply(); + } } diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index 7fdbf8a49e51..1b979f23a433 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -17,7 +17,6 @@ import android.annotation.SuppressLint; import android.app.Activity; -import android.app.ActivityManager; import android.app.Application; import android.app.NotificationChannel; import android.app.NotificationManager; @@ -31,7 +30,6 @@ import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.ConnectivityManager; -import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.StrictMode; @@ -389,10 +387,14 @@ public void disableDocumentsStorageProvider() { private final LifecycleEventObserver lifecycleEventObserver = ((lifecycleOwner, event) -> { if (event == Lifecycle.Event.ON_START) { Log_OC.d(TAG, "APP IN FOREGROUND"); - FilesSyncHelper.startAutoUploadForEnabledSyncedFolders(syncedFolderProvider, - backgroundJobManager, - new String[]{}, - true); + + if (preferences.startAutoUploadOnStart()) { + FilesSyncHelper.startAutoUploadForEnabledSyncedFolders(syncedFolderProvider, + backgroundJobManager, + new String[]{}, + true); + preferences.setLastAutoUploadOnStartTime(System.currentTimeMillis()); + } } else if (event == Lifecycle.Event.ON_STOP) { passCodeManager.setCanAskPin(true); Log_OC.d(TAG, "APP IN BACKGROUND"); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9f682d40b69f..a73e55806662 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -927,7 +927,6 @@ Offline operations Shows progress of offline file operations - Detecting content changes Content observer Detects local file changes From bb07e103e672381f723345cb6a0cf8e327d35885 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 11:54:16 +0100 Subject: [PATCH 44/83] fix(auto-upload): file detection Signed-off-by: alperozturk96 --- .../client/database/dao/FileSystemDao.kt | 4 + .../client/database/dao/UploadDao.kt | 4 +- .../client/jobs/BackgroundJobFactory.kt | 2 +- .../jobs/autoUpload/AutoUploadWorker.kt | 103 +----------------- .../jobs/autoUpload/FileSystemRepository.kt | 54 ++++++--- .../jobs/autoUpload/SyncFolderHelper.kt | 102 +++++++++++++++++ .../client/jobs/upload/FileUploadHelper.kt | 2 +- 7 files changed, 153 insertions(+), 118 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt diff --git a/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt index 91a03044afde..af363b743283 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/FileSystemDao.kt @@ -8,6 +8,7 @@ package com.nextcloud.client.database.dao import androidx.room.Dao +import androidx.room.Delete import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query @@ -19,6 +20,9 @@ interface FileSystemDao { @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrReplace(filesystemEntity: FilesystemEntity) + @Delete + fun delete(entity: FilesystemEntity) + @Query( """ DELETE FROM ${ProviderMeta.ProviderTableMeta.FILESYSTEM_TABLE_NAME} diff --git a/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt b/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt index 7f09f4aa0543..2e1d86d726d5 100644 --- a/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt +++ b/app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt @@ -41,7 +41,7 @@ interface UploadDao { "WHERE ${ProviderTableMeta.UPLOADS_ACCOUNT_NAME} = :accountName " + "AND ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath" ) - fun deleteByAccountAndRemotePath(remotePath: String, accountName: String) + fun deleteByRemotePathAndAccountName(remotePath: String, accountName: String) @Query( "SELECT * FROM " + ProviderTableMeta.UPLOADS_TABLE_NAME + @@ -51,7 +51,7 @@ interface UploadDao { ) fun getUploadById(id: Long, accountName: String): UploadEntity? - @Insert(onConflict = OnConflictStrategy.Companion.REPLACE) + @Insert(onConflict = OnConflictStrategy.REPLACE) fun insertOrReplace(entity: UploadEntity): Long @Query( diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt index 4b1dd1d659b2..8b6012c0e8db 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt @@ -179,7 +179,7 @@ class BackgroundJobFactory @Inject constructor( powerManagementService = powerManagementService, syncedFolderProvider = syncedFolderProvider, backgroundJobManager = backgroundJobManager.get(), - repository = FileSystemRepository(dao = database.fileSystemDao(), context), + repository = FileSystemRepository(dao = database.fileSystemDao(), uploadsStorageManager, context), viewThemeUtils = viewThemeUtils.get(), localBroadcastManager = localBroadcastManager.get() ) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index af38b417a2b5..f2af22abf14e 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -9,8 +9,6 @@ package com.nextcloud.client.jobs.autoUpload import android.app.Notification import android.content.Context -import android.content.res.Resources -import androidx.exifinterface.media.ExifInterface import androidx.localbroadcastmanager.content.LocalBroadcastManager import androidx.work.CoroutineWorker import androidx.work.ForegroundInfo @@ -25,13 +23,11 @@ import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService -import com.nextcloud.client.preferences.SubFolderRule import com.nextcloud.utils.extensions.isNonRetryable import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FileDataStorageManager -import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderProvider import com.owncloud.android.datamodel.UploadsStorageManager @@ -44,16 +40,10 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.operations.UploadFileOperation import com.owncloud.android.ui.activity.SettingsActivity -import com.owncloud.android.utils.FileStorageUtils -import com.owncloud.android.utils.MimeType import com.owncloud.android.utils.theme.ViewThemeUtils import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.File -import java.text.ParsePosition -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.TimeZone @Suppress("LongParameterList", "TooManyFunctions", "TooGenericExceptionCaught") class AutoUploadWorker( @@ -79,6 +69,7 @@ class AutoUploadWorker( } private val helper = AutoUploadHelper() + private val syncFolderHelper = SyncFolderHelper(context) private val fileUploadBroadcastManager = FileUploadBroadcastManager(localBroadcastManager) private lateinit var syncedFolder: SyncedFolder private val notificationManager = AutoUploadNotificationManager(context, viewThemeUtils, NOTIFICATION_ID) @@ -233,13 +224,6 @@ class AutoUploadWorker( Log_OC.d(TAG, "Exception collectFileChangesFromContentObserverWork: $e") } - private fun prepareDateFormat(): SimpleDateFormat { - val currentLocale = context.resources.configuration.locales[0] - return SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale).apply { - timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) - } - } - private fun getUserOrReturn(syncedFolder: SyncedFolder): User? { val optionalUser = userAccountManager.getUser(syncedFolder.account) if (!optionalUser.isPresent) { @@ -274,13 +258,10 @@ class AutoUploadWorker( @Suppress("LongMethod", "DEPRECATION", "TooGenericExceptionCaught") private suspend fun uploadFiles(syncedFolder: SyncedFolder) = withContext(Dispatchers.IO) { - val dateFormat = prepareDateFormat() val user = getUserOrReturn(syncedFolder) ?: return@withContext val ocAccount = OwnCloudAccount(user.toPlatformAccount(), context) val client = OwnCloudClientManagerFactory.getDefaultSingleton() .getClientFor(ocAccount, context) - val lightVersion = context.resources.getBoolean(R.bool.syncedFolder_light) - val currentLocale = context.resources.configuration.locales[0] trySetForeground() updateNotification() @@ -299,14 +280,7 @@ class AutoUploadWorker( filePathsWithIds.forEachIndexed { batchIndex, (path, id) -> val file = File(path) val localPath = file.absolutePath - val remotePath = getRemotePath( - file, - syncedFolder, - dateFormat, - lightVersion, - context.resources, - currentLocale - ) + val remotePath = syncFolderHelper.getAutoUploadRemotePath(syncedFolder, file) try { val entityResult = getEntityResult(user, localPath, remotePath) @@ -471,79 +445,6 @@ class AutoUploadWorker( FileDataStorageManager(user, context.contentResolver) ) - private fun getRemotePath( - file: File, - syncedFolder: SyncedFolder, - sFormatter: SimpleDateFormat, - lightVersion: Boolean, - resources: Resources, - currentLocale: Locale - ): String { - val lastModificationTime = calculateLastModificationTime(file, syncedFolder, sFormatter) - - val (remoteFolder, useSubfolders, subFolderRule) = if (lightVersion) { - Triple( - resources.getString(R.string.syncedFolder_remote_folder), - resources.getBoolean(R.bool.syncedFolder_light_use_subfolders), - SubFolderRule.YEAR_MONTH - ) - } else { - Triple( - syncedFolder.remotePath, - syncedFolder.isSubfolderByDate, - syncedFolder.subfolderRule - ) - } - - return FileStorageUtils.getInstantUploadFilePath( - file, - currentLocale, - remoteFolder, - syncedFolder.localPath, - lastModificationTime, - useSubfolders, - subFolderRule - ) - } - - private fun hasExif(file: File): Boolean { - val mimeType = FileStorageUtils.getMimeTypeFromName(file.absolutePath) - return MimeType.JPEG.equals(mimeType, ignoreCase = true) || MimeType.TIFF.equals(mimeType, ignoreCase = true) - } - - @Suppress("NestedBlockDepth") - private fun calculateLastModificationTime( - file: File, - syncedFolder: SyncedFolder, - formatter: SimpleDateFormat - ): Long { - var lastModificationTime = file.lastModified() - if (MediaFolderType.IMAGE == syncedFolder.type && hasExif(file)) { - Log_OC.d(TAG, "calculateLastModificationTime exif found") - - @Suppress("TooGenericExceptionCaught") - try { - val exifInterface = ExifInterface(file.absolutePath) - val exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME) - if (!exifDate.isNullOrBlank()) { - val pos = ParsePosition(0) - val dateTime = formatter.parse(exifDate, pos) - if (dateTime != null) { - lastModificationTime = dateTime.time - Log_OC.w(TAG, "calculateLastModificationTime calculatedTime is: $lastModificationTime") - } else { - Log_OC.w(TAG, "calculateLastModificationTime dateTime is empty") - } - } else { - Log_OC.w(TAG, "calculateLastModificationTime exifDate is empty") - } - } catch (e: Exception) { - Log_OC.d(TAG, "Failed to get the proper time " + e.localizedMessage) - } - } - return lastModificationTime - } - private fun sendUploadFinishEvent(operation: UploadFileOperation, result: RemoteOperationResult<*>) { fileUploadBroadcastManager.sendFinished( operation, diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt index f9c12b36b9a1..b50da8c76b2c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt @@ -15,19 +15,37 @@ import com.nextcloud.client.database.entity.FilesystemEntity import com.nextcloud.utils.extensions.shouldSkipFile import com.nextcloud.utils.extensions.toFile import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.datamodel.UploadsStorageManager import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.utils.SyncedFolderUtils import java.io.File import java.util.zip.CRC32 @Suppress("TooGenericExceptionCaught", "NestedBlockDepth", "MagicNumber", "ReturnCount") -class FileSystemRepository(private val dao: FileSystemDao, private val context: Context) { +class FileSystemRepository( + private val dao: FileSystemDao, + private val uploadsStorageManager: UploadsStorageManager, + private val context: Context +) { + private val syncFolderHelper = SyncFolderHelper(context) companion object { private const val TAG = "FilesystemRepository" const val BATCH_SIZE = 50 } + fun deleteAutoUploadEntityAndUploadEntity(syncedFolder: SyncedFolder, localPath: String, entity: FilesystemEntity) { + Log_OC.d(TAG, "deleting auto upload entity and upload entity") + + val file = File(localPath) + val remotePath = syncFolderHelper.getAutoUploadRemotePath(syncedFolder, file) + uploadsStorageManager.uploadDao.deleteByRemotePathAndAccountName( + remotePath = remotePath, + accountName = syncedFolder.account + ) + dao.delete(entity) + } + suspend fun deleteByLocalPathAndId(path: String, id: Int) { dao.deleteByLocalPathAndId(path, id) } @@ -39,20 +57,23 @@ class FileSystemRepository(private val dao: FileSystemDao, private val context: val entities = dao.getAutoUploadFilesEntities(syncedFolderId, BATCH_SIZE, lastId) val filtered = mutableListOf>() - entities.forEach { - it.localPath?.let { path -> + entities.forEach { entity -> + entity.localPath?.let { path -> val file = File(path) if (!file.exists()) { Log_OC.w(TAG, "Ignoring file for upload (doesn't exist): $path") + deleteAutoUploadEntityAndUploadEntity(syncedFolder, path, entity) } else if (!SyncedFolderUtils.isQualifiedFolder(file.parent)) { Log_OC.w(TAG, "Ignoring file for upload (unqualified folder): $path") + deleteAutoUploadEntityAndUploadEntity(syncedFolder, path, entity) } else if (!SyncedFolderUtils.isFileNameQualifiedForAutoUpload(file.name)) { Log_OC.w(TAG, "Ignoring file for upload (unqualified file): $path") + deleteAutoUploadEntityAndUploadEntity(syncedFolder, path, entity) } else { Log_OC.d(TAG, "Adding path to upload: $path") - if (it.id != null) { - filtered.add(path to it.id) + if (entity.id != null) { + filtered.add(path to entity.id) } else { Log_OC.w(TAG, "cant adding path to upload, id is null") } @@ -160,22 +181,29 @@ class FileSystemRepository(private val dao: FileSystemDao, private val context: } val entity = dao.getFileByPathAndFolder(localPath, syncedFolder.id.toString()) - val fileSentForUpload = (entity != null && entity.fileSentForUpload == 1) - if (fileSentForUpload) { - Log_OC.d(TAG, "File was sent for upload, checking if it changed...") - } val fileModified = (lastModified ?: file.lastModified()) - if (syncedFolder.shouldSkipFile(file, fileModified, creationTime, fileSentForUpload)) { + if (fileModified <= 0L) { + Log_OC.d(TAG, "file is deleted, skipping: $localPath") + entity?.let { + deleteAutoUploadEntityAndUploadEntity(syncedFolder, localPath, entity) + } return } - if (fileSentForUpload) { - Log_OC.d(TAG, "File was sent for upload before but has changed, will re-upload: $localPath") + val hasNotChanged = entity?.fileModified == fileModified + val fileSentForUpload = entity?.fileSentForUpload == 1 + + if (hasNotChanged && fileSentForUpload) { + Log_OC.d(TAG, "File hasn't changed since last scan. skipping: $localPath") + return } - val crc = getFileChecksum(file) + if (syncedFolder.shouldSkipFile(file, fileModified, creationTime, fileSentForUpload)) { + return + } + val crc = getFileChecksum(file) val newEntity = FilesystemEntity( id = entity?.id, localPath = localPath, diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt new file mode 100644 index 000000000000..6100487f6210 --- /dev/null +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt @@ -0,0 +1,102 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2026 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.client.jobs.autoUpload + +import android.content.Context +import androidx.exifinterface.media.ExifInterface +import com.nextcloud.client.preferences.SubFolderRule +import com.owncloud.android.R +import com.owncloud.android.datamodel.MediaFolderType +import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.lib.common.utils.Log_OC +import com.owncloud.android.utils.FileStorageUtils +import com.owncloud.android.utils.MimeType +import java.io.File +import java.text.ParsePosition +import java.text.SimpleDateFormat +import java.util.TimeZone + +class SyncFolderHelper(context: Context) { + + private val resources = context.resources + private val isLightVersion = resources.getBoolean(R.bool.syncedFolder_light) + + companion object { + private const val TAG = "SyncFolderHelper" + } + + fun getAutoUploadRemotePath(syncedFolder: SyncedFolder, file: File): String { + val lastModificationTime = calculateLastModificationTime(file, syncedFolder) + + val remoteFolder: String + val useSubfolders: Boolean + val subFolderRule: SubFolderRule + + if (isLightVersion) { + remoteFolder = resources.getString(R.string.syncedFolder_remote_folder) + useSubfolders = resources.getBoolean(R.bool.syncedFolder_light_use_subfolders) + subFolderRule = SubFolderRule.YEAR_MONTH + } else { + remoteFolder = syncedFolder.remotePath + useSubfolders = syncedFolder.isSubfolderByDate + subFolderRule = syncedFolder.subfolderRule + } + + return FileStorageUtils.getInstantUploadFilePath( + file, + resources.configuration.locales[0], + remoteFolder, + syncedFolder.localPath, + lastModificationTime, + useSubfolders, + subFolderRule + ) + } + + @Suppress("NestedBlockDepth") + private fun calculateLastModificationTime(file: File, syncedFolder: SyncedFolder): Long { + val currentLocale = resources.configuration.locales[0] + val formatter = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale).apply { + timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) + } + var lastModificationTime = file.lastModified() + if (MediaFolderType.IMAGE == syncedFolder.type && hasExif(file)) { + Log_OC.d(TAG, "calculateLastModificationTime exif found") + + @Suppress("TooGenericExceptionCaught") + try { + val exifInterface = ExifInterface(file.absolutePath) + val exifDate = exifInterface.getAttribute(ExifInterface.TAG_DATETIME) + if (!exifDate.isNullOrBlank()) { + val pos = ParsePosition(0) + val dateTime = formatter.parse(exifDate, pos) + if (dateTime != null) { + lastModificationTime = dateTime.time + Log_OC.w( + TAG, + "calculateLastModificationTime calculatedTime is: $lastModificationTime" + ) + } else { + Log_OC.w(TAG, "calculateLastModificationTime dateTime is empty") + } + } else { + Log_OC.w(TAG, "calculateLastModificationTime exifDate is empty") + } + } catch (e: Exception) { + Log_OC.d(TAG, "Failed to get the proper time " + e.localizedMessage) + } + } + return lastModificationTime + } + + private fun hasExif(file: File): Boolean { + val mimeType = FileStorageUtils.getMimeTypeFromName(file.absolutePath) + return mimeType.equals(MimeType.JPEG, ignoreCase = true) || + mimeType.equals(MimeType.TIFF, ignoreCase = true) + } +} diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 809d24c8e66d..5ef42d5aa2ea 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -261,7 +261,7 @@ class FileUploadHelper { } fun removeFileUpload(remotePath: String, accountName: String) { - uploadsStorageManager.uploadDao.deleteByAccountAndRemotePath(remotePath, accountName) + uploadsStorageManager.uploadDao.deleteByRemotePathAndAccountName(remotePath, accountName) } fun updateUploadStatus(remotePath: String, accountName: String, status: UploadStatus) { From 1c4dfd831f786468a56381e8c1777f9b404bc483 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 11:55:22 +0100 Subject: [PATCH 45/83] fix(auto-upload): file detection Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt index 6100487f6210..bf0d549774b8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt @@ -47,7 +47,7 @@ class SyncFolderHelper(context: Context) { subFolderRule = syncedFolder.subfolderRule } - return FileStorageUtils.getInstantUploadFilePath( + val result = FileStorageUtils.getInstantUploadFilePath( file, resources.configuration.locales[0], remoteFolder, @@ -56,6 +56,10 @@ class SyncFolderHelper(context: Context) { useSubfolders, subFolderRule ) + + Log_OC.d(TAG, "auto upload remote path: $result") + + return result } @Suppress("NestedBlockDepth") From 818cad563f9491e924cea111ddca104cdfeaa4f3 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 11:56:36 +0100 Subject: [PATCH 46/83] fix(auto-upload): file detection Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt index bf0d549774b8..990ad440990c 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt @@ -21,16 +21,15 @@ import java.text.ParsePosition import java.text.SimpleDateFormat import java.util.TimeZone -class SyncFolderHelper(context: Context) { - - private val resources = context.resources - private val isLightVersion = resources.getBoolean(R.bool.syncedFolder_light) +class SyncFolderHelper(private val context: Context) { companion object { private const val TAG = "SyncFolderHelper" } fun getAutoUploadRemotePath(syncedFolder: SyncedFolder, file: File): String { + val resources = context.resources + val isLightVersion = resources.getBoolean(R.bool.syncedFolder_light) val lastModificationTime = calculateLastModificationTime(file, syncedFolder) val remoteFolder: String @@ -64,6 +63,7 @@ class SyncFolderHelper(context: Context) { @Suppress("NestedBlockDepth") private fun calculateLastModificationTime(file: File, syncedFolder: SyncedFolder): Long { + val resources = context.resources val currentLocale = resources.configuration.locales[0] val formatter = SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale).apply { timeZone = TimeZone.getTimeZone(TimeZone.getDefault().id) From e7c37be566731828f7cf6c9802105c6342863123 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 13:28:47 +0100 Subject: [PATCH 47/83] fix(auto-upload): file lock unlock Signed-off-by: alperozturk96 --- .../client/jobs/upload/FileUploadHelper.kt | 9 ++- .../operations/UploadFileOperation.java | 81 +++++++++++++++---- 2 files changed, 73 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 5ef42d5aa2ea..9478850e1be0 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -19,9 +19,9 @@ import com.nextcloud.client.device.BatteryStatus import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManager import com.nextcloud.client.jobs.upload.FileUploadWorker.Companion.currentUploadFileOperation -import com.nextcloud.client.notifications.AppWideNotificationManager import com.nextcloud.client.network.Connectivity import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.client.notifications.AppWideNotificationManager import com.nextcloud.utils.extensions.getUploadIds import com.owncloud.android.MainApp import com.owncloud.android.R @@ -479,7 +479,12 @@ class FileUploadHelper { } @Suppress("MagicNumber") - fun isSameFileOnRemote(user: User, localFile: File, remotePath: String, context: Context): Boolean { + fun isSameFileOnRemote(user: User?, localFile: File?, remotePath: String?, context: Context?): Boolean { + if (user == null || localFile == null || remotePath == null || context == null) { + Log_OC.e(TAG,"cannot compare remote and local file") + return false + } + // Compare remote file to local file val localLastModifiedTimestamp = localFile.lastModified() / 1000 // remote file timestamp in milli not micro sec val localCreationTimestamp = FileUtil.getCreationTimestamp(localFile) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index fa8b2a13c72a..652b4bb24c7f 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1011,6 +1011,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { File expectedFile = null; FileLock fileLock = null; FileChannel channel = null; + FileInputStream fileInputStream = null; long size; @@ -1043,9 +1044,19 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { final Long creationTimestamp = FileUtil.getCreationTimestamp(originalFile); try { - channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); - fileLock = channel.tryLock(); + fileInputStream = new FileInputStream(mFile.getStoragePath()); + channel = fileInputStream.getChannel(); + try { + // request a shared lock instead of exclusive one, since we are just reading file + fileLock = channel.tryLock(0L, Long.MAX_VALUE, true); + Log_OC.d(TAG ,"file locked"); + } catch (OverlappingFileLockException e) { + // if another thread has the lock, current thread can still read the file. + Log_OC.e(TAG, "shared lock overlap detected; proceeding safely."); + } } catch (FileNotFoundException e) { + Log_OC.e(TAG, "file not found exception: normal upload"); + // this basically means that the file is on SD card // try to copy file to temporary dir if it doesn't exist String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + @@ -1058,8 +1069,13 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (result.isSuccess()) { if (temporalFile.length() == originalFile.length()) { - channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); - fileLock = channel.tryLock(); + try { + fileInputStream = new FileInputStream(temporalFile.getAbsolutePath()); + channel = fileInputStream.getChannel(); + fileLock = channel.tryLock(0L, Long.MAX_VALUE, true); + } catch (OverlappingFileLockException ex) { + Log_OC.e(TAG, "shared lock overlap detected; proceeding safely."); + } } else { result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } @@ -1067,7 +1083,11 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } try { - size = channel.size(); + if (channel != null && channel.isOpen()) { + size = channel.size(); + } else { + size = new File(mFile.getStoragePath()).length(); + } } catch (Exception exception) { Log_OC.e(TAG, "normalUpload, size cannot be determined from channel: " + exception); size = new File(mFile.getStoragePath()).length(); @@ -1121,28 +1141,42 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } catch (FileNotFoundException e) { Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); result = new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); - } catch (OverlappingFileLockException e) { - Log_OC.d(TAG, "Overlapping file lock exception"); - result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } catch (Exception e) { result = new RemoteOperationResult<>(e); } finally { mUploadStarted.set(false); - if (fileLock != null) { + if (fileLock != null && fileLock.isValid()) { try { fileLock.release(); - } catch (IOException e) { - Log_OC.e(TAG, "Failed to unlock file with path " + mOriginalStoragePath); + Log_OC.d(TAG ,"file lock released"); + } catch (IOException ignored) { + Log_OC.e(TAG, "failed to unlock file with path " + mOriginalStoragePath); } + } else { + Log_OC.e(TAG, "file lock is null"); } if (channel != null) { try { channel.close(); - } catch (IOException e) { - Log_OC.w(TAG, "Failed to close file channel"); + Log_OC.d(TAG ,"file channel closed"); + } catch (IOException ignored) { + Log_OC.e(TAG, "failed to close file channel"); } + } else { + Log_OC.e(TAG, "channel is null"); + } + + if (fileInputStream != null) { + try { + fileInputStream.close(); + Log_OC.d(TAG ,"file input stream closed"); + } catch (IOException ignored) { + Log_OC.e(TAG, "failed to close file input stream"); + } + } else { + Log_OC.e(TAG, "file input stream is null"); } if (temporalFile != null && !originalFile.equals(temporalFile)) { @@ -1248,7 +1282,24 @@ private RemoteOperationResult checkNameCollision(OCFile parentFile, break; case ASK_USER: Log_OC.d(TAG, "Name collision; asking the user what to do"); - return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + + // check if its real SYNC_CONFLICT + boolean isSameFileOnRemote = false; + if (mFile != null) { + String localPath = mFile.getStoragePath(); + + if (localPath != null) { + File localFile = new File(localPath); + isSameFileOnRemote = FileUploadHelper.Companion.instance() + .isSameFileOnRemote(user, localFile, mRemotePath, mContext); + } + } + + if (isSameFileOnRemote) { + return new RemoteOperationResult<>(ResultCode.OK); + } else { + return new RemoteOperationResult<>(ResultCode.SYNC_CONFLICT); + } } } @@ -1474,7 +1525,7 @@ private static boolean existsFile(OwnCloudClient client, return false; } else { ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, false); - RemoteOperationResult result = existsOperation.execute(client); + final var result = existsOperation.execute(client); return result.isSuccess(); } } From aa47a9c54b44a67d136143025d7b107a05757e7e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 13:32:50 +0100 Subject: [PATCH 48/83] fix codacy Signed-off-by: alperozturk96 --- .../com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt | 2 +- .../java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt index 990ad440990c..75896f372d0b 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/SyncFolderHelper.kt @@ -46,7 +46,7 @@ class SyncFolderHelper(private val context: Context) { subFolderRule = syncedFolder.subfolderRule } - val result = FileStorageUtils.getInstantUploadFilePath( + val result = FileStorageUtils.getInstantUploadFilePath( file, resources.configuration.locales[0], remoteFolder, diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt index 9478850e1be0..5bb40b30969d 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadHelper.kt @@ -478,10 +478,10 @@ class FileUploadHelper { } } - @Suppress("MagicNumber") + @Suppress("MagicNumber", "ReturnCount", "ComplexCondition") fun isSameFileOnRemote(user: User?, localFile: File?, remotePath: String?, context: Context?): Boolean { if (user == null || localFile == null || remotePath == null || context == null) { - Log_OC.e(TAG,"cannot compare remote and local file") + Log_OC.e(TAG, "cannot compare remote and local file") return false } From 2d2ea943554b8c7032be721d57fea46a9c0fe69e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Fri, 6 Feb 2026 14:26:45 +0100 Subject: [PATCH 49/83] fix test Signed-off-by: alperozturk96 --- .../client/jobs/autoUpload/FileSystemRepository.kt | 10 +++++----- .../android/operations/UploadFileOperation.java | 12 ++++++------ .../owncloud/android/utils/AutoUploadHelperTest.kt | 4 +++- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt index b50da8c76b2c..d4804134f466 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt @@ -34,7 +34,7 @@ class FileSystemRepository( const val BATCH_SIZE = 50 } - fun deleteAutoUploadEntityAndUploadEntity(syncedFolder: SyncedFolder, localPath: String, entity: FilesystemEntity) { + fun deleteAutoUploadAndUploadEntity(syncedFolder: SyncedFolder, localPath: String, entity: FilesystemEntity) { Log_OC.d(TAG, "deleting auto upload entity and upload entity") val file = File(localPath) @@ -62,13 +62,13 @@ class FileSystemRepository( val file = File(path) if (!file.exists()) { Log_OC.w(TAG, "Ignoring file for upload (doesn't exist): $path") - deleteAutoUploadEntityAndUploadEntity(syncedFolder, path, entity) + deleteAutoUploadAndUploadEntity(syncedFolder, path, entity) } else if (!SyncedFolderUtils.isQualifiedFolder(file.parent)) { Log_OC.w(TAG, "Ignoring file for upload (unqualified folder): $path") - deleteAutoUploadEntityAndUploadEntity(syncedFolder, path, entity) + deleteAutoUploadAndUploadEntity(syncedFolder, path, entity) } else if (!SyncedFolderUtils.isFileNameQualifiedForAutoUpload(file.name)) { Log_OC.w(TAG, "Ignoring file for upload (unqualified file): $path") - deleteAutoUploadEntityAndUploadEntity(syncedFolder, path, entity) + deleteAutoUploadAndUploadEntity(syncedFolder, path, entity) } else { Log_OC.d(TAG, "Adding path to upload: $path") @@ -186,7 +186,7 @@ class FileSystemRepository( if (fileModified <= 0L) { Log_OC.d(TAG, "file is deleted, skipping: $localPath") entity?.let { - deleteAutoUploadEntityAndUploadEntity(syncedFolder, localPath, entity) + deleteAutoUploadAndUploadEntity(syncedFolder, localPath, entity) } return } diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 652b4bb24c7f..1f4a6b611fa7 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1049,7 +1049,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { try { // request a shared lock instead of exclusive one, since we are just reading file fileLock = channel.tryLock(0L, Long.MAX_VALUE, true); - Log_OC.d(TAG ,"file locked"); + Log_OC.d(TAG ,"🔒" + "file locked"); } catch (OverlappingFileLockException e) { // if another thread has the lock, current thread can still read the file. Log_OC.e(TAG, "shared lock overlap detected; proceeding safely."); @@ -1089,7 +1089,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { size = new File(mFile.getStoragePath()).length(); } } catch (Exception exception) { - Log_OC.e(TAG, "normalUpload, size cannot be determined from channel: " + exception); + Log_OC.e(TAG, "size cannot be determined from channel: " + exception); size = new File(mFile.getStoragePath()).length(); } @@ -1149,7 +1149,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (fileLock != null && fileLock.isValid()) { try { fileLock.release(); - Log_OC.d(TAG ,"file lock released"); + Log_OC.d(TAG ,"🔓" + "file lock released"); } catch (IOException ignored) { Log_OC.e(TAG, "failed to unlock file with path " + mOriginalStoragePath); } @@ -1160,7 +1160,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (channel != null) { try { channel.close(); - Log_OC.d(TAG ,"file channel closed"); + Log_OC.d(TAG ,"📢" + "file channel closed"); } catch (IOException ignored) { Log_OC.e(TAG, "failed to close file channel"); } @@ -1171,7 +1171,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (fileInputStream != null) { try { fileInputStream.close(); - Log_OC.d(TAG ,"file input stream closed"); + Log_OC.d(TAG ,"📝" + "file input stream closed"); } catch (IOException ignored) { Log_OC.e(TAG, "failed to close file input stream"); } @@ -1181,7 +1181,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (temporalFile != null && !originalFile.equals(temporalFile)) { boolean isTempFileDeleted = temporalFile.delete(); - Log_OC.d(TAG, "normalUpload, temp folder deletion: " + isTempFileDeleted); + Log_OC.d(TAG, "temp folder deletion: " + isTempFileDeleted); } if (result == null) { diff --git a/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt b/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt index a7aff50a8cc3..5eb368139729 100644 --- a/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt +++ b/app/src/test/java/com/owncloud/android/utils/AutoUploadHelperTest.kt @@ -15,6 +15,7 @@ import com.nextcloud.client.preferences.SubFolderRule import com.nextcloud.utils.extensions.shouldSkipFile import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder +import com.owncloud.android.datamodel.UploadsStorageManager import io.mockk.clearAllMocks import io.mockk.mockk import org.junit.After @@ -36,6 +37,7 @@ class AutoUploadHelperTest { private val mockContext: Context = mockk(relaxed = true) private lateinit var repo: FileSystemRepository + private val mockUploadsStorageManager: UploadsStorageManager = mockk(relaxed = true) @Before fun setup() { @@ -43,7 +45,7 @@ class AutoUploadHelperTest { tempDir.mkdirs() assertTrue("Failed to create temp directory", tempDir.exists()) - repo = FileSystemRepository(mockDao, mockContext) + repo = FileSystemRepository(mockDao, mockUploadsStorageManager, mockContext) } @After From 1fcbf72b32e396a298cb99cab081c51b9baf3c7e Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 10 Feb 2026 08:51:41 +0100 Subject: [PATCH 50/83] only update last scan time after uploading files Signed-off-by: alperozturk96 --- .../nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index f2af22abf14e..c4f9e26034d8 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -93,7 +93,11 @@ class AutoUploadWorker( collectFileChangesFromContentObserverWork(contentUris) uploadFiles(syncedFolder) - Log_OC.d(TAG, "✅ ${syncedFolder.remotePath} finished checking files.") + // only update last scan time after uploading files + syncedFolder.lastScanTimestampMs = System.currentTimeMillis() + syncedFolderProvider.updateSyncFolder(syncedFolder) + + Log_OC.d(TAG, "✅ ${syncedFolder.remotePath} completed") Result.success() } catch (e: Exception) { Log_OC.e(TAG, "❌ failed: ${e.message}") @@ -217,8 +221,6 @@ class AutoUploadWorker( helper.insertEntries(syncedFolder, repository) } } - syncedFolder.lastScanTimestampMs = System.currentTimeMillis() - syncedFolderProvider.updateSyncFolder(syncedFolder) } } catch (e: Exception) { Log_OC.d(TAG, "Exception collectFileChangesFromContentObserverWork: $e") From f749f0471ee0b7c3d1ce2324c757fa44f08592f9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Feb 2026 08:23:42 +0100 Subject: [PATCH 51/83] only update database within upload file operation to prevent unnecessary db updates Signed-off-by: alperozturk96 --- .../jobs/autoUpload/AutoUploadWorker.kt | 6 ---- .../client/jobs/upload/FileUploadWorker.kt | 6 +--- .../client/jobs/upload/UploadTask.kt | 1 - .../UploadStorageManagerExtensions.kt | 28 ------------------- .../datamodel/UploadsStorageManager.java | 1 - .../operations/UploadFileOperation.java | 6 ++++ 6 files changed, 7 insertions(+), 41 deletions(-) delete mode 100644 app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index c4f9e26034d8..72bd41b6e7c2 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -24,7 +24,6 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.utils.extensions.isNonRetryable -import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FileDataStorageManager @@ -313,7 +312,6 @@ class AutoUploadWorker( val result = operation.execute(client) fileUploadBroadcastManager.sendStarted(operation, context) - uploadsStorageManager.updateStatus(uploadEntity, result.isSuccess) UploadErrorNotificationManager.handleResult( context, @@ -343,10 +341,6 @@ class AutoUploadWorker( sendUploadFinishEvent(operation, result) } } catch (e: Exception) { - uploadsStorageManager.updateStatus( - uploadEntity, - UploadsStorageManager.UploadStatus.UPLOAD_FAILED - ) Log_OC.e( TAG, "Exception during upload file, localPath: $localPath, remotePath: $remotePath," + diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index ea0bac258db8..ddea5679e41e 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -24,7 +24,6 @@ import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.utils.ForegroundServiceHelper import com.nextcloud.utils.extensions.getPercent -import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.ForegroundServiceType @@ -258,8 +257,6 @@ class FileUploadWorker( val result = withContext(Dispatchers.IO) { upload(operation, user, client) } - val entity = uploadsStorageManager.uploadDao.getUploadById(upload.uploadId, accountName) - uploadsStorageManager.updateStatus(entity, result.isSuccess) currentUploadFileOperation = null if (result.code == ResultCode.QUOTA_EXCEEDED) { @@ -346,10 +343,9 @@ class FileUploadWorker( fileUploadBroadcastManager.sendStarted(operation, context) } catch (e: Exception) { Log_OC.e(TAG, "Error uploading", e) - result = RemoteOperationResult(e) + result = RemoteOperationResult(e) } finally { if (!isStopped) { - uploadsStorageManager.updateDatabaseUploadResult(result, operation) UploadErrorNotificationManager.handleResult( context, notificationManager, diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt index 21aa3619b85d..efe129f8d765 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/UploadTask.kt @@ -78,7 +78,6 @@ class UploadTask( val client = clientProvider() uploadsStorageManager.updateDatabaseUploadStart(op) val result = op.execute(client) - uploadsStorageManager.updateDatabaseUploadResult(result, op) return Result(file, result.isSuccess) } } diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt deleted file mode 100644 index e86a9f0f8ec3..000000000000 --- a/app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Nextcloud - Android Client - * - * SPDX-FileCopyrightText: 2025 Alper Ozturk - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -package com.nextcloud.utils.extensions - -import com.nextcloud.client.database.entity.UploadEntity -import com.owncloud.android.datamodel.UploadsStorageManager - -fun UploadsStorageManager.updateStatus(entity: UploadEntity?, status: UploadsStorageManager.UploadStatus) { - entity ?: return - uploadDao.insertOrReplace(entity.withStatus(status)) -} - -fun UploadsStorageManager.updateStatus(entity: UploadEntity?, success: Boolean) { - entity ?: return - val newStatus = if (success) { - UploadsStorageManager.UploadStatus.UPLOAD_SUCCEEDED - } else { - UploadsStorageManager.UploadStatus.UPLOAD_FAILED - } - uploadDao.insertOrReplace(entity.withStatus(newStatus)) -} - -private fun UploadEntity.withStatus(newStatus: UploadsStorageManager.UploadStatus) = this.copy(status = newStatus.value) diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index b339655b438c..130045768810 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -554,7 +554,6 @@ public void clearSuccessfulUploads() { * Updates the persistent upload database with upload result. */ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) { - // result: success or fail notification Log_OC.d(TAG, "updateDatabaseUploadResult uploadResult: " + uploadResult + " upload: " + upload); if (uploadResult.isCancelled()) { diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1f4a6b611fa7..e1a8acbb3a22 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -577,6 +577,9 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare result = new RemoteOperationResult<>(e); } finally { result = cleanupE2EUpload(fileLock, channel, e2eFiles, result, object, client, token); + + // update upload status + uploadsStorageManager.updateDatabaseUploadResult(result, this); } completeE2EUpload(result, e2eFiles, client); @@ -1189,6 +1192,9 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } logResult(result, mOriginalStoragePath, mRemotePath); + + // update upload status + uploadsStorageManager.updateDatabaseUploadResult(result, this); } if (result.isSuccess()) { From 519afc8a7fa4937ad4a9fda889c6965f442baa3d Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Feb 2026 09:12:44 +0100 Subject: [PATCH 52/83] fix updateDatabaseUploadResult Signed-off-by: alperozturk96 --- .../datamodel/UploadsStorageManager.java | 83 ++++---- .../operations/UploadFileOperation.java | 201 ++++++------------ 2 files changed, 104 insertions(+), 180 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 130045768810..41f8ea969674 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -550,58 +550,49 @@ public void clearSuccessfulUploads() { } } - /** - * Updates the persistent upload database with upload result. - */ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, UploadFileOperation upload) { Log_OC.d(TAG, "updateDatabaseUploadResult uploadResult: " + uploadResult + " upload: " + upload); if (uploadResult.isCancelled()) { - removeUpload( - upload.getUser().getAccountName(), - upload.getRemotePath() - ); - } else { - String localPath = (FileUploadWorker.LOCAL_BEHAVIOUR_MOVE == upload.getLocalBehaviour()) - ? upload.getStoragePath() : null; - - if (uploadResult.isSuccess()) { - updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_SUCCEEDED, - UploadResult.UPLOADED, - upload.getRemotePath(), - localPath - ); - } else if (uploadResult.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT && - new FileUploadHelper().isSameFileOnRemote( - upload.getUser(), new File(upload.getStoragePath()), upload.getRemotePath(), upload.getContext())) { - - updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_SUCCEEDED, - UploadResult.SAME_FILE_CONFLICT, - upload.getRemotePath(), - localPath - ); - } else if (uploadResult.getCode() == RemoteOperationResult.ResultCode.LOCAL_FILE_NOT_FOUND) { - updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_SUCCEEDED, - UploadResult.FILE_NOT_FOUND, - upload.getRemotePath(), - localPath - ); - } else if (uploadResult.getCode() != RemoteOperationResult.ResultCode.USER_CANCELLED){ - updateUploadStatus( - upload.getOCUploadId(), - UploadStatus.UPLOAD_FAILED, - UploadResult.fromOperationResult(uploadResult), - upload.getRemotePath(), - localPath - ); + Log_OC.w(TAG, "upload is cancelled, removing upload"); + removeUpload(upload.getUser().getAccountName(), upload.getRemotePath()); + return; + } + + String localPath = (upload.getLocalBehaviour() == FileUploadWorker.LOCAL_BEHAVIOUR_MOVE) + ? upload.getStoragePath() : null; + + Log_OC.d(TAG, "local path of upload: " + localPath); + + UploadStatus status = UploadStatus.UPLOAD_FAILED; + UploadResult result = UploadResult.fromOperationResult(uploadResult); + RemoteOperationResult.ResultCode code = uploadResult.getCode(); + + if (uploadResult.isSuccess()) { + status = UploadStatus.UPLOAD_SUCCEEDED; + result = UploadResult.UPLOADED; + } else if (code == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { + boolean isSame = new FileUploadHelper().isSameFileOnRemote( + upload.getUser(), new File(upload.getStoragePath()), upload.getRemotePath(), upload.getContext()); + + if (isSame) { + result = UploadResult.SAME_FILE_CONFLICT; + status = UploadStatus.UPLOAD_SUCCEEDED; + } else { + result = UploadResult.SYNC_CONFLICT; } + } else if (code == RemoteOperationResult.ResultCode.LOCAL_FILE_NOT_FOUND) { + result = UploadResult.FILE_NOT_FOUND; } + + Log_OC.d(TAG, String.format( + "Upload Finished [%s] | RemoteCode: %s | internalResult: %s | FinalStatus: %s | Path: %s", + uploadResult.isSuccess() ? "✅" : "❌", + code, + result.name(), + status, + upload.getRemotePath())); + updateUploadStatus(upload.getOCUploadId(), status, result, upload.getRemotePath(), localPath); } /** diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index e1a8acbb3a22..a0cbcef60658 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -83,7 +83,9 @@ import java.nio.channels.FileLock; import java.nio.channels.OverlappingFileLockException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; @@ -1008,60 +1010,33 @@ private RemoteOperationResult checkConditions(File originalFile) { } private RemoteOperationResult normalUpload(OwnCloudClient client) { - RemoteOperationResult result = null; + RemoteOperationResult result = null; File temporalFile = null; File originalFile = new File(mOriginalStoragePath); File expectedFile = null; - FileLock fileLock = null; - FileChannel channel = null; - FileInputStream fileInputStream = null; - - long size; try { - // check conditions result = checkConditions(originalFile); + if (result != null) return result; - if (result != null) { - return result; - } - - // check name collision final var collisionResult = checkNameCollision(null, client, null, false); - if (collisionResult != null) { - result = collisionResult; - return collisionResult; - } + if (collisionResult != null) return collisionResult; String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); expectedFile = new File(expectedPath); result = copyFile(originalFile, expectedPath); - if (!result.isSuccess()) { - return result; - } + if (!result.isSuccess()) return result; // Get the last modification date of the file from the file system long lastModifiedTimestamp = originalFile.lastModified() / 1000; - final Long creationTimestamp = FileUtil.getCreationTimestamp(originalFile); - try { - fileInputStream = new FileInputStream(mFile.getStoragePath()); - channel = fileInputStream.getChannel(); - try { - // request a shared lock instead of exclusive one, since we are just reading file - fileLock = channel.tryLock(0L, Long.MAX_VALUE, true); - Log_OC.d(TAG ,"🔒" + "file locked"); - } catch (OverlappingFileLockException e) { - // if another thread has the lock, current thread can still read the file. - Log_OC.e(TAG, "shared lock overlap detected; proceeding safely."); - } - } catch (FileNotFoundException e) { - Log_OC.e(TAG, "file not found exception: normal upload"); + Path filePath = Paths.get(mFile.getStoragePath()); - // this basically means that the file is on SD card - // try to copy file to temporary dir if it doesn't exist + // file does not exists in storage + if (!Files.exists(filePath)) { + Log_OC.e(TAG, "file not found exception: normal upload, probably file in sd card"); String temporalPath = FileStorageUtils.getInternalTemporalPath(user.getAccountName(), mContext) + mFile.getRemotePath(); mFile.setStoragePath(temporalPath); @@ -1070,121 +1045,86 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { Files.deleteIfExists(Paths.get(temporalPath)); result = copy(originalFile, temporalFile); - if (result.isSuccess()) { - if (temporalFile.length() == originalFile.length()) { - try { - fileInputStream = new FileInputStream(temporalFile.getAbsolutePath()); - channel = fileInputStream.getChannel(); - fileLock = channel.tryLock(0L, Long.MAX_VALUE, true); - } catch (OverlappingFileLockException ex) { - Log_OC.e(TAG, "shared lock overlap detected; proceeding safely."); - } - } else { - result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); - } + if (!result.isSuccess()) return result; + + if (temporalFile.length() != originalFile.length()) { + return new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } + filePath = temporalFile.toPath(); } - try { - if (channel != null && channel.isOpen()) { + // file exists in storage + try (FileChannel channel = FileChannel.open(filePath, StandardOpenOption.READ)) { + FileLock fileLock = null; + try { + // request a shared lock instead of exclusive one, since we are just reading file + fileLock = channel.tryLock(0L, Long.MAX_VALUE, true); + Log_OC.d(TAG ,"🔒" + "file locked"); + } catch (OverlappingFileLockException e) { + Log_OC.e(TAG, "shared lock overlap detected; proceeding safely."); + } + + // determine size + long size; + try { size = channel.size(); + } catch (IOException e) { + size = Files.size(filePath); + } + updateSize(size); + + // decide whether chunked or not + if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { + boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); + mUploadOperation = new ChunkedFileUploadRemoteOperation( + mFile.getStoragePath(), mFile.getRemotePath(), mFile.getMimeType(), + mFile.getEtagInConflict(), lastModifiedTimestamp, creationTimestamp, + onWifiConnection, mDisableRetries); } else { - size = new File(mFile.getStoragePath()).length(); + mUploadOperation = new UploadFileRemoteOperation( + mFile.getStoragePath(), mFile.getRemotePath(), mFile.getMimeType(), + mFile.getEtagInConflict(), lastModifiedTimestamp, creationTimestamp, + mDisableRetries); } - } catch (Exception exception) { - Log_OC.e(TAG, "size cannot be determined from channel: " + exception); - size = new File(mFile.getStoragePath()).length(); - } - - updateSize(size); - // perform the upload - if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { - boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); - - mUploadOperation = new ChunkedFileUploadRemoteOperation(mFile.getStoragePath(), - mFile.getRemotePath(), - mFile.getMimeType(), - mFile.getEtagInConflict(), - lastModifiedTimestamp, - creationTimestamp, - onWifiConnection, - mDisableRetries); - } else { - mUploadOperation = new UploadFileRemoteOperation(mFile.getStoragePath(), - mFile.getRemotePath(), - mFile.getMimeType(), - mFile.getEtagInConflict(), - lastModifiedTimestamp, - creationTimestamp, - mDisableRetries); - } - - /** - * Adds the onTransferProgress in FileUploadWorker - * {@link FileUploadWorker#onTransferProgress(long, long, long, String)()} - */ - for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { - mUploadOperation.addDataTransferProgressListener(mDataTransferListener); - } + /** + * Adds the onTransferProgress in FileUploadWorker + * {@link FileUploadWorker#onTransferProgress(long, long, long, String)()} + */ + for (OnDatatransferProgressListener mDataTransferListener : mDataTransferListeners) { + mUploadOperation.addDataTransferProgressListener(mDataTransferListener); + } - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); - } + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } - if (result.isSuccess() && mUploadOperation != null) { + // Execute result = mUploadOperation.execute(client); - /// move local temporal file or original file to its corresponding + // move local temporal file or original file to its corresponding // location in the Nextcloud local folder if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { result = new RemoteOperationResult<>(ResultCode.SYNC_CONFLICT); } + + if (fileLock != null && fileLock.isValid()) { + fileLock.release(); + Log_OC.d(TAG ,"🔓" + "file lock released"); + } } } catch (FileNotFoundException e) { - Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); + Log_OC.e(TAG, mOriginalStoragePath + " not exists anymore"); result = new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (Exception e) { + Log_OC.e(TAG, "normal upload exception: ", e); result = new RemoteOperationResult<>(e); } finally { mUploadStarted.set(false); - if (fileLock != null && fileLock.isValid()) { - try { - fileLock.release(); - Log_OC.d(TAG ,"🔓" + "file lock released"); - } catch (IOException ignored) { - Log_OC.e(TAG, "failed to unlock file with path " + mOriginalStoragePath); - } - } else { - Log_OC.e(TAG, "file lock is null"); - } - - if (channel != null) { - try { - channel.close(); - Log_OC.d(TAG ,"📢" + "file channel closed"); - } catch (IOException ignored) { - Log_OC.e(TAG, "failed to close file channel"); - } - } else { - Log_OC.e(TAG, "channel is null"); - } - - if (fileInputStream != null) { - try { - fileInputStream.close(); - Log_OC.d(TAG ,"📝" + "file input stream closed"); - } catch (IOException ignored) { - Log_OC.e(TAG, "failed to close file input stream"); - } - } else { - Log_OC.e(TAG, "file input stream is null"); - } - - if (temporalFile != null && !originalFile.equals(temporalFile)) { - boolean isTempFileDeleted = temporalFile.delete(); - Log_OC.d(TAG, "temp folder deletion: " + isTempFileDeleted); + // Clean up temporal file if it exists + if (temporalFile != null && !temporalFile.delete() && temporalFile.exists()) { + Log_OC.e(TAG, "Could not delete temporal file"); } if (result == null) { @@ -1192,8 +1132,6 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } logResult(result, mOriginalStoragePath, mRemotePath); - - // update upload status uploadsStorageManager.updateDatabaseUploadResult(result, this); } @@ -1203,11 +1141,6 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); } - // delete temporal file - if (temporalFile != null && temporalFile.exists() && !temporalFile.delete()) { - Log_OC.e(TAG, "Could not delete temporal file " + temporalFile.getAbsolutePath()); - } - return result; } From 9b649e87ddffea3cf45fdabded3767bfd63ca3fd Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Feb 2026 09:32:15 +0100 Subject: [PATCH 53/83] assign result in failed scenarious Signed-off-by: alperozturk96 --- .../android/operations/UploadFileOperation.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index a0cbcef60658..c496b0bbc90c 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1017,16 +1017,23 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { try { result = checkConditions(originalFile); - if (result != null) return result; + if (result != null) { + return result; + } final var collisionResult = checkNameCollision(null, client, null, false); - if (collisionResult != null) return collisionResult; + if (collisionResult != null) { + result = collisionResult; + return collisionResult; + } String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); expectedFile = new File(expectedPath); result = copyFile(originalFile, expectedPath); - if (!result.isSuccess()) return result; + if (!result.isSuccess()) { + return result; + } // Get the last modification date of the file from the file system long lastModifiedTimestamp = originalFile.lastModified() / 1000; @@ -1048,7 +1055,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (!result.isSuccess()) return result; if (temporalFile.length() != originalFile.length()) { - return new RemoteOperationResult<>(ResultCode.LOCK_FAILED); + result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } filePath = temporalFile.toPath(); } From 9ac039046d23bce9096c5a1ee99e270808ce023b Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Feb 2026 11:25:46 +0100 Subject: [PATCH 54/83] cover edge cases Signed-off-by: alperozturk96 --- .../jobs/autoUpload/AutoUploadWorker.kt | 5 ++++ .../jobs/autoUpload/FileSystemRepository.kt | 8 ------ .../client/jobs/upload/FileUploadWorker.kt | 12 +++++++- .../UploadStorageManagerExtensions.kt | 28 +++++++++++++++++++ .../java/com/owncloud/android/MainApp.java | 2 +- .../datamodel/UploadsStorageManager.java | 2 ++ .../operations/UploadFileOperation.java | 21 ++++++++++---- 7 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 72bd41b6e7c2..01d78e1d9959 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -24,6 +24,7 @@ import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService import com.nextcloud.utils.extensions.isNonRetryable +import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.ArbitraryDataProviderImpl import com.owncloud.android.datamodel.FileDataStorageManager @@ -341,6 +342,10 @@ class AutoUploadWorker( sendUploadFinishEvent(operation, result) } } catch (e: Exception) { + uploadsStorageManager.updateStatus( + uploadEntity, + UploadsStorageManager.UploadStatus.UPLOAD_FAILED + ) Log_OC.e( TAG, "Exception during upload file, localPath: $localPath, remotePath: $remotePath," + diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt index d4804134f466..17c3bddb4806 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/FileSystemRepository.kt @@ -183,14 +183,6 @@ class FileSystemRepository( val entity = dao.getFileByPathAndFolder(localPath, syncedFolder.id.toString()) val fileModified = (lastModified ?: file.lastModified()) - if (fileModified <= 0L) { - Log_OC.d(TAG, "file is deleted, skipping: $localPath") - entity?.let { - deleteAutoUploadAndUploadEntity(syncedFolder, localPath, entity) - } - return - } - val hasNotChanged = entity?.fileModified == fileModified val fileSentForUpload = entity?.fileSentForUpload == 1 diff --git a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt index ddea5679e41e..e64ca25ca296 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/upload/FileUploadWorker.kt @@ -24,6 +24,7 @@ import com.nextcloud.client.network.ConnectivityService import com.nextcloud.client.preferences.AppPreferences import com.nextcloud.utils.ForegroundServiceHelper import com.nextcloud.utils.extensions.getPercent +import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R import com.owncloud.android.datamodel.FileDataStorageManager import com.owncloud.android.datamodel.ForegroundServiceType @@ -255,7 +256,7 @@ class FileUploadWorker( ) val result = withContext(Dispatchers.IO) { - upload(operation, user, client) + upload(upload, operation, user, client) } currentUploadFileOperation = null @@ -327,6 +328,7 @@ class FileUploadWorker( @Suppress("TooGenericExceptionCaught", "DEPRECATION") private suspend fun upload( + upload: OCUpload, operation: UploadFileOperation, user: User, client: OwnCloudClient @@ -343,6 +345,14 @@ class FileUploadWorker( fileUploadBroadcastManager.sendStarted(operation, context) } catch (e: Exception) { Log_OC.e(TAG, "Error uploading", e) + uploadsStorageManager.run { + uploadDao.getUploadById(upload.uploadId, user.accountName)?.let { entity -> + updateStatus( + entity, + UploadsStorageManager.UploadStatus.UPLOAD_FAILED + ) + } + } result = RemoteOperationResult(e) } finally { if (!isStopped) { diff --git a/app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt new file mode 100644 index 000000000000..e86a9f0f8ec3 --- /dev/null +++ b/app/src/main/java/com/nextcloud/utils/extensions/UploadStorageManagerExtensions.kt @@ -0,0 +1,28 @@ +/* + * Nextcloud - Android Client + * + * SPDX-FileCopyrightText: 2025 Alper Ozturk + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +package com.nextcloud.utils.extensions + +import com.nextcloud.client.database.entity.UploadEntity +import com.owncloud.android.datamodel.UploadsStorageManager + +fun UploadsStorageManager.updateStatus(entity: UploadEntity?, status: UploadsStorageManager.UploadStatus) { + entity ?: return + uploadDao.insertOrReplace(entity.withStatus(status)) +} + +fun UploadsStorageManager.updateStatus(entity: UploadEntity?, success: Boolean) { + entity ?: return + val newStatus = if (success) { + UploadsStorageManager.UploadStatus.UPLOAD_SUCCEEDED + } else { + UploadsStorageManager.UploadStatus.UPLOAD_FAILED + } + uploadDao.insertOrReplace(entity.withStatus(newStatus)) +} + +private fun UploadEntity.withStatus(newStatus: UploadsStorageManager.UploadStatus) = this.copy(status = newStatus.value) diff --git a/app/src/main/java/com/owncloud/android/MainApp.java b/app/src/main/java/com/owncloud/android/MainApp.java index 1b979f23a433..f46ed83f14e8 100644 --- a/app/src/main/java/com/owncloud/android/MainApp.java +++ b/app/src/main/java/com/owncloud/android/MainApp.java @@ -392,7 +392,7 @@ public void disableDocumentsStorageProvider() { FilesSyncHelper.startAutoUploadForEnabledSyncedFolders(syncedFolderProvider, backgroundJobManager, new String[]{}, - true); + false); preferences.setLastAutoUploadOnStartTime(System.currentTimeMillis()); } } else if (event == Lifecycle.Event.ON_STOP) { diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 41f8ea969674..492c20d943a8 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -582,6 +582,8 @@ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, Uploa result = UploadResult.SYNC_CONFLICT; } } else if (code == RemoteOperationResult.ResultCode.LOCAL_FILE_NOT_FOUND) { + // upload status is SUCCEEDED because user cannot take action about it, it will always fail + status = UploadStatus.UPLOAD_SUCCEEDED; result = UploadResult.FILE_NOT_FOUND; } diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index c496b0bbc90c..fc8513a5e7bd 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1106,8 +1106,10 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { throw new OperationCancelledException(); } - // Execute - result = mUploadOperation.execute(client); + // execute + if (result.isSuccess() && mUploadOperation != null) { + result = mUploadOperation.execute(client); + } // move local temporal file or original file to its corresponding // location in the Nextcloud local folder @@ -1129,11 +1131,20 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } finally { mUploadStarted.set(false); - // Clean up temporal file if it exists - if (temporalFile != null && !temporalFile.delete() && temporalFile.exists()) { - Log_OC.e(TAG, "Could not delete temporal file"); + // clean up temporal file if it exists + try { + if (temporalFile != null) { + if (temporalFile.exists() && !temporalFile.delete()) { + Log_OC.e(TAG, "Could not delete temporal file"); + } + } else { + Log_OC.e(TAG, "temporal file is null, cannot delete it"); + } + } catch (Exception e) { + Log_OC.e(TAG, "an exception occurred during deletion of temporal file: ", e); } + if (result == null) { result = new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); } From 9e64e9223d75a44026316cc4cda362a967fd0332 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Feb 2026 11:55:14 +0100 Subject: [PATCH 55/83] correct logs Signed-off-by: alperozturk96 --- .../com/owncloud/android/datamodel/UploadsStorageManager.java | 2 ++ .../com/owncloud/android/operations/UploadFileOperation.java | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index 492c20d943a8..244f87cecfe2 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -562,6 +562,8 @@ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, Uploa String localPath = (upload.getLocalBehaviour() == FileUploadWorker.LOCAL_BEHAVIOUR_MOVE) ? upload.getStoragePath() : null; + + Log_OC.d(TAG, "local behaviour: " + upload.getLocalBehaviour()); Log_OC.d(TAG, "local path of upload: " + localPath); UploadStatus status = UploadStatus.UPLOAD_FAILED; diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index fc8513a5e7bd..efbf642eb0c1 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1138,13 +1138,12 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { Log_OC.e(TAG, "Could not delete temporal file"); } } else { - Log_OC.e(TAG, "temporal file is null, cannot delete it"); + Log_OC.d(TAG, "temporal file is null - internal storage is used instead of sd-card"); } } catch (Exception e) { Log_OC.e(TAG, "an exception occurred during deletion of temporal file: ", e); } - if (result == null) { result = new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); } From 04818387561de0a751aed5a60a6c75a014fc4809 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Wed, 7 Jan 2026 07:53:39 +0100 Subject: [PATCH 56/83] Thumbnail: only generate locally if image use remoteId instead of localId Signed-off-by: tobiasKaminsky --- .../nextcloud/utils/extensions/OwnCloudClientExtensions.kt | 4 ++-- .../owncloud/android/datamodel/ThumbnailsCacheManager.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt index 0a28f83f7616..06e4ff1a9b11 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/OwnCloudClientExtensions.kt @@ -21,9 +21,9 @@ fun OwnCloudClient.toNextcloudClient(context: Context): NextcloudClient = OwnClo isFollowRedirects ) -fun OwnCloudClient.getPreviewEndpoint(localFileId: Long, x: Int, y: Int): String = baseUri +fun OwnCloudClient.getPreviewEndpoint(remoteId: String, x: Int, y: Int): String = baseUri .toString() + "/index.php/core/preview?fileId=" + - localFileId + + remoteId + "&x=" + (x / 2) + "&y=" + (y / 2) + "&a=1&mode=cover&forceIcon=0" diff --git a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java index 2213a77b7439..72cfadc35f03 100644 --- a/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java +++ b/app/src/main/java/com/owncloud/android/datamodel/ThumbnailsCacheManager.java @@ -1270,7 +1270,7 @@ public static Bitmap doResizedImageInBackground(OCFile file, FileDataStorageMana int pxW = p.x; int pxH = p.y; - if (file.isDown()) { + if (file.isDown() && MimeTypeUtil.isImage(file)) { Bitmap bitmap = BitmapUtils.decodeSampledBitmapFromFile(file.getStoragePath(), pxW, pxH); if (bitmap != null) { if (OCFileExtensionsKt.isPNG(file)) { @@ -1283,7 +1283,7 @@ public static Bitmap doResizedImageInBackground(OCFile file, FileDataStorageMana GetMethod getMethod = null; try { - String uri = OwnCloudClientExtensionsKt.getPreviewEndpoint(mClient, file.getLocalId(), pxW, pxH); + String uri = OwnCloudClientExtensionsKt.getPreviewEndpoint(mClient, file.getRemoteId(), pxW, pxH); Log_OC.d(TAG, "generating resized image: " + file.getFileName() + " URI: " + uri); getMethod = new GetMethod(uri); From 688298d1d30ff0432abec9e9786a096aa8e11d65 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 11 Feb 2026 13:05:34 +0100 Subject: [PATCH 57/83] chore: bump version to v3.36.0 RC2 Signed-off-by: alperozturk96 --- app/build.gradle.kts | 2 +- fastlane/metadata/android/en-US/changelogs/30360052.txt | 8 ++++++++ .../android/en-US/changelogs/30360052.txt.license | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/30360052.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/30360052.txt.license diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2c3f654276c7..f8a30e90b69f 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -65,7 +65,7 @@ configurations.configureEach { val versionMajor = 3 val versionMinor = 36 val versionPatch = 0 -val versionBuild = 51 // 0-50=Alpha / 51-98=RC / 90-99=stable +val versionBuild = 52 // 0-50=Alpha / 51-98=RC / 90-99=stable val ndkEnv = buildMap { file("${project.rootDir}/ndk.env").readLines().forEach { diff --git a/fastlane/metadata/android/en-US/changelogs/30360052.txt b/fastlane/metadata/android/en-US/changelogs/30360052.txt new file mode 100644 index 000000000000..3000245fd9c8 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30360052.txt @@ -0,0 +1,8 @@ +## 3.36.0 RC2 (February 11, 2026) + +- Auto upload fixes +- Bug fixes and performance improvements + +Minimum: NC 20 Server, Android 9 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/120 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/30360052.txt.license b/fastlane/metadata/android/en-US/changelogs/30360052.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/30360052.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file From d14709b5172690297f5cd1620c7c79e43d1c8251 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 12 Feb 2026 03:08:42 +0000 Subject: [PATCH 58/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-b+en+001/strings.xml | 1 - app/src/main/res/values-cs-rCZ/strings.xml | 1 - app/src/main/res/values-de/strings.xml | 1 - app/src/main/res/values-es/strings.xml | 62 ++++ app/src/main/res/values-et-rEE/strings.xml | 1 - app/src/main/res/values-fi-rFI/strings.xml | 1 - app/src/main/res/values-fr/strings.xml | 1 - app/src/main/res/values-ga/strings.xml | 1 - app/src/main/res/values-gl/strings.xml | 1 - app/src/main/res/values-hr/strings.xml | 361 ++++++++++++++++++- app/src/main/res/values-hu-rHU/strings.xml | 1 - app/src/main/res/values-in/strings.xml | 1 - app/src/main/res/values-it/strings.xml | 1 - app/src/main/res/values-ja-rJP/strings.xml | 1 - app/src/main/res/values-lo/strings.xml | 1 - app/src/main/res/values-pl/strings.xml | 10 +- app/src/main/res/values-pt-rBR/strings.xml | 1 - app/src/main/res/values-ru/strings.xml | 1 - app/src/main/res/values-sk-rSK/strings.xml | 1 - app/src/main/res/values-sr/strings.xml | 1 - app/src/main/res/values-sv/strings.xml | 1 - app/src/main/res/values-sw/strings.xml | 2 +- app/src/main/res/values-tr/strings.xml | 1 - app/src/main/res/values-ug/strings.xml | 1 - app/src/main/res/values-uk/strings.xml | 1 - app/src/main/res/values-zh-rCN/strings.xml | 1 - app/src/main/res/values-zh-rHK/strings.xml | 2 +- app/src/main/res/values-zh-rTW/strings.xml | 1 - 28 files changed, 433 insertions(+), 27 deletions(-) diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index 31114fb42401..08de0f690b24 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -224,7 +224,6 @@ Import failed to start. Please try again No file found Could not find your last backup! - Detecting content changes Failed to create conversation Delete conversation Failed to delete conversation diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 11270088bf41..fb1594a35a17 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -224,7 +224,6 @@ Import se nepodařilo spustit. Zkuste to znovu Nenalezen žádný soubor Nepodařilo se najít vaši nejaktuálnější zálohu! - Zjišťování změn obsahu Vytváření konverzace se nezdařilo Odstranit konverzaci Odstranění konverzace se nezdařilo diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f0f8bcb1bcf0..f1a29ffab7c0 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -224,7 +224,6 @@ Der Import konnte nicht gestartet werden. Bitte erneut versuchen Keine Datei gefunden Wir können Ihr letztes Backup nicht finden! - Erkennen von Inhaltsänderungen Unterhaltung konnte nicht erstellt werden Unterhaltung löschen Unterhaltung konnte nicht gelöscht werden diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index e2fae5794077..76d3fb24b8bb 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -13,6 +13,7 @@ Enviar/compartir Vista en cuadrícula Vista de lista + Acción ejecutada Restaurar los contactos y el calendario Nueva carpeta Mover o copiar @@ -45,21 +46,35 @@ Buscar en %s Aparecer como desconectado Esta salida fue generada mediante IA. Asegúrese de revisar de nuevo. + Fallo al mandar un mensaje + Fallo al cargar los mensajes del chat + Volver a la página de asistente ¿Está seguro de querer eliminar esta tarea? Eliminar tarea Intente enviar un mensaje para iniciar una conversación. ¡Hola!, ¿En qué te puedo ayudar hoy? + Selecciona una tarea + Enviar mensaje + Abrir la lista de chats Ocurrió un error al crear la tarea Se creó la tarea Ocurrió un error al eliminar la tarea + Tarea borrada + Lista de tareas vacía. Comprueba la configuración de la app de asistente. No se pudo obtener la lista de tareas, por favor, revise su conexión a internet. Eliminar tarea El resultado de la tarea no está listo aún. + Texto copiado desde otra app Asistente Entrada Salida + fallado funcionando + programado + exitosa + Estado de la tarea: %1$s desconocido + Pensando … ¡Cuenta asociada no encontrada! Acceso fallido: %1$s La cuenta no se ha añadido aún en este dispositivo @@ -96,10 +111,14 @@ Ha habido un problema al procesar su petición de acceso. Por favor, vuelva a intentarlo más tarde. No hay un navegador disponible para abrir este enlace. Por favor, complete el proceso de inicio de sesión en su navegador + Auto-Subida está pausada porque el Ahorro de Batería está activo. se mantiene en la carpeta original, pues es de solo lectura + Batería baja, la subida podría tomar más tiempo Subir sólo con conexión Wi-Fi sin límite de datos /CargaAutomática Esta carpeta ya está incluida en la sincronización de la carpeta principal, lo que puede provocar subidas duplicadas. + Esperando al Wi-Fi para iniciar la carga + Subiendo archivos desde %1$s a %2$s Configurar Crear nueva configuración de una carpeta especifica Configure una carpeta especifica @@ -205,9 +224,12 @@ La importación falló al iniciar. Por favor, intente de nuevo No se ha encontrado ningún archivo ¡No podemos encontrar tu última copia de seguridad! + Fallo al crear la conversación Borrar conversación + Fallo al borrar la conversación No se han encontrado conversaciones No hay conversaciones todavía + Fallo al cargar la lista de chats Conversaciones Copiado Se ha producido un error al tratar de copiar este archivo o carpeta @@ -309,9 +331,11 @@ El cifrado de extremo a extremo (E2E) todavía no está configurado No es posible sin conexión a internet La firma no coincide + No se pueden verificar los metadatos, la firma está vacía. Asistente Más Más apps de Nextcloud + No se puede abrir el selector de archivos Fallo al seleccionar dirección de correo. Establecer como cifrado No fue posible recuperar el certificado del servidor @@ -381,8 +405,10 @@ No tiene permisos para crear o cargar archivos en esta carpeta. Recursos compartidos externos Añadir o subir + Fallo al crear el diálogo de conflicto Error al enviar el fichero al gestor de descargas Fallo al imprimir el archivo + ¡Fallo al iniciar la acción! Fallo al inicial el editor Fallo al actualizar la UI Añadir a favoritos @@ -414,6 +440,8 @@ No se han encontrado resultados para su consulta Inicie su búsqueda Escriba en la barra de búsqueda más arriba para encontrar archivos, contactos, eventos de calendario, y más en toda su cuenta. + Comprueba tu conexión a internet o inténtalo de nuevo más tarde + Conexión pobre carpeta EN VIVO Cargando… @@ -475,6 +503,11 @@ La carpeta ya existe Esta carpeta se ve mejor en %1$s. Crear + %1$d de %2$d · %3$s + Ha ocurrido un error durante la sincronización de la carpeta %s + Espacio insuficiente la sincronización se ha cancelado + %s carpetas sincronizadas + Sincronizando… No hay carpetas aquí El nombre de la carpeta no puede estar vacío Seleccione @@ -608,6 +641,8 @@ Fallo al ejecutar la acción. Muestra notificaciones para interactuar con el resultado de las operaciones en segundo plano Operaciones en segundo plano + Detecta cambios en archivos locales + Vigilante del contenido Muestra el progreso de la descarga Descargas Muestra progreso y resultados de la sincronización de archivos @@ -631,6 +666,7 @@ No hay conexión a internet Incluso sin una conexión a internet, puede organizar sus carpetas o crear archivos. Una vez vuelva a estar en línea, las acciones pendientes se sincronizarán automáticamente. Está desconectado, pero el trabajo continúa + El archivo aún no existe. Sube el archivo primero por favor. No se pudo crear %s. Un archivo con el mismo nombre existe en el servidor. No se pudo crear %s. Una carpeta con el mismo nombre existe en el servidor. La operación sin conexión no se ha podido completar. %s @@ -646,6 +682,7 @@ Menú Introduce tu código de acceso Por favor introduzca su código de acceso + El código será requerido cada vez que se abra la app o se vuelva abrir tras 5 segundos. Los códigos de acceso no son iguales Por favor, vuelve a introducir tu código de acceso Borra tu código de acceso @@ -674,6 +711,8 @@ Renombrar la nueva versión ¿Cómo proceder si ya existe el archivo? Agregar cuenta + Permitir a la app acceder y administrar todos los archivos de tu dispositivo + Acceso a todos los archivos Sincronizar calendario y contactos Ni F-Droid ni Google Play están instalados Configurar DAVx⁵ (antes conocido como DAVdroid) (v1.3.0+) para la cuenta actual @@ -728,6 +767,7 @@ Tema Intervalo Administrar carpetas internas para sincronización bidireccional + Sincronizar de dos formas Oscuro Claro Seguir el del sistema @@ -910,6 +950,10 @@ Almacenamiento interno Películas Música + Acceso a todos los archivos + Se requiere el permiso de Almacenamiento para la Auto-Subida. + Se requiere el permiso de Almacenamiento para subir archivos. + No preguntar Medios en sólo lectura Imágenes La plataforma de productividad auto-hospedada que le mantiene en control.\n\nCaracterísticas;\n* Interfaz moderna y fácil de usar adecuada al tema de su servidor\n* Cargue archivos a su servidor Nextcloud\n* Comparta los mismos con otros\n* Mantenga sus archivos y carpetas favoritas sincronizados\n* Busque en todas las carpetas de su servidor\n* Carga automática para fotos y videos capturados en su dispositivo\n* Manténgase al día con notificaciones\n* Soporte para múltiples cuentas\n* Acceda a su información de forma segura usando su huella dactilar o PIN\n* Integración con DAVx5 (anteriormente conocido como DAVdroid) para configurar fácilmente la sincronización del calendario y contactos\n\nPor favor, reporte cualquier problema en https://github.com/nextcloud/android/issues y discuta acerca de esta aplicación en https://help.nextcloud.com/c/clients/android\n\n¿Es Ud. nuevo en Nextcloud? Nextcloud es un servidor privado para sincronizar y compartir archivos, y comunicación. Es un programa de uso libre, y lo puede hospedar Ud. mismo o pagar a una compañía para hacerlo por Ud. De esa manera, Ud. está en control de sus fotos, su calendario e información de sus contactos, sus documentos y todo lo demás.\n\nConozca Nextcloud en https://nextcloud.com @@ -930,6 +974,9 @@ Sugerir Sincronizar Sincronizar de todas formas + Resolver conflictos + Se han detectado conflictos en la carga. Abre para resolverlo. + Conflictos en la carga de archivos Se han encontrado conflictos La carpeta %1$s ya no existe Duplicación de sincronización @@ -968,6 +1015,12 @@ Miniatura para el archivo nuevo La carga está tardando más de lo esperado Hoy + Introduce el texto para traducir… + Traducir desde: + Traducir a: + Pulsa el botón para traducir + La traducción está tardando más de lo esperado. + Traduciendo… Archivos eliminados No hay archivos eliminados Podrás recuperar los archivos eliminados desde aquí. @@ -984,6 +1037,7 @@ Evento no encontrado, siempre puede sincronizar para actualizar. Redirigiendo a la web… Contacto no encontrado, siempre puede sincronizar para actualizar. Redirigiendo a la web… Se requieren permisos para abrir el resultado de la búsqueda. De otra forma, se redirigirá a la web… + En esta carpeta Desconocido Desbloquear archivo Hay comentarios no leídos @@ -1035,6 +1089,7 @@ Permisos de la App Sus archivos no pueden ser subidos sin acceso al almacenamiento local. Toque para conceder los permisos. Subida Detenida – Se Requieren Permisos para el Almacenamiento + Puedes borrar o continuar desde Cargas %1$d / %2$d - %3$s El cifrado solo es posible con >= Android 5.0 No hay suficiente espacio para copiar los archivos seleccionados en la carpeta %1$s. En su lugar, ¿le gustaría moverlos? @@ -1089,6 +1144,8 @@ El certificado del servidor no es de confianza Obteniendo la versión del servidor… La aplicación se ha interrumpido + Saltado + Un archivo con el mismo nombre ya existe. Completado Se encontró el mismo archivo en el remoto, omitiendo subida Error desconocido @@ -1192,6 +1249,11 @@ %d archivos exportados, el resto han sido omitidos por un error %d archivos exportados, el resto han sido omitidos por un error + + Puedes subir solo %d archivo a la vez. + Puedes subir hasta %d archivos a la vez. + Puedes subir hasta %d archivos a la vez. + %1$d carpeta %1$d carpetas diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index 6bbd30c1b708..f56aa4a53b35 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -223,7 +223,6 @@ Ei õnnestunud käivitada importimist. Palun proovi uuesti Faili ei leitud Sinu viimast varukoopiat ei leidu! - Tuvastan sisu muudatusi Vestluse loomine ei õnnestunud Kustuta vestlus Vestluse kustutamine ei õnnestunud diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index 804baf6398c0..d718f7fc2684 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -191,7 +191,6 @@ Tuonti on ajastettu ja se alkaa pian Tiedostoa ei löytynyt Viimeisintä varmuuskopiota ei löytynyt! - Havaitaan sisältömuutoksia Keskustelun luominen epäonnistui Poista keskustelu Keskustelun poistaminen epäonnistui diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index e42372a6d733..01fde446d933 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -214,7 +214,6 @@ L\'importation n\'a pas pu démarrer. Veuillez réessayer. Aucun fichier trouvé Impossible de trouver la dernière sauvegarde ! - Détection des modifications de contenu Échec de la conversation Supprimer la conversation Impossible de supprimer la conversation diff --git a/app/src/main/res/values-ga/strings.xml b/app/src/main/res/values-ga/strings.xml index 08053360eb1f..c79db0f8e7aa 100644 --- a/app/src/main/res/values-ga/strings.xml +++ b/app/src/main/res/values-ga/strings.xml @@ -224,7 +224,6 @@ Theip ar an iompórtáil. Bain triail eile as Níor aimsíodh aon chomhad Níorbh fhéidir do chúltaca deiridh a aimsiú! - Athruithe ábhair á mbrath Theip ar chomhrá a chruthú Scrios an comhrá Theip ar an gcomhrá a scriosadh diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index af7f03bde94a..17a860fb3e45 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -224,7 +224,6 @@ Importación fallou ao iniciar. Ténteo de novo Non se atopou ningún ficheiro Non foi posíbel atopar a súa última copia de seguranza! - Detección de cambios no contido Produciuse un fallo ao crear a conversa Eliminar a conversa Produciuse un fallo ao eliminar a conversa diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index b5bec22877cb..05f2981a03fd 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -13,6 +13,8 @@ Šalji/dijeli Prikaz rešetke Prikaz popisa + Radnja je pokrenuta + Vrati kontakte i kalendar Nova mapa Premjesti ili kopiraj Otvori s @@ -28,16 +30,51 @@ Pošalji poveznicu na... Aktivnost Dodaj novu poveznicu za javno dijeljenje + Dodaj novi sigurni prijenos datoteka Dodaj na %1$s Osnovni URL + Onemogući međuspremnik + Onemogući uvod + Onemogući zapisnik + Onemogući vanjske stranice + Onemogući više računa + Onemogući dijeljenje + Prisili zaštitu + Naziv proxy poslužitelja Proxy port + Prikazuje jedan widget s nadzorne ploče Traži u %s Prikaži se kao izvan mreže + Prikazani izlaz generirala je umjetna inteligencija. Obavezno ga uvijek provjerite. + Slanje poruke nije uspjelo + Dohvat poruka chata nije uspio + Natrag na stranicu asistenta + Jeste li sigurni da želite izbrisati ovaj zadatak? Izbriši zadatak + Pošaljite poruku kako biste započeli razgovor. + Bok! Kako vam mogu pomoći danas? + Odaberite zadatak + Pošalji poruku + Otvori popis razgovora + Došlo je do pogreške pri stvaranju zadatka Zadatak je stvoren + Došlo je do pogreške pri brisanju zadatka + Zadatak je izbrisan + Popis zadataka je prazan. Provjerite konfiguraciju aplikacije Asistent. + Nije moguće dohvatiti popis zadataka, provjerite internetsku vezu. + Izbriši zadatak + Izlaz zadatka još nije spreman. + Tekst je kopiran iz druge aplikacije Asistent + Ulaz + Izlaz + neuspješno radi + zakazano + uspješno + Status zadatka: %1$s nepoznato + Razmišljam … Pripadajući račun nije pronađen! Neuspjeli pristup: %1$s Račun još nije dodan na ovaj uređaj @@ -68,9 +105,20 @@ Nije moguće pronaći računalo %1$s ne podržava višestruke račune Nije moguće uspostaviti vezu + Odustani od prijave + Unesite valjanu adresu poslužitelja. + Nije moguće dohvatiti podatke za prijavu. Pokušajte ponovno. + Došlo je do problema pri obradi zahtjeva za prijavu. Pokušajte ponovno kasnije. + Nema dostupnog preglednika za otvaranje ove poveznice. + Dovršite postupak prijave u pregledniku + Automatski prijenos je pauziran jer je uključen štedni način rada baterije. nalazi se u izvornoj mapi jer je samo za čitanje + Slaba baterija, prijenos bi mogao potrajati dulje Otpremite samo putem nemjerene bežične (Wi-Fi) mreže /AutoUpload + Ova je mapa već uključena u sinkronizaciju nadređene mape, što može uzrokovati dvostruke prijenose + Čeka se Wi‑Fi za početak prijenosa + Prijenos datoteka iz %1$s u %2$s Konfiguriraj Stvorite novu postavku za prilagođenu mapu Postavite prilagođenu mapu @@ -79,6 +127,7 @@ Avatar Odsutan Postavke sigurnosne kopije + Sigurnosna kopija kontakata i kalendara Zatvori Onemogući Možda je omogućena optimizacija rada baterije na uređaju. Mogućnost automatskog otpremanja AutoUpload djeluje samo ako izuzmete ovu aplikaciju iz optimizacije baterije. @@ -93,11 +142,13 @@ Cancel Došlo je do poteškoća prilikom učitavanja vjerodajnice. Zapis promjena razvojne inačice + Provjerite kasnije ili ponovno učitajte. Potvrdni okvir Odaberite lokalnu mapu... Odaberite udaljenu mapu... Odaberite predložak i unesite naziv datoteke. Odaberite datoteku koju želite zadržati! + Odaberite widget Izbriši Brisanje obavijesti nije uspjelo. Izbriši status nakon @@ -150,25 +201,37 @@ Prijavite problem na GitHubu Nextcloud Asistent Konfiguriraj + Ukloni lokalno šifriranje Želite li zaista izbrisati %1$s? Želite li zaista izbrisati odabrane stavke? Želite li zaista izbrisati %1$s i pripadajući sadržaj? Želite li zaista izbrisati odabrane stavke i pripadajući sadržaj? Samo lokalno + Nije moguće stvoriti dijalog za rješavanje sukoba + Sukob mape Lokalna datoteka Ako odaberete obje inačice, lokalna će datoteka uz naziv imati i broj. + Ako odaberete obje verzije, lokalnoj će se mapi u naziv dodati broj. Datoteka na poslužitelju + Sigurnosna kopija kontakata + Potrebno je dopuštenje za kontakte. Ikona korisnika za popis kontakata Nema dopuštenja, ništa nije uvezeno. Kontakti Sigurnosno kopiraj Sigurnosno kopiranje je zakazano i počet će uskoro Uvoz je zakazan i počet će uskoro + Uvoz se nije uspio pokrenuti. Pokušajte ponovno Nije pronađena datoteka Nije moguće pronaći zadnju sigurnosnu kopiju! + Nije uspjelo stvaranje razgovora Obriši razgovor + Nije uspjelo brisanje razgovora Nije pronađen nijedan razgovor + Još nema razgovora + Nije uspio dohvat popisa razgovora Razgovori + Kopirano Došlo je do pogreške prilikom kopiranja ove datoteke ili mape Nije moguće kopirati mapu u jednu od svojih osnovnih mapa Datoteka je već prisutna u odredišnoj mapi @@ -180,12 +243,14 @@ Nije moguće dohvatiti URL Stvori Nije moguće stvoriti mapu + Stvori poveznicu Novo Novi dokument Nova mapa Nova prezentacija Nova proračunska tablica Dodaj opis mape + Dodaje opis mape Vjerodajnice onemogućene Svakodnevno sigurnosno kopiranje Podaci za sigurnosno kopiranje @@ -202,13 +267,28 @@ Provjera postojanja duplikata nije izvršena. Algoritam sažetka nije dostupan na vašem uređaju. Prijava putem izravne poveznice nije uspjela! + Prijavite se s %1$s na %2$s Onemogući Zanemari Zanemari obavijest + Prikazuje vašu šifru od 12 riječi Ne ometaj + Više slika + PDF datoteka + Odaberite vrstu izvoza + Stvaranje PDF-a nije uspjelo + Stvaranje PDF-a… + Ovdje nije moguće stvarati stavke: nedostaje dopuštenje za stvaranje. + Nije moguće stvoriti datoteku: nedostaje dopuštenje. + Nije moguće stvoriti mapu: nedostaje dopuštenje. + Nije moguće izbrisati stavku: nedostaje dopuštenje za brisanje. + Nije moguće premjestiti stavku: nedostaje dopuštenje za premještanje. + Nije moguće otvoriti stavku: nedostaje dopuštenje za čitanje. + Nije moguće preimenovati stavku: nedostaje dopuštenje za preimenovanje. Gotovo Ne briši Nije moguće stvoriti lokalnu datoteku + Neispravan naziv lokalne datoteke Preuzmi najnoviju razvojnu inačicu Ograničenje preuzimanja Nije moguće preuzeti %1$s @@ -219,7 +299,10 @@ Preuzimanje... %1$s je preuzeto Preuzeto + Korisnik je otkazao preuzimanje nekih datoteka + Došlo je do pogreške pri preuzimanju datoteka Nema preuzimanja + Došlo je do neočekivane pogreške pri preuzimanju datoteka Zatvori bočnu traku Zajednica Pozadinska slika zaglavlja ladice @@ -228,6 +311,7 @@ Asistent Favoriti Medij + Grupe mape Početna Obavijesti Na uređaju @@ -241,12 +325,25 @@ Iskorišteno %1$s od %2$s Upotrijebljeno %1$s Automatsko otpremanje + %s zbog previše pogrešnih pokušaja + Brojač je prestar + Hash nije pronađen + E2E još nije postavljen + Nije moguće bez internetske veze + Potpis se ne podudara + Nije moguće provjeriti metapodatke, potpis je prazan. Asistent Više + Više Nextcloud aplikacija + Nije moguće otvoriti odabir datoteke + Odabir adrese e‑pošte nije uspio. Postavi kao šifrirano + Nije moguće dohvatiti certifikat poslužitelja + Neuspjela provjera javnog ključa Postavi šifriranje Dešifriranje u tijeku... Zatvori + Unesite svoju šifrirnu frazu za pristup datotekama Ova mapa nije prazna. Generiranje novih ključeva... Svih 12 riječi zajedno čine vrlo složenu zaporku koja samo vama omogućuje da pregledavate i koristite se šifriranim datotekama. Zapišite je i čuvajte na sigurnom mjestu. @@ -254,8 +351,11 @@ Zabilježite zaporku za šifriranje od 12 riječi Zaporka… Dohvaćanje ključeva... + Nije moguće dohvatiti privatni ključ + Nije moguće dohvatiti javni ključ Pohranjivanje ključeva Postavi šifriranje + Došlo je do neočekivane pogreške pri preuzimanju ključeva Spremanje ključeva nije uspjelo, pokušajte ponovno. Došlo je do pogreške tijekom dešifriranja. Pogrešna zaporka? Unesite naziv odredišne datoteke @@ -266,13 +366,21 @@ Pogreška pri komentiranju datoteke Ispad %1$s Pogreška kod stvaranja datoteke iz predloška + Nije moguće dohvatiti primatelje dijeljenja. + Pogreška pri prikazu radnji nad datotekom Greška kod promjene statusa zaključavanja datoteke Prijavi Prijaviti grešku razvojnom timu? (potreban GitHub korisnički račun) Pogreška pri dohvaćanju datoteke Pogreška pri dohvaćanju predložaka + Pogreška pri postavljanju poruke statusa! + Pogreška pri prikazu dijaloga postavljanja šifriranja! Pogreška pri pokretanju kamere + Pogreška pri pokretanju skeniranja dokumenta + Neuspješan prijenos snimljenog medija Računi + Broj pokretanja u 48 h + Stvoreno Naziv zadatka Napredak Stanje @@ -286,6 +394,7 @@ Zaustavi ispitni zadatak Migracije (nadogradnja aplikacije) Preferencije + Inženjerski testni način rada Prijenos datoteka Stavi testno preuzimanje u red čekanja Stavi testno otpremanje u red čekanja @@ -293,18 +402,24 @@ Prijenos Preuzmi Otpremi + Nemate dopuštenje za stvaranje ili prijenos datoteka u ovoj mapi. Vanjska dijeljenja Dodaj ili otpremi + Nije uspjelo stvaranje dijaloga sukoba Neuspješno prosljeđivanje datoteke upravitelju preuzimanja Ispisivanje datoteke nije uspjelo + Nije uspjelo pokretanje radnje! Nije uspjelo pokretanje uređivača Ažuriranje korisničkog sučelja nije uspjelo Dodaj u favorite Favorit 15 minuta + Dijeljenu datoteku nije moguće ažurirati Datoteka tog naziva već postoji Izbriši Pogreška pri dohvaćanju aktivnosti za datoteku + Ne možete stvoriti dijeljenje; dijeljenje je već aktivno od ovog korisnika. + Nema dostupne aplikacije za odabir kontakata Učitavanje pojedinosti nije uspjelo Datoteka Zadrži @@ -315,6 +430,7 @@ Ovdje nema datoteka Nema rezultata u ovoj mapi Nema rezultata + Nema datoteka ili mapa koje odgovaraju pretraživanju Ovdje nema ničega. Možete dodati mapu. Ovdje će se prikazati preuzete datoteke i mape. Nije pronađena nijedna datoteka izmijenjena u posljednjih 7 dana @@ -322,6 +438,10 @@ Ovdje će se prikazati datoteke i mape koje dijelite. Još ništa nije dijeljeno Nema rezultata za vaš upit + Započnite pretraživanje + Upišite u traku za pretraživanje iznad kako biste pronašli datoteke, kontakte, događaje u kalendaru i još mnogo toga na svom računu. + Provjerite internetsku vezu ili pokušajte ponovno kasnije + Loša veza mapa UŽIVO Učitavanje… @@ -329,6 +449,7 @@ prije nekoliko sekundi Dopuštenja za pohranu %1$sradi najbolje s dozvoljenim pristupom prostoru za pohranu. Možete odabrati potpuni pristup svim datotekama ili samo mogućnost \"čitanja\" za fotografije i video materijale. + Dopusti pristup iz drugih aplikacija Provjera odredišta... Čišćenje… Ažuriranje mape za pohranu datoteka @@ -339,6 +460,8 @@ Nije moguće pisati u odredišnu datoteku Neuspjeh tijekom migracije Neuspješno ažuriranje indeksa + %1$s +(%2$s / %3$s) Premještanje podataka... Završeno Zamijeni @@ -350,9 +473,19 @@ Ažuriranje indeksa... Koristi Čeka se potpuna sinkronizacija... + Trenutačni naziv mape nije valjan, preimenujte mapu. Preusmjeravanje na početnu… + Putanja mape sadrži rezervirane nazive ili neispravne znakove + %s je zabranjena ekstenzija datoteke + Nazivi datoteka ne smiju sadržavati razmake na početku ili kraju + Naziv sadrži neispravne znakove: %s + %s je zabranjen naziv + %s. Preimenujte datoteku prije premještanja ili kopiranja Datoteka nije pronađena + Datoteka nije pronađena. Nije moguće stvoriti dijeljenje. Datoteku nije moguće sinkronizirati. Prikazana je posljednja dostupna inačica. Preimenuj + Prijenos nije uspio. Nema internetske veze + %s već postoji, nije otkriven sukob Pogreška pri vraćanju inačice datoteke! Pojedinosti Preuzmi @@ -365,9 +498,19 @@ Naziv datoteke sadrži barem jedan nevažeći znak Naziv datoteke Čuvajte svoje podatke sigurnim i pod kontrolom + Sigurna suradnja i razmjena datoteka + Jednostavan webmail, kalendar i kontakti + Dijeljenje zaslona, online sastanci i web konferencije Mapa već postoji + Ovu mapu najbolje je pregledavati u %1$s. Stvori + %1$d od %2$d · %3$s + Došlo je do pogreške tijekom sinkronizacije mape %s + Nedovoljno prostora na disku, sinkronizacija je otkazana + Mapa %s je sinkronizirana + Sinkronizacija… Ovdje nema mapa + Naziv mape ne može biti prazan Odaberite Odaberi ciljnu mapu Kopirajte @@ -385,22 +528,44 @@ Udaljeno: %1$s Naprijed 4 sata + Google je ograničio preuzimanje APK/AAB datoteka! + Ova ikona označava dostupnost live fotografije Datoteka s ovakvim nazivom će biti skrivena Naziv Bilješka Zaporka Poslužitelj nije dostupan Postavi vlastiti poslužitelj + Ikona za prazan popis + Ikona widgeta nadzorne ploče + Ikona stavke widgeta + uređeno + Okreni vodoravno + Okreni okomito + Rotiraj suprotno od kazaljke na satu + Rotiraj u smjeru kazaljke na satu + Nije moguće urediti sliku. + Detalji datoteke + Uvjeti snimanja slike + ƒ/%s + ISO %s + %s MP + %s mm + %s s Također otpremi postojeće datoteke Otpremanje samo tijekom punjenja /InstantUpload Unutarnja dijeljenja + Interna dvosmjerna sinkronizacija + Još ne, uskoro će se sinkronizirati + Za postavljanje šifrirane mape potrebna je internetska veza Neispravan URL Nevidljiva Oznaka ne može biti prazna Zadnja sigurnosna kopija: %1$s Poveznica Naziv pozivnice + Poveznica nije otvorena zbog sigurnosnih postavki. Uređivanje Navedeni izgled Učitaj više rezultata @@ -460,11 +625,14 @@ Otkrivena je nova medijska mapa %1$s. fotografija videozapis + Nova obavijest Stvorena je nova inačica Nema radnji za ovog korisnika Nema dostupnih aplikacija za upravljanje poveznicama Ne postoji kalendar Nema dostupnih aplikacija za upravljanje adresama e-pošte + Nema stavki + Nema dostupne aplikacije za karte Dopušten je samo jedan račun Nema dostupnih aplikacija za upravljanje PDF datotekama Aplikacija za slanje odabranih datoteka ne postoji na uređaju ili nije dostupna @@ -472,6 +640,10 @@ Nije moguće poslati bilješku Ikona bilješke Izvršavanje radnje nije uspjelo. + Prikazuj obavijesti za rezultate pozadinskih operacija + Pozadinske operacije + Prikazuj obavijesti o promjenama sadržaja + Promjene sadržaja Prikazuje napredak preuzimanja Preuzimanja Prikazuje napredak i rezultate sinkronizacije datoteka @@ -480,21 +652,38 @@ Opće obavijesti Napredak alata za reprodukciju glazbe Alat za reprodukciju medijskih datoteka + Prikazuj obavijesti za izvanmrežne operacije + Izvanmrežne operacije Prikazuje push obavijesti koje šalje poslužitelj: navode u komentarima, primanje novih udaljenih dijeljenja, obavijesti administratora itd. Push obavijesti Prikazuje napredak otpreme Otpreme Ikona obavijesti + Ikona obavijesti Nema obavijesti Provjerite kasnije. + Na čekanju za izvanmrežnu obradu + Uklonit će se kad se ponovno povežete Nema internetske veze + U izvanmrežnom načinu rada dostupne su samo datoteke spremljene za izvanmrežnu upotrebu. Promjene će se sinkronizirati kada se ponovno povežete. + Izvanmrežni način rada + Datoteka još ne postoji + Sukob pri stvaranju datoteke + Sukob pri stvaranju mape + Pogreška pri izvanmrežnim operacijama + Izvanmrežne operacije… + Sukob pri uklanjanju + Sukob pri preimenovanju + Obrada izvanmrežnih promjena 1 sat Na mreži Status na mreži + Otvori u aplikaciji Poslužitelj je na kraju radnog vijeka, nadogradite ga! Više izbornika Unesite zaporku Unesite zaporku + Postavite šifru za zaštitu aplikacije. Zaporke se ne podudaraju Ponovno unesite zaporku Izbrišite zaporku @@ -502,12 +691,16 @@ Zaporka je spremljena Neispravna zaporka Pauza + PDF je zaštićen lozinkom Dopusti Spriječi + Odaberite kontakt za dijeljenje Nije pronađena nijedna aplikacija za postavljanje slike s + Prikvači na početni zaslon Otvori %1$s zaustavi uključi/isključi + Odaberite poslužitelj Onemogućivanje provjere uštede energije može rezultirati otpremanjem datoteka pri niskoj razini napunjenosti baterije! izbrisano spremljeno u izvornoj mapi @@ -519,7 +712,12 @@ Preimenuj novu inačicu Što učiniti ako datoteka već postoji? Dodaj račun + Omogućuje aplikaciji pristup svim datotekama na uređaju (prema Androidovim pravilima). + Pristup svim datotekama + Kalendar i kontakti Nisu instalirani F-Droid ni Google Play + Postavke sinkronizacije kalendara i kontakata + Postavljanje sinkronizacije je uspješno Informacije Pojedinosti Dev @@ -527,7 +725,12 @@ Općenito Više Sinkronizacija + Stvaraj sigurnosnu kopiju jednom dnevno Svakodnevno sigurnosno kopiranje vaših kontakata + Lokacija pohrane podataka + Odaberite gdje će aplikacija spremati svoje podatke + Neuspjelo postavljanje DAVx⁵ + Šifriranje s kraja na kraj je aktivno E2E mnemonička oznaka Omogućite vjerodajnice uređaja kako biste prikazali mnemoničku oznaku. Prikaži obavijesti o skeniranju medija @@ -537,7 +740,12 @@ Bilješke o izdanju Izvorna datoteka bit će... Izvorna datoteka bit će... + Ne prenosi skrivene datoteke i mape + Preskoči skrivene stavke + Organiziraj prijenose po podmapama prema datumu Koristi podmape + Pravilo podmape za automatski prijenos + Ključevi već postoje Licenca Zaporka aplikacije Omogućene su vjerodajnice uređaja @@ -548,32 +756,49 @@ Zaporka Upravljaj računima Preporuči prijatelju + Ukloni šifriranje s kraja na kraj + Postavi šifriranje s kraja na kraj + Prikaži aplikacije ekosustava + Prikaži preporučene aplikacije iz Nextcloud ekosustava Prikaz skrivenih datoteka Preuzmi izvorni kod Upravljanje mapama za automatsko otpremanje Lokalna mapa Udaljena mapa Tema + Interval dvosmjerne sinkronizacije + Sinkroniziraj promjene na uređaju i na poslužitelju + Dvosmjerna sinkronizacija Tamno Svijetlo Prati sustav Pretpregled slike + Preuzimanje slike za uređivanje… Nema lokalne datoteke za pretpregled Nije moguće prikazati sliku + Datoteka nije preuzeta + Poslužitelj je vratio neočekivani HTTP kod. Oprosti Privatnost Push obavijesti su onemogućene zbog ovisnosti o vlasničkim servisima trgovine Google Play. Nema push obavijesti zbog zastarjele prijave. Razmislite o ponovnom dodavanju računa. Push obavijesti trenutno nisu dostupne. Čitanje QR kôda nije uspjelo! + Mapa za sinkronizaciju ne postoji. Stvorite je pa pokušajte ponovno. + Nije moguće pronaći datoteku za prijenos. Isprobajte %1$s na uređaju! Želim vas pozvati da koristite %1$s na svom uređaju.\nPreuzmite ovdje: %2$s %1$s ili %2$s Preporučene datoteke + Osvježi sadržaj Ponovno učitaj (na daljinu) Datoteka nije pronađena! + Ukloni E2E šifriranje + Ovo će ukloniti lokalne podatke za šifriranje s kraja na kraj na ovom uređaju. Šifrirane datoteke na poslužitelju neće biti izbrisane. Brisanje nije uspjelo + Ukloni lokalni račun + Ovo će ukloniti račun s ovog uređaja. Datoteke na poslužitelju ostaju netaknute. Nije uspjelo uklanjanje obavijesti. Ukloni Izbrisano @@ -581,6 +806,9 @@ Nije moguće preimenovati lokalnu kopiju, pokušajte s drugim nazivom Preimenovanje nije moguće, naziv je već zauzet Zatraži brisanje računa + Zatraži brisanje računa + Poslat ćemo zahtjev za brisanje vašeg računa administratoru poslužitelja. + Ponovno dijeljenje nije dopušteno Vrati datoteku Vrati sigurnosnu kopiju Vrati izbrisanu datoteku @@ -590,6 +818,7 @@ Pokušaj ponovno Učitavanje dokumenta nije uspjelo! Prijavite se putem QR koda + Skeniraj stranicu Zaštita podataka samopostavljeno pospješivanje produktivnosti Pretražujte i dijelite @@ -599,17 +828,34 @@ Svi vaši računi na jednom mjestu Automatsko otpremanje + Automatski prenesite fotografije i videozapise u pozadini. + Sinkronizirajte kontakte i kalendar + Povežite kontakte i kalendar s uređajem putem DAVx⁵. Pogreška pri dohvaćanju rezultata pretraživanja + Sigurno dijeljenje nije postavljeno + Pretraži za sigurno dijeljenje Odaberi sve Podesi mapu za medijske datoteke Odaberite jedan predložak Odaberi predložak Pošalji + Pošalji kopiju u + Pošalji dijeljenje Ikona gumba za slanje + Poslužitelj nije dostupan + Nije moguće uspostaviti vezu s poslužiteljem. Provjerite internetsku vezu i pokušajte ponovno. Postavi kao + Neuspjelo postavljanje ograničenja preuzimanja + Postavi poruku + Postavi napomenu + Postavi online status + Nije moguće dohvatiti ili postaviti status. Provjerite vezu s poslužiteljem. Koristi sliku kao + Postavi E2E šifriranje Dijeli Dopusti preuzimanje i sinkronizaciju + Napomena ne može biti prazna + Kopiraj poveznicu Stvori Prilagođena dopuštenja Izbriši @@ -633,13 +879,19 @@ Dijeli poveznicu (%1$s) Postavi datum isteka Postavi zaporku + Dijeljenje nije dopušteno kada je uključeno primanje datoteka + Potrebno je odabrati opciju dijeljenja Uređivanje moguće Zahtjev za datotekom + Siguran prijenos datoteka Samo za gledanje + Dopuštenja dijeljenja Dijeli Čitaj %1$s (udaljeno) %1$s (razgovor) + Pretraži korisnike ili grupe + Pretraži unutar instance Pošalji novu poruku e-pošte Obavijest primatelju Postavke @@ -653,10 +905,12 @@ dijeljeno putem poveznice S vama podijelio %1$s Dodavanje osobe za dijeljenje nije uspjelo + Ovaj je primatelj već dodan Prikaži sve Prikaži fotografije Prikaži manje Prikaži video + Neuspjelo prihvaćanje uvjeta korištenja Prijavite se kod davatelja usluga Dopusti %1$s pristup Nextcloud računu %2$s? Razvrstaj prema @@ -697,23 +951,36 @@ Unutarnja pohrana Filmovi Glazba + Dopusti pristup svim datotekama + Za automatski prijenos potrebna su dopuštenja za pristup datotekama. + Za prijenos datoteka potrebna su dopuštenja za pristup datotekama. + Ne pitaj ponovno Medijske datoteke samo za čitanje Slike + Nextcloud za Android: sigurna suradnja, razmjena datoteka i sinkronizacija s vašim poslužiteljem. Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu.\nTo je službena razvojna inačica koja se isporučuje s dnevnim uzorkom svake nove neispitane funkcionalnosti, što može uzrokovati nestabilnost i gubitak podataka. Aplikacija je namijenjena korisnicima koji žele testirati i prijaviti pogreške, ako se iste pojave. Nemojte je upotrebljavati za važne radne zadatke!\n\nSlužbena razvojna i uobičajena inačica dostupne su putem F-droida i mogu biti istovremeno instalirane. Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu (razvojna inačica za pretpregled) Strujanje s... Unutarnje strujanje nije moguće Umjesto toga preuzmite medijske datoteke ili se koristite vanjskom aplikacijom. + Po danu + Po mjesecu Godina \"%1$s\" je dijeljen s vama %1$s dijeli \"%2$s\" s vama Samo fotografije + Fotografije i videozapisi Samo video materijali Predloži Sinkronizacija + Svejedno sinkroniziraj + Riješi sukob + Neke datoteke imaju sukob. Odaberite verziju koju želite zadržati. + Sukob pri sinkronizaciji Postoje nepodudaranja Mapa %1$s više ne postoji + Duplikati pri sinkronizaciji Nije moguće sinkronizirati %1$s Pogrešna zaporka za %1$s Značajka trajne sinkronizacije datoteka nije uspjela @@ -730,6 +997,7 @@ Nema dovoljno prostora Gumb statusa sinkronizacije Datoteke + U redu Gumb postavki Konfiguriraj mape Trenutačno otpremanje datoteka potpuno je obnovljeno. Ponovno konfigurirajte automatsko otpremanje iz glavnog izbornika.\n\nUživajte u novoj i proširenoj značajci automatskog otpremanja datoteka. @@ -739,13 +1007,21 @@ Sinkronizirano Oznake Uvjeti pružanja usluge + Slažem se Testirajte vezu s poslužiteljem 30 minuta Ovaj tjedan Umanjeni prikaz Umanjeni prikaz postojeće datoteke Umanjeni prikaz nove datoteke + Istek vremena pri povezivanju s Richdocuments Danas + Unesite tekst za prijevod + S + Na + Započni prijevod + Obrada zadatka… + Prevođenje… Izbrisane datoteke Nema izbrisanih datoteka Ovdje ćete moći vratiti izbrisane datoteke. @@ -754,11 +1030,21 @@ Trajno izbrišite Učitavanje kante za smeće nije uspjelo! Datoteke nije moguće trajno izbrisati! + Onemogući sve + Nema stavki za dvosmjernu sinkronizaciju. + Nema stavki + Dvosmjerna sinkronizacija Došlo je do neočekivane pogreške + Događaj u kalendaru nije pronađen + Kontakt nije pronađen + Potrebno je dopuštenje + Pretraži u ovoj mapi Nepoznata pogreška Otključaj datoteku Postoje nepročitani komentari Ukloni šifriranje + Ukloni iz omiljenih + Isključi internu dvosmjernu sinkronizaciju Došlo je do pogreške prilikom prestanka dijeljenja ove datoteke ili mape. Nije moguće poništiti dijeljenje. Provjerite postoji li datoteka. kako biste poništili dijeljenje ove datoteke @@ -768,12 +1054,18 @@ Nije moguće ažurirati. Provjerite postoji li datoteka. za ažuriranje ovog dijeljenja Ažuriranje dijeljenja nije uspjelo + Očisti otkazano + Nastavi otkazano Izbriši neuspješne otpreme Ponovno pokušaj neuspjele otpreme + Datoteka više ne postoji. + Pauziraj sve prijenose + Nastavi sve prijenose Nije moguće stvoriti lokalnu datoteku Otpremi iz... Otpremi sadržaj iz drugih aplikacija Fotografija + Prenijeti ovu datoteku sada? Otpremanje iz kamere Videozapis Naziv datoteke @@ -783,17 +1075,26 @@ Tekstna datoteka isječka (.txt) Unesite naziv datoteke i vrstu datoteke za otpremanje Otpremi datoteke + Prijenosi su pauzirani Gumb za otpremanje stavke Izbriši Nema dostupnih otprema Otpremite neki sadržaj ili aktivirajte automatsko otpremanje. + Prikaži više Neriješeno nepodudaranje Lokalni prostor za pohranu je pun Datoteku nije moguće kopirati u lokalni prostor za pohranu Zaključavanje mape nije uspjelo Korisnik je prekinuo prilaganje + Dopusti pristup datotekama + Postavke dopuštenja aplikacije + Za prijenos je potrebno dopustiti pristup datotekama na uređaju. + Nedostaje dopuštenje za pohranu + Otvori status prijenosa + Prijenos datoteka… Šifriranje je moguće samo na inačici >= Androida 5.0 Nedostatak prostora sprječava kopiranje odabranih datoteka u mapu %1$s. Želite li ih premjestiti tamo? + Kvote je premašena Skeniraj dokument pomoću kamere Nepodudaranje tijekom sinkronizacije, ručno otklonite poteškoću Nepoznata pogreška @@ -805,6 +1106,8 @@ Datoteka odabrana za otpremanje nije pronađena. Provjerite postoji li datoteka. Ovu datoteku nije moguće otpremiti Nema datoteke za otpremu + Datoteka nije pronađena na uređaju. + Datoteka nije pronađena na poslužitelju. Naziv mape Odaberi mapu za otpremu Otpremanje %1$s nije uspjelo @@ -842,7 +1145,10 @@ Nepouzdana vjerodajnica poslužitelja Dohvaćanje inačice poslužitelja... Prekinut rad aplikacije + Preskočeno + Razlog preskakanja Završeno + Uspješno (datoteka je već postojala) Nepoznata pogreška Otkriven je virus. Nije moguće dovršiti otpremanje! Čekanje izlaza iz načina uštede energije @@ -859,13 +1165,16 @@ Dodajte ime, sliku i kontaktne podatke na stranicu profila. Korisničko ime Preuzmi + Ikona preklopa za video Pričekajte… U tijeku je provjera spremljenih vjerodajnica Kopiranje datoteke iz osobnog podatkovnog prostora + Promjena ekstenzije može učiniti datoteku neupotrebljivom. Što je nova slika Preskoči Novo u %1$s Koji je vaš status? + Widgeti nisu dostupni Nije dostupno Preuzimanje datoteka... Pošalji poruku e-pošte @@ -881,6 +1190,21 @@ %d minute %d minuta + + prije %d sekundu + prije %d sekunde + prije %d sekundi + + + prije %d minutu + prije %d minute + prije %d minuta + + + prije %d sat + prije %d sata + prije %d sati + Nije moguće sinkronizirati datoteku %1$d (nepodudaranja: %2$d) Nije moguće sinkronizirati datoteke %1$d (nepodudaranja: %2$d) @@ -926,6 +1250,11 @@ Broj izvezenih datoteka: %d. Ostale su preskočene zbog greške Broj izvezenih datoteka: %d. Ostale su preskočene zbog greške + + Možete prenijeti najviše %d datoteku odjednom. + Možete prenijeti najviše %d datoteke odjednom. + Možete prenijeti najviše %d datoteka odjednom. + Mapa %1$d Mape %1$d @@ -936,6 +1265,11 @@ Datoteke %1$d Datoteke %1$d + + %d datoteka + %d datoteke + %d datoteka + Prikaži %1$d skrivenu mapu Prikaži %1$d skrivene mape @@ -946,4 +1280,29 @@ Odabrano je %d Odabrano je %d - + + Pričekajte %d sekundu + Pričekajte %d sekunde + Pričekajte %d sekundi + + + Pričekajte %d minutu + Pričekajte %d minute + Pričekajte %d minuta + + + %d minuta + %d minute + %d minuta + + + %d sekunda + %d sekunde + %d sekundi + + + Ova poveznica dopušta još %d preuzimanje. + Ova poveznica dopušta još %d preuzimanja. + Ova poveznica dopušta još %d preuzimanja. + + diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index 1ed29a1c93d8..95a05e07d968 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -222,7 +222,6 @@ Az importálás elkezdése sikertelen. Próbálja újra. Nincs fájl A legutóbbi biztonsági mentés nem található! - Tartalmi változások észlelése Nem sikerült létrehozni a beszélgetést Beszélgetés törlése Nem sikerült a beszélgetés törlése diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 9aa2f940fd93..19727d8834f7 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -208,7 +208,6 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Impor gagal dimulai. Coba lagi Tidak ada berkas ditemukan. Tidak dapat menemukan pencadangan terakhir kamu! - Mendeteksi perubahan konten Belum ada percakapan Tersalin Terjadi kesalahan ketika mencoba menyalin berkas atau folder ini diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index ff6b2b854a14..6dba7ab5a402 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -212,7 +212,6 @@ Impossibile avviare l\'importazione. Riprova. Nessun file trovato Non troviamo il tuo ultimo backup! - Rilevamento delle modifiche ai contenuti Impossibile creare conversazione Elimina conversazione Impossibile eliminare la conversazione diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 8a109738a20b..bb88bba210d1 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -208,7 +208,6 @@ インポートを開始できませんでした。もう一度やり直してください ファイルが見つかりません あなたの最後のバックアップを見つけることができませんでした! - コンテンツの変更の検出 会話が見つかりません まだ会話はありません 会話 diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 630fb36edbc5..80fd5d432f6e 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -210,7 +210,6 @@ Import failed to start. Please try again ບໍ່ພົບຟາຍ ບໍ່ສາມາດຊອກຫາ ການສໍາຮອງລ່າສຸດຂອງທ່ານໄດ້! - Detecting content changes Delete conversation No conversations found ຍັງບໍ່ມີການສົນທະນາ diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 9d6026c020d3..5d90c39ba71c 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -53,6 +53,7 @@ Usuń zadanie Spróbuj wysłać wiadomość, aby rozpocząć rozmowę. Cześć! W czym mogę Ci dziś pomóc? + Wybierz zadanie Wyślij wiadomość Otwórz listę rozmowy Wystąpił błąd podczas tworzenia zadania @@ -117,6 +118,7 @@ /AutoUpload Ten katalog jest już uwzględniony w synchronizacji katalogu nadrzędnego, co może powodować duplikowanie wysyłanych plików Oczekiwanie na Wi-Fi, aby zacząć aktualizowanie + Przesyłanie plików z %1$s do %2$s Konfiguruj Utwórz nową niestandardową konfigurację katalogu Ustaw własny katalog @@ -222,7 +224,6 @@ Nie udało się rozpocząć importu. Spróbuj ponownie Nie znaleziono żadnych plików Nie odnaleziono Twojej ostatniej kopii zapasowej! - Sprawdzanie zmian treści Nie udało się utworzyć rozmowy Usunąć rozmowę Nie udało się usunąć rozmowy @@ -1014,6 +1015,12 @@ Miniaturka dla nowego pliku Ładowanie trwa dłużej niż oczekiwano Dzisiaj + Wpisz tekst do przetłumaczenia... + Tłumacz z: + Tłumacz na: + Naciśnij przycisk, aby przetłumaczyć + Tłumaczenie trwa dłużej niż oczekiwano. + Tłumaczenie... Usunięte pliki Brak usuniętych plików Tutaj będziesz mógł odzyskać usunięte pliki. @@ -1082,6 +1089,7 @@ Uprawnienia aplikacji Twoje pliki nie mogą być przesłane bez dostępu do pamięci lokalnej. Kliknij, aby udzielić pozwolenia. Przesyłanie zatrzymane – wymagane uprawnienie do pamięci + Możesz usunąć lub wznowić w sekcji Przesyłane pliki %1$d / %2$d - %3$s Szyfrowanie jest możliwe tylko przy >= Android 5.0 Brak wystarczającego miejsca, aby skopiować wybrane pliki do katalogu %1$s. Czy chcesz je tam przenieść? diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index b131fcb9e65b..5a58371b2c7c 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -224,7 +224,6 @@ Falha ao iniciar a importação. Por favor, tente novamente. Nenhum arquivo encontrado Não foi possível encontrar seu último backup! - Detectando alterações no conteúdo Falha ao criar conversa Excluir conversa Falha ao excluir conversa diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 99b972920872..a31430726736 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -220,7 +220,6 @@ Не удалось запустить импорт. Пожалуйста, попробуйте снова Файл не найден Не удалось найти последнюю резервную копию! - Проверка изменений в папках Не удалось создать чат Удалить обсуждение Не удалось создать чат. diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index b68a492f0a70..2c4ffcb979f1 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -214,7 +214,6 @@ Spustenie mportu zlyhalo. Prosím skúste to znova Nenašiel sa žiadny súbor Nenašla sa posledná záloha! - Detekcia zmien obsahu Nepodarilo sa vytvoriť konverzáciu Zmazať konverzáciu Nepodarilo sa vymazať konverzáciu diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml index 240f3c831e35..d78ebf7ba731 100644 --- a/app/src/main/res/values-sr/strings.xml +++ b/app/src/main/res/values-sr/strings.xml @@ -208,7 +208,6 @@ Није успело покретање увоза. Молимо вас да покушате поново Није нађен фајл Не могу да нађем последњу резерву! - Откривају се измене садржаја Обриши разовор Није пронађен ниједан разговор Још увек нема разговора diff --git a/app/src/main/res/values-sv/strings.xml b/app/src/main/res/values-sv/strings.xml index 1d82fea06619..7f90973f8d1a 100644 --- a/app/src/main/res/values-sv/strings.xml +++ b/app/src/main/res/values-sv/strings.xml @@ -211,7 +211,6 @@ Importen kunde inte startas. Försök igen Ingen fil funnen Vi kunde inte hitta din senaste backup! - Upptäcker innehållsförändringar Kunde inte skapa konversationen Ta bort konversationen Kunde inte ta bort konversationen diff --git a/app/src/main/res/values-sw/strings.xml b/app/src/main/res/values-sw/strings.xml index 94619ac025a8..23849331306d 100644 --- a/app/src/main/res/values-sw/strings.xml +++ b/app/src/main/res/values-sw/strings.xml @@ -118,6 +118,7 @@ /Pakia Kiotomatiki Folda hii tayari imejumuishwa katika usawazishaji wa folda kuu, ambayo inaweza kusababisha upakiaji unaorudiwa Inasubiri Wi-Fi kuanza kupakia + Inapakia faili kutoka %1$s hadi %2$s Sanidi Unda usanidi mpya wa folda maalum Sanidi folda maalum @@ -223,7 +224,6 @@ Imeshindwa kuanza kuleta. Tafadhali jaribu tena Hakuna faili iliyopatikana Haikuweza kupata nakala yako ya mwisho! - Inagundua mabadiliko ya maudhui Imeshindwa kuunda mazungumzo Futa mazungumzo Imeshindwa kufuta mazungumzo diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml index e75d5753c2f4..e0647c660716 100644 --- a/app/src/main/res/values-tr/strings.xml +++ b/app/src/main/res/values-tr/strings.xml @@ -215,7 +215,6 @@ İçe aktarım başlatılamadı. Lütfen yeniden deneyin Herhangi bir dosya bulunamadı Son yedeğiniz bulunamadı! - İçerik değişiklikleri denetleniyor Görüşme oluşturulamadı Görüşmeyi sil Görüşme silinemedi diff --git a/app/src/main/res/values-ug/strings.xml b/app/src/main/res/values-ug/strings.xml index 6db09d590200..c159b641aaf6 100644 --- a/app/src/main/res/values-ug/strings.xml +++ b/app/src/main/res/values-ug/strings.xml @@ -213,7 +213,6 @@ كىرگۈزۈشنى باشلىغىلى بولمىدى. قايتا سىناڭ ھۆججەت تېپىلمىدى ئاخىرقى زاپاسلاشنى تاپالمىدىڭىز! - مەزمۇن ئۆزگ سۆھبەت قۇرۇش مەغلۇپ بولدى سۆھبەتنى ئۆچۈرۈڭ سۆھبەتنى ئۆچۈرۈش مەغلۇپ بولدى diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index af48d1c80ce3..2c67d919df76 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -224,7 +224,6 @@ Не вдалося здійснити імпорт. Спробуйте ще раз Файл не знайдено Неможливо знайти вашу останню резервну копію - Пошук змін у вмісті Не вдалося створити розмову Вилучити розмову Не вдалося вилучити розмову diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index f8c129ace7a1..cc38d3718500 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -222,7 +222,6 @@ 导入无法开始。请重试 没有文件被发现 无法找到末次备份 - 正在检测内容更改 无法创建对话 删除对话 无法删除对话 diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index f742c891c42d..57c8c1f17d12 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -118,6 +118,7 @@ 自動上傳 此資料夾已包含在父資料夾的同步中,這可能會導致重複上傳 等待 Wi-Fi 開始上傳 + 正從 %1$s 上傳檔案至 %2$s 設定 新增自訂資料夾 設定自訂資料夾 @@ -223,7 +224,6 @@ 匯入無法開始。請再試一次 無檔案 找不到您最新的備份 - 偵測內容變更 無法創建對話 刪除對話 無法刪除對話 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index 49e5cbdaf82e..c24b5061fc66 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -224,7 +224,6 @@ 匯入無法開始。請再試一次 找不到檔案 找不到您最新的備份! - 偵測內容變更 建立對話失敗 刪除對話 刪除對話失敗 From 57262c76f0a6c90a328bac48d5b78736be394c1c Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Fri, 13 Feb 2026 03:30:02 +0000 Subject: [PATCH 59/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-et-rEE/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-et-rEE/strings.xml b/app/src/main/res/values-et-rEE/strings.xml index f56aa4a53b35..bac7c2993a9b 100644 --- a/app/src/main/res/values-et-rEE/strings.xml +++ b/app/src/main/res/values-et-rEE/strings.xml @@ -118,6 +118,7 @@ /AutoUpload Kuna ülalpool asuv kaust kuulub sünkroonimisele, siis see kaust on juba kaasatud ning nii võivad tekkida topelt üleslaadimised Ootan sünkroonimiseks WiFi ühenduse loomist + Laadin faile üles: „%1$s“ → „%2$s“ Seadista Loo uus kohandatud kausta seadistus Seadista kohandatud kaust From 446dff3ecf3a1bf66966ccf765eff7ac31d6cac1 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sat, 14 Feb 2026 03:08:04 +0000 Subject: [PATCH 60/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-fr/strings.xml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 01fde446d933..5c144c487b79 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -13,6 +13,7 @@ Envoyer / Partager Vue en grille Vue en liste + Action déclenchée Restaurer les contacts et l\'agenda Nouveau dossier Déplacer ou copier @@ -51,19 +52,25 @@ Supprimer la tâche Essayez d\'envoyer un message pour déclencher une conversation. Bonjour ! En quoi puis-je vous aider aujourd’hui ? + Veuillez sélectionner une tâche + Envoyer un message + Ouvrir la liste des conversations Une erreur est survenue lors de la création de la tâche Tâche créée Une erreur est survenue lors de la suppression de la tâche + Tâche supprimée La liste des tâches est vide. Vérifiez la configuration de l’application de l’assistant. Impossible de récupérer la liste des tâches, veuillez vérifier votre connexion Internet. Supprimer la tâche Le résultat de la tâche n\'est pas encore prêt. + Texte copié depuis une autre application Assistant Entrée Sortie défectueux en cours planifié + réussite Inconnu En cours de réflexion... Compte associé introuvable ! @@ -109,6 +116,7 @@ /TéléversementAuto Ce dossier fait partie de la synchronisation du dossier parent, ce qui peut créer des doublons. En attente du Wi-Fi pour commencer le téléversement + Téléversement des fichiers de %1$s vers %2$s Configurer Créer une nouvelle configuration de dossier personnalisé Définir un dossier personnalisé @@ -325,6 +333,7 @@ Assistant Plus Plus d\'applications Nextcloud + Impossible d\'ouvrir le sélecteur de fichiers Le choix de l\'adresse e-mail a échoué. Définir comme chiffré Impossible de retrouver le certificat du serveur @@ -396,6 +405,7 @@ Ajouter ou téléverser Transmission du fichier au gestionnaire de téléchargement échoué Impossible d\'imprimer le fichier + Impossible de lancer l\'action ! Impossible de lancer l\'éditeur Échec de la mise à jour de l\'interface utilisateur Ajouter aux favoris @@ -824,6 +834,7 @@ Veuillez sélectionner un modèle Sélectionnez un modèle Envoyer + Envoyer une copie à Envoyer Partager Icône du bouton d\'envoi Impossible de charger le contenu @@ -999,6 +1010,12 @@ Vignette pour nouveau fichier Le chargement prend plus de temps que prévu Aujourd\'hui + Entrez du texte à traduire... + Traduire depuis : + Traduire vers : + Appuyez sur le bouton pour traduire + La traduction prend plus de temps que prévu. + Traduction en cours... Fichiers supprimés Aucun fichier supprimé Les fichiers supprimés s\'afficheront ici et vous pourrez les restaurer. From 43ba594f311d3d1c2fb3137ecc30a501442e81d7 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 16 Feb 2026 03:07:44 +0000 Subject: [PATCH 61/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-fr/strings.xml | 1 + app/src/main/res/values-sk-rSK/strings.xml | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 5c144c487b79..cb269a8169cf 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -71,6 +71,7 @@ en cours planifié réussite + Etat de la tâche : %1$s Inconnu En cours de réflexion... Compte associé introuvable ! diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml index 2c4ffcb979f1..477b3ea1159b 100644 --- a/app/src/main/res/values-sk-rSK/strings.xml +++ b/app/src/main/res/values-sk-rSK/strings.xml @@ -48,13 +48,18 @@ Výstup zobrazený tu je generovaný umelou inteligenciou. Uistite sa, že vždy dôkladne skontrolujete. Nepodarilo sa odoslať správu Nepodarilo sa načítať správy chatu + Vrátiť sa na stránku asistenta Naozaj chcete vymazať túto úlohu? Odstrániť úlohu Skúste poslať správu, aby ste rozprúdili konverzáciu. Dobrý deň! S čím vám dnes môžem pomôcť? + Vyberte úlohu + Odoslať správu + Otvor zoznam rozhovorov Pri vytváraní úlohy nastala chyba Úloha vytvorená Pri odstraňovaní úlohy nastala chyba + Úloha vymazaná Zoznam úloh je prázdny. Skontrolujte konfiguráciu aplikácie asistenta. Nie je možné načítať zoznam úloh, skontrolujte svoje internetové pripojenie. Vymazať Úlohu @@ -63,7 +68,11 @@ Asistent Vstup Výstup + zlyhalo spustené + naplánované + úspešné + Stav úlohy: %1$s neznámy Premýšľam … Priradený účet sa nenašiel @@ -109,6 +118,7 @@ /AutomatickéNahrávanie Tento adresár je už zahrnutý v synchronizácii nadradeného adresára, čo môže spôsobiť duplicitné nahrávania Čaká sa na Wi-Fi pre spustenie nahrávania. + Nahrávanie súborov z %1$s do %2$s Nastaviť Nastavenie vytvorenia vlastného priečinku Nastaviť vlastný priečinok @@ -496,6 +506,7 @@ %1$d z %2$d · %3$s Došlo k chybe počas synchronizácie priečinka %s. Nedostatok miesta na disku, synchronizácia zrušená + %s zložka synchronizovaná Synchronizuje sa... Nie sú tu žiadne priečinky Názov adresára nemôže byť prázdny @@ -1004,6 +1015,12 @@ Náhľad nového súboru Načítavanie trvá dlhšie ako sa očakávalo Dnes + Zadajte text na preklad… + Preložiť z: + Preložiť do: + Stlačte tlačidlo na preklad + Preklad trvá dlhšie, než sa očakávalo. + Prekladám… Zmazané súbory Žiadne zmazané súbory Tu budete mať možnosť obnoviť zmazané súbory. @@ -1072,6 +1089,7 @@ Povolenia aplikácie Vaše súbory nemožno nahrať bez prístupu k miestnemu úložisku. Klepnite pre udelenie povolenia. Nahrávanie zastavené – Vyžaduje sa povolenie pre úložisko + Môžete to odstrániť alebo pokračovať v nahrávaní z nahratých súborov %1$d / %2$d - %3$s Šifrovanie je možné iba pri >= Android 5.0 Pre skopírovanie vybratých súborov do adresára %1$s nie je dostatok voľného miesta. Chcete ich namiesto toho presunúť? From 33c26b04f5750d83e26daeff6b314022495fdadc Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 16 Feb 2026 08:32:52 +0000 Subject: [PATCH 62/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-hr/strings.xml | 21 +++++++++++---------- app/src/main/res/values-ja-rJP/strings.xml | 12 ++++++++++++ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index 05f2981a03fd..ca87c3100e63 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -668,17 +668,17 @@ U izvanmrežnom načinu rada dostupne su samo datoteke spremljene za izvanmrežnu upotrebu. Promjene će se sinkronizirati kada se ponovno povežete. Izvanmrežni način rada Datoteka još ne postoji - Sukob pri stvaranju datoteke - Sukob pri stvaranju mape - Pogreška pri izvanmrežnim operacijama - Izvanmrežne operacije… - Sukob pri uklanjanju - Sukob pri preimenovanju + Sukob pri stvaranju datoteke %s. Na poslužitelju već postoji datoteka istog naziva. + Sukob pri stvaranju mape %s. Na poslužitelju postoji mapa s istim imenom. + Pogreška pri izvanmrežnim operacijama. %s + Izvanmrežne operacije + Sukob pri uklanjanju %s. Datoteka je izmijenjena na poslužitelju. + Sukob pri preimenovanju %s. Na poslužitelju postoji datoteka s istim imenom. Obrada izvanmrežnih promjena 1 sat Na mreži Status na mreži - Otvori u aplikaciji + Otvori u %1$s Poslužitelj je na kraju radnog vijeka, nadogradite ga! Više izbornika Unesite zaporku @@ -957,7 +957,8 @@ Ne pitaj ponovno Medijske datoteke samo za čitanje Slike - Nextcloud za Android: sigurna suradnja, razmjena datoteka i sinkronizacija s vašim poslužiteljem. + Samostalno hostirana platforma za produktivnost koja vam omogućuje kontrolu. n\nZnačajke:\n* Jednostavno, moderno sučelje prilagođeno temi vašeg poslužitelja\n* Učitavanje datoteka na vaš Nextcloud poslužitelj\n* Dijeljenje s drugima\n* Sinkronizacija omiljenih datoteka i mapa\n* Pretraživanje svih mapa na vašem poslužitelju\n* Automatsko učitavanje fotografija i videozapisa snimljenih vašim uređajem\n* Ostanite u tijeku uz obavijesti\n* Podrška za više računa\n* Sigurn pristup vašim podacima pomoću otiska prsta ili PIN-a\n* Integracija s DAVx⁵ (ranije poznatim kao DAVdroid) za jednostavno postavljanje sinkronizacije kalendara i kontakata\n\nMolimo prijavite sve probleme na https://github.com/nextcloud/android/issues i raspravljajte o ovoj aplikaciji na https://help.nextcloud.com/c/clients/android\n\nNovi ste na Nextcloudu? Nextcloud je privatni poslužitelj za sinkronizaciju i dijeljenje datoteka te komunikaciju. To je slobodni softver i možete ga sami hostati ili platiti tvrtki da to učini za vas. Na taj način imate kontrolu nad svojim fotografijama, podacima kalendara i kontakata, dokumentima i svime ostalim.\n\nPogledajte Nextcloud na https://nextcloud.com + Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu.\nTo je službena razvojna inačica koja se isporučuje s dnevnim uzorkom svake nove neispitane funkcionalnosti, što može uzrokovati nestabilnost i gubitak podataka. Aplikacija je namijenjena korisnicima koji žele testirati i prijaviti pogreške, ako se iste pojave. Nemojte je upotrebljavati za važne radne zadatke!\n\nSlužbena razvojna i uobičajena inačica dostupne su putem F-droida i mogu biti istovremeno instalirane. Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu Samopostavljena platforma pospješuje produktivnost i omogućuje vam da zadržite kontrolu (razvojna inačica za pretpregled) @@ -1020,7 +1021,7 @@ S Na Započni prijevod - Obrada zadatka… + Prijevod traje duže nego što se očekivalo. Prevođenje… Izbrisane datoteke Nema izbrisanih datoteka @@ -1091,7 +1092,7 @@ Za prijenos je potrebno dopustiti pristup datotekama na uređaju. Nedostaje dopuštenje za pohranu Otvori status prijenosa - Prijenos datoteka… + %1$d / %2$d - %3$s Šifriranje je moguće samo na inačici >= Androida 5.0 Nedostatak prostora sprječava kopiranje odabranih datoteka u mapu %1$s. Želite li ih premjestiti tamo? Kvote je premašena diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index bb88bba210d1..08ad47251083 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -49,17 +49,25 @@ タスクを削除 メッセージを送って会話を始めてみましょう。 こんにちは!何かお手伝いできることはありますか? + タスクを選択して下さい + メッセージを送信する タスクの作成中にエラーが発生しました。 タスクを作成しました タスクの削除中にエラーが発生しました。 + 削除されたタスク タスクリストを取得できません。インターネット接続を確認してください。 タスクを削除 タスクの出力はまだ準備ができていません。 アシスタント 入力 出力 + 失敗しました 実行中 + 予定済み + 成功しました + タスクの状況:%1$s 不明 + 考え中... 関連付けられたアカウントが見つかりません! アクセスに失敗しました: %1$s このアカウントはまだこのデバイスに追加されていません @@ -103,6 +111,7 @@ /AutoUpload このフォルダは既に親の同期に含まれているため、重複してアップロードされる可能性があります アップロードのためWi-Fiに接続されるのを待っています… + ファイルを %1$s から %2$s へアップロードしています 設定 新しいカスタムフォルダセットアップを作成する カスタムフォルダを設定する @@ -972,6 +981,9 @@ 新規ファイルのサムネイル ローディング中 期待した時間より長くかかっています 今日 + 翻訳元: + 翻訳先: + 翻訳中... ゴミ箱 削除されたファイルはありません ここから削除されたファイルを元に戻すことができます。 From f0ab72c27b7dc4d5af20e95c94afc0e6af6757c9 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 16 Feb 2026 09:42:29 +0000 Subject: [PATCH 63/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-hr/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-hr/strings.xml b/app/src/main/res/values-hr/strings.xml index ca87c3100e63..ae733ba72277 100644 --- a/app/src/main/res/values-hr/strings.xml +++ b/app/src/main/res/values-hr/strings.xml @@ -1175,7 +1175,7 @@ Preskoči Novo u %1$s Koji je vaš status? - Widgeti nisu dostupni + Widgeti su dostupni samo u verziji %1$s 25 ili novijoj kada je aplikacija Dashboard omogućena Nije dostupno Preuzimanje datoteka... Pošalji poruku e-pošte From c3805db6eac7cd1acf966fa6526c723fcf564911 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 11:23:09 +0100 Subject: [PATCH 64/83] simplify calling logics Signed-off-by: alperozturk96 --- .../ui/activity/FileDisplayActivity.kt | 58 ++++++++----------- .../ui/preview/PreviewImageActivity.kt | 11 ++-- 2 files changed, 27 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index f137102a71d7..46ee0dcfbe9d 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -1935,20 +1935,18 @@ class FileDisplayActivity : } fun previewImageWithSearchContext(file: OCFile, searchFragment: Boolean, currentSearchType: SearchType?) { - // preview image - it handles the download, if needed - if (searchFragment) { - val type = when (currentSearchType) { + val type = if (searchFragment) { + when (currentSearchType) { SearchType.FAVORITE_SEARCH -> VirtualFolderType.FAVORITE SearchType.GALLERY_SEARCH -> VirtualFolderType.GALLERY else -> VirtualFolderType.NONE } + } else null - startImagePreview(file, type, file.isDown) - } else { - startImagePreview(file, file.isDown) - } + startImagePreview(file, file.isDown, type) } + fun previewFile(file: OCFile, setFabVisible: CompletionCallback?) { if (!file.isDown) { Log_OC.d(TAG, "File is not downloaded, cannot be previewed") @@ -2472,43 +2470,33 @@ class FileDisplayActivity : } } - fun startImagePreview(file: OCFile, showPreview: Boolean) { - val showDetailsIntent = Intent(this, PreviewImageActivity::class.java) - showDetailsIntent.putExtra(EXTRA_FILE, file) - showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) - showDetailsIntent.putExtra( - EXTRA_USER, - user.orElseThrow(Supplier { RuntimeException() }) - ) - if (showPreview) { - startActivity(showDetailsIntent) - } else { - val fileOperationsHelper = - FileOperationsHelper(this, userAccountManager, connectivityService, editorUtils) - fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent) + fun startImagePreview( + file: OCFile, + showPreview: Boolean, + type: VirtualFolderType? = null + ) { + if (user.isEmpty) { + Log_OC.e(TAG, "cannot start image preview") + return } - } - fun startImagePreview(file: OCFile, type: VirtualFolderType?, showPreview: Boolean) { - val showDetailsIntent = Intent(this, PreviewImageActivity::class.java) - showDetailsIntent.putExtra(EXTRA_FILE, file) - showDetailsIntent.putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) - showDetailsIntent.putExtra( - EXTRA_USER, - user.orElseThrow(Supplier { RuntimeException() }) - ) - showDetailsIntent.putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, type) + val intent = Intent(this, PreviewImageActivity::class.java).apply { + putExtra(EXTRA_FILE, file) + putExtra(EXTRA_LIVE_PHOTO_FILE, file.livePhotoVideo) + putExtra(EXTRA_USER, user.get()) + type?.let { putExtra(PreviewImageActivity.EXTRA_VIRTUAL_TYPE, it) } + } if (showPreview) { - startActivity(showDetailsIntent) + startActivity(intent) } else { - val fileOperationsHelper = FileOperationsHelper( + val helper = FileOperationsHelper( this, userAccountManager, connectivityService, editorUtils ) - fileOperationsHelper.startSyncForFileAndIntent(file, showDetailsIntent) + helper.startSyncForFileAndIntent(file, intent) } } @@ -2772,8 +2760,8 @@ class FileDisplayActivity : val virtualType = bundle.get(PreviewImageActivity.EXTRA_VIRTUAL_TYPE) as VirtualFolderType? startImagePreview( file, + true, virtualType, - true ) } else { startImagePreview(file, true) diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 018fdccc6001..72cbafdfaf79 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -166,13 +166,10 @@ class PreviewImageActivity : preferences ) } else { - // get parent from path - var parentFolder = file?.let { storageManager.getFileById(it.parentId) } - - if (parentFolder == null) { - // should not be necessary - parentFolder = storageManager.getFileByEncryptedRemotePath(OCFile.ROOT_PATH) - } + val chosenFile = intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) + val parentFolder = file?.let { storageManager.getFileById(it.parentId) } + ?: chosenFile?.parentId?.let { storageManager.getFileById(it) } + ?: storageManager.getFileByEncryptedRemotePath(OCFile.ROOT_PATH) previewImagePagerAdapter = PreviewImagePagerAdapter( this, From 3307635b1fb3d0fdfc0796cc3f864f40beb80247 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 11:56:04 +0100 Subject: [PATCH 65/83] fix(media): photo search event Signed-off-by: alperozturk96 --- app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt | 2 ++ .../com/owncloud/android/ui/preview/PreviewImageActivity.kt | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt b/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt index c0baa03e7f64..e7827d36eeff 100644 --- a/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt +++ b/app/src/main/java/com/owncloud/android/ui/events/SearchEvent.kt @@ -33,6 +33,8 @@ data class SearchEvent(val searchQuery: String, val searchType: SearchRemoteOper SearchRemoteOperation.SearchType.FAVORITE_SEARCH -> SearchType.FAVORITE_SEARCH SearchRemoteOperation.SearchType.RECENTLY_MODIFIED_SEARCH -> SearchType.RECENTLY_MODIFIED_SEARCH SearchRemoteOperation.SearchType.SHARED_FILTER -> SearchType.SHARED_FILTER + SearchRemoteOperation.SearchType.PHOTO_SEARCH -> SearchType.GALLERY_SEARCH + SearchRemoteOperation.SearchType.GALLERY_SEARCH -> SearchType.GALLERY_SEARCH else -> null } diff --git a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt index 72cbafdfaf79..ecd7ac821d0c 100644 --- a/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.kt @@ -166,9 +166,7 @@ class PreviewImageActivity : preferences ) } else { - val chosenFile = intent.getParcelableArgument(EXTRA_FILE, OCFile::class.java) val parentFolder = file?.let { storageManager.getFileById(it.parentId) } - ?: chosenFile?.parentId?.let { storageManager.getFileById(it) } ?: storageManager.getFileByEncryptedRemotePath(OCFile.ROOT_PATH) previewImagePagerAdapter = PreviewImagePagerAdapter( From 2a97457e9db4166623a2792c84f6d4db01956ee9 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 12:02:59 +0100 Subject: [PATCH 66/83] fix codacy Signed-off-by: alperozturk96 --- .../android/ui/activity/FileDisplayActivity.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 46ee0dcfbe9d..95f0192d22dc 100644 --- a/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/app/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -1941,12 +1941,13 @@ class FileDisplayActivity : SearchType.GALLERY_SEARCH -> VirtualFolderType.GALLERY else -> VirtualFolderType.NONE } - } else null + } else { + null + } startImagePreview(file, file.isDown, type) } - fun previewFile(file: OCFile, setFabVisible: CompletionCallback?) { if (!file.isDown) { Log_OC.d(TAG, "File is not downloaded, cannot be previewed") @@ -2470,11 +2471,7 @@ class FileDisplayActivity : } } - fun startImagePreview( - file: OCFile, - showPreview: Boolean, - type: VirtualFolderType? = null - ) { + fun startImagePreview(file: OCFile, showPreview: Boolean, type: VirtualFolderType? = null) { if (user.isEmpty) { Log_OC.e(TAG, "cannot start image preview") return @@ -2761,7 +2758,7 @@ class FileDisplayActivity : startImagePreview( file, true, - virtualType, + virtualType ) } else { startImagePreview(file, true) From 6fa28c4b673582ce29d2f4e1810ae82fb33e82c4 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 18 Feb 2026 03:08:17 +0000 Subject: [PATCH 67/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-cs-rCZ/strings.xml | 2 +- app/src/main/res/values-in/strings.xml | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index fb1594a35a17..4a6de3185b37 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -1020,7 +1020,7 @@ Přeložit do: Přeložíte kliknutím na tlačítko Překládání trvá déle než očekáváno. - Překládá se… + Překládání… Smazané soubory Žádné smazané soubory Odsud je možné obnovit smazané soubory. diff --git a/app/src/main/res/values-in/strings.xml b/app/src/main/res/values-in/strings.xml index 19727d8834f7..e84ca988024c 100644 --- a/app/src/main/res/values-in/strings.xml +++ b/app/src/main/res/values-in/strings.xml @@ -680,6 +680,7 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Ganti nama versi baru Apa yang harus dilakukan jika berkas sudah ada? Tambah akun + Akses semua berkas Sinkronkan kalender dan kontak F-Droid maupun Google Play tidak terinstal Siapkan DAVx⁵ (sebelumnya dikenal sebagai DAVdroid) (versi 1.3.0 ke atas) untuk akun saat ini @@ -914,6 +915,9 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Penyimpanan internal Film Musik + Akses semua berkas + Izin penyimpanan dibutuhkan untuk Unggah Otomatis + Jangan tanya Media hanya baca Gambar Platform produktivitas mandiri yang memberi Anda kendali penuh.\n\Fitur:\n* Mudah, tampilan modern, dan cocok dengan tema server Anda\n* Unggah file ke server Nextcloud Anda\n* Bagikan dengan yang lain\n* Buat file dan folder favorit Anda tetap tersinkron\n* Telusuri semua folder di server Anda\n* Unggah otomatis untuk foto dan video yang diambil dari perangkat Anda\n* Tetap terkabarkan dengan notifikasi\n* Mendukung penggunaan banyak akun (multi-account)\n* Akses data Anda dengan aman dengan sidik jari atau PIN\n* Integrasi dengan DAVx⁵ (dulu dikenal sebagai DAVdroid) untuk pemasangan sinkronisasi kalender dan kontak yang mudah\n\nTolong laporkan semua masalah aplikasi ini di https://github.com/nextcloud/android/issues dan diskusikan aplikasi ini di https://help.nextcloud.com/c/clients/android\n\nBaru mencoba Nextcloud? Nextcloud adalah server pribadi untuk sinkronisasi dan berbagi file serta berkomunikasi. Nextcloud adalah software libre sehingga Anda dapat meng-host sendiri atau membayar perusahaan untuk melakukannya untuk Anda. Dengan begitu, Anda memiliki kendali atas foto Anda, kalender dan data kontak Anda, dokumen dan semuanya yang Anda miliki.\n\nYuk, cek Nextcloud di https://nextcloud.com @@ -934,6 +938,8 @@ Otomatis unggah hanya bekerja dengan baik apabila Anda mengeluarkan aplikasi ini Sarankan Sinkron Tetap sinkronkan + Perbaiki konflik + Berkas unggahan konflik Konflik ditemukan Folder %1$s sudah tidak ada lagi Duplikasi sinkronisasi @@ -976,6 +982,11 @@ Berikut ini adalah daftar berkas lokal, dan berkas jarak jauh di %5$s yang terhu Thumbnail untuk berkas baru Pemuatan membutuhkan waktu lebih lama dari yang diperkirakan. Hari ini + Masukkan teks untuk di terjemahkan + Terjemahkan dari : + Terjemahkan ke : + Tekan tombol nya untuk menerjemahkan + Menerjemahkan... Berkas terhapus Tidak ada berkas yang dihapus. Anda akan dapat memulihkan berkas yang dihapus dari sini. @@ -1098,6 +1109,7 @@ Berikut ini adalah daftar berkas lokal, dan berkas jarak jauh di %5$s yang terhu Sertifikat server tidak terpecaya Mendapatkan versi server... Aplikasi ditutup + Ter lewat kan Selesai File yang sama ditemukan saat transfer jarak jauh, melewati unggahan Kesalahan tidak diketahui From e24db360597b97f6b5e8b6080b6426649ac20649 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 14:02:39 +0100 Subject: [PATCH 68/83] fix(auto-upload): expedited work Signed-off-by: alperozturk96 --- .../java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt index c5f6ceb021d4..8a8647e53d5f 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/BackgroundJobManagerImpl.kt @@ -501,7 +501,6 @@ internal class BackgroundJobManagerImpl( jobName = JOB_IMMEDIATE_FILES_SYNC + "_" + syncedFolderID ) .setInputData(arguments) - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) .setConstraints(constraints) .setBackoffCriteria( BackoffPolicy.LINEAR, From e507498fe235573b5a05ee7549f20228742f15de Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 10:01:17 +0100 Subject: [PATCH 69/83] chore: version bump to 33.0.0 Signed-off-by: alperozturk96 --- app/build.gradle.kts | 6 +++--- fastlane/metadata/android/en-US/changelogs/33000090.txt | 8 ++++++++ .../android/en-US/changelogs/33000090.txt.license | 2 ++ gradle/libs.versions.toml | 2 +- gradle/verification-metadata.xml | 8 ++++++++ 5 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 fastlane/metadata/android/en-US/changelogs/33000090.txt create mode 100644 fastlane/metadata/android/en-US/changelogs/33000090.txt.license diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f8a30e90b69f..9907b8679c11 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -62,10 +62,10 @@ configurations.configureEach { } // semantic versioning for version code -val versionMajor = 3 -val versionMinor = 36 +val versionMajor = 33 +val versionMinor = 0 val versionPatch = 0 -val versionBuild = 52 // 0-50=Alpha / 51-98=RC / 90-99=stable +val versionBuild = 90 // 0-50=Alpha / 51-98=RC / 90-99=stable val ndkEnv = buildMap { file("${project.rootDir}/ndk.env").readLines().forEach { diff --git a/fastlane/metadata/android/en-US/changelogs/33000090.txt b/fastlane/metadata/android/en-US/changelogs/33000090.txt new file mode 100644 index 000000000000..0c147eb5431e --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/33000090.txt @@ -0,0 +1,8 @@ +## 33.0.0 (February 18, 2026) + +- Auto upload improvements +- Bug fixes and performance improvements + +Minimum: NC 20 Server, Android 9 Nougat + +For a full list, please see https://github.com/nextcloud/android/milestone/120 \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/33000090.txt.license b/fastlane/metadata/android/en-US/changelogs/33000090.txt.license new file mode 100644 index 000000000000..a67e1fec2721 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/33000090.txt.license @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors +# SPDX-License-Identifier: AGPL-3.0-or-later \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 847f6925246a..17498e0c9bba 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ androidCommonLibraryVersion = "4fc0f29981" androidGifDrawableVersion = "1.2.30" androidImageCropperVersion = "4.7.0" -androidLibraryVersion ="rc-2.23.0" +androidLibraryVersion ="2.23.0" androidPluginVersion = "9.0.0" androidsvgVersion = "1.4" androidxMediaVersion = "1.5.1" diff --git a/gradle/verification-metadata.xml b/gradle/verification-metadata.xml index 637fe2a94f9a..99bde9333885 100644 --- a/gradle/verification-metadata.xml +++ b/gradle/verification-metadata.xml @@ -20751,6 +20751,14 @@ + + + + + + + + From 7ff6d25880f89f786acb103bc3436a316e8a6922 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 10:20:27 +0100 Subject: [PATCH 70/83] chore: version bump to 33.0.0 Signed-off-by: alperozturk96 --- .../en-US/changelogs/{33000090.txt.license => 330000090.license} | 0 .../android/en-US/changelogs/{33000090.txt => 330000090.txt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename fastlane/metadata/android/en-US/changelogs/{33000090.txt.license => 330000090.license} (100%) rename fastlane/metadata/android/en-US/changelogs/{33000090.txt => 330000090.txt} (100%) diff --git a/fastlane/metadata/android/en-US/changelogs/33000090.txt.license b/fastlane/metadata/android/en-US/changelogs/330000090.license similarity index 100% rename from fastlane/metadata/android/en-US/changelogs/33000090.txt.license rename to fastlane/metadata/android/en-US/changelogs/330000090.license diff --git a/fastlane/metadata/android/en-US/changelogs/33000090.txt b/fastlane/metadata/android/en-US/changelogs/330000090.txt similarity index 100% rename from fastlane/metadata/android/en-US/changelogs/33000090.txt rename to fastlane/metadata/android/en-US/changelogs/330000090.txt From 6238eccb6a509ba28c33e49930a40aa0f086e60d Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 15:26:59 +0100 Subject: [PATCH 71/83] check calls and dont set update size Signed-off-by: alperozturk96 --- .../operations/UploadFileOperation.java | 42 ++++++++++++++----- .../android/utils/FileStorageUtils.java | 5 +++ 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index efbf642eb0c1..11be44cf5eae 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -436,27 +436,49 @@ protected RemoteOperationResult run(OwnCloudClient client) { mCancellationRequested.set(false); mUploadStarted.set(true); - updateSize(0); - String remoteParentPath = new File(getRemotePath()).getParent(); - remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) ? remoteParentPath : remoteParentPath + OCFile.PATH_SEPARATOR; - remoteParentPath = AutoRename.INSTANCE.rename(remoteParentPath, getCapabilities()); + if (remoteParentPath == null) { + Log_OC.e(TAG, "remoteParentPath is null: " + getRemotePath()); + return new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); + } + remoteParentPath = remoteParentPath.endsWith(OCFile.PATH_SEPARATOR) + ? remoteParentPath + : remoteParentPath + OCFile.PATH_SEPARATOR; + + final String renamedRemoteParentPath = AutoRename.INSTANCE.rename(remoteParentPath, getCapabilities()); + if (!remoteParentPath.equals(renamedRemoteParentPath)) { + Log_OC.w(TAG, "remoteParentPath was renamed: " + remoteParentPath + " → " + renamedRemoteParentPath); + } + remoteParentPath = renamedRemoteParentPath; OCFile parent = getStorageManager().getFileByPath(remoteParentPath); + Log_OC.d(TAG, "parent lookup for path: " + remoteParentPath + " → " + + (parent == null ? "not found in DB" : "found, id=" + parent.getFileId())); // in case of a fresh upload with subfolder, where parent does not exist yet if (parent == null && (mFolderUnlockToken == null || mFolderUnlockToken.isEmpty())) { - // try to create folder + Log_OC.d(TAG, "parent not in DB and no unlock token, attempting to grant folder existence: " + + remoteParentPath); final var result = grantFolderExistence(remoteParentPath, client); if (!result.isSuccess()) { + Log_OC.e(TAG, "grantFolderExistence failed for: " + remoteParentPath + ", code: " + + result.getCode() + ", message: " + result.getMessage()); return result; } parent = getStorageManager().getFileByPath(remoteParentPath); + if (parent == null) { + Log_OC.e(TAG, "parent still null after grantFolderExistence: " + remoteParentPath); + return new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); + } + + Log_OC.d(TAG, "parent created and retrieved successfully: " + remoteParentPath + ", id=" + + parent.getFileId()); } if (parent == null) { + Log_OC.e(TAG, "parent is null, cannot proceed: " + remoteParentPath + "," + " unlock token: " + mFolderUnlockToken); return new RemoteOperationResult<>(false, "Parent folder not found", HttpStatus.SC_NOT_FOUND); } @@ -1366,9 +1388,9 @@ private OCCapability getCapabilities() { * @param pathToGrant Full remote path whose existence will be granted. * @return An {@link OCFile} instance corresponding to the folder where the file will be uploaded. */ - private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) { - RemoteOperation operation = new ExistenceCheckRemoteOperation(pathToGrant, false); - RemoteOperationResult result = operation.execute(client); + private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudClient client) { + var operation = new ExistenceCheckRemoteOperation(pathToGrant, false); + var result = operation.execute(client); if (!result.isSuccess() && result.getCode() == ResultCode.FILE_NOT_FOUND && mRemoteFolderToBeCreated) { SyncOperation syncOp = new CreateFolderOperation(pathToGrant, user, getContext(), getStorageManager()); result = syncOp.execute(client); @@ -1379,9 +1401,9 @@ private RemoteOperationResult grantFolderExistence(String pathToGrant, OwnCloudC parentDir = createLocalFolder(pathToGrant); } if (parentDir != null) { - result = new RemoteOperationResult(ResultCode.OK); + result = new RemoteOperationResult<>(ResultCode.OK); } else { - result = new RemoteOperationResult(ResultCode.CANNOT_CREATE_FILE); + result = new RemoteOperationResult<>(ResultCode.CANNOT_CREATE_FILE); } } return result; diff --git a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java index b9988cbdba1f..c31e21a6a48d 100644 --- a/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java +++ b/app/src/main/java/com/owncloud/android/utils/FileStorageUtils.java @@ -588,6 +588,11 @@ public static void checkIfFileFinishedSaving(OCFile file) { * @return true if file itself or ancestor is encrypted */ public static boolean checkEncryptionStatus(OCFile file, FileDataStorageManager storageManager) { + if (file == null) { + Log_OC.e(TAG, "checkEncryptionStatus called with null file"); + return false; + } + if (file.isEncrypted()) { return true; } From 51065a0c1b4984b22fc741a985251297ea6afe7b Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 16:15:00 +0100 Subject: [PATCH 72/83] add logs for file size determination Signed-off-by: alperozturk96 --- .../android/operations/UploadFileOperation.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 11be44cf5eae..e7a9fa221c50 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1097,8 +1097,16 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { long size; try { size = channel.size(); - } catch (IOException e) { - size = Files.size(filePath); + } catch (Exception e) { + Log_OC.e(TAG, "failed to determine file size from channel: ", e); + + try { + size = Files.size(filePath); + } catch (Exception exception) { + Log_OC.e(TAG, "failed to determine file size from nio.File: ", exception); + result = new RemoteOperationResult<>(ResultCode.FILE_NOT_FOUND); + return result; + } } updateSize(size); From e1666999228e103da31aa9529d5a3b7ce75acc6b Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Tue, 17 Feb 2026 16:15:12 +0100 Subject: [PATCH 73/83] add logs for file size determination Signed-off-by: alperozturk96 --- .../com/owncloud/android/operations/UploadFileOperation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index e7a9fa221c50..e8c44c6ff3a7 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -43,7 +43,6 @@ import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.network.ProgressiveDataTransfer; import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.lib.common.utils.Log_OC; From 044da4e612ccf42821b9834321373ab58460d2ce Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 09:32:23 +0100 Subject: [PATCH 74/83] add logs revert update size Signed-off-by: alperozturk96 --- .../operations/UploadFileOperation.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index e8c44c6ff3a7..1472c99a1b22 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -435,6 +435,9 @@ protected RemoteOperationResult run(OwnCloudClient client) { mCancellationRequested.set(false); mUploadStarted.set(true); + updateSize(0); + Log_OC.d(TAG, "file size set to 0KB before upload"); + String remoteParentPath = new File(getRemotePath()).getParent(); if (remoteParentPath == null) { Log_OC.e(TAG, "remoteParentPath is null: " + getRemotePath()); @@ -489,10 +492,10 @@ protected RemoteOperationResult run(OwnCloudClient client) { mFile.setEncrypted(encryptedAncestor); if (encryptedAncestor) { - Log_OC.d(TAG, "encrypted upload"); + Log_OC.d(TAG, "⬆️🔗" + "encrypted upload"); return encryptedUpload(client, parent); } else { - Log_OC.d(TAG, "normal upload"); + Log_OC.d(TAG, "⬆️" + "normal upload"); return normalUpload(client); } } @@ -1037,13 +1040,16 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { File expectedFile = null; try { + Log_OC.d(TAG, "checking conditions"); result = checkConditions(originalFile); if (result != null) { return result; } + Log_OC.d(TAG, "checking name collision"); final var collisionResult = checkNameCollision(null, client, null, false); if (collisionResult != null) { + Log_OC.e(TAG, "name collision detected"); result = collisionResult; return collisionResult; } @@ -1053,6 +1059,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { result = copyFile(originalFile, expectedPath); if (!result.isSuccess()) { + Log_OC.e(TAG, "file copying failed"); return result; } @@ -1108,6 +1115,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } } updateSize(size); + Log_OC.d(TAG, "file size set to " + size); // decide whether chunked or not if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { @@ -1123,6 +1131,8 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { mDisableRetries); } + Log_OC.d(TAG, "upload operation determined"); + /** * Adds the onTransferProgress in FileUploadWorker * {@link FileUploadWorker#onTransferProgress(long, long, long, String)()} @@ -1132,17 +1142,20 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } if (mCancellationRequested.get()) { + Log_OC.e(TAG, "upload operation cancelled"); throw new OperationCancelledException(); } // execute if (result.isSuccess() && mUploadOperation != null) { + Log_OC.d(TAG, "upload operation completed"); result = mUploadOperation.execute(client); } // move local temporal file or original file to its corresponding // location in the Nextcloud local folder if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { + Log_OC.e(TAG, "upload operation failed with SC_PRECONDITION_FAILED"); result = new RemoteOperationResult<>(ResultCode.SYNC_CONFLICT); } @@ -1198,7 +1211,7 @@ private void updateSize(long size) { } } - private void logResult(RemoteOperationResult result, String sourcePath, String targetPath) { + private void logResult(RemoteOperationResult result, String sourcePath, String targetPath) { if (result.isSuccess()) { Log_OC.i(TAG, "Upload of " + sourcePath + " to " + targetPath + ": " + result.getLogMessage()); } else { @@ -1231,7 +1244,7 @@ private RemoteOperationResult copyFile(File originalFile, String expectedPath) t throw new OperationCancelledException(); } - return new RemoteOperationResult(ResultCode.OK); + return new RemoteOperationResult<>(ResultCode.OK); } @CheckResult From 26657969888e3093ca10ba9e171714f39dbb7f51 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 09:35:57 +0100 Subject: [PATCH 75/83] add logs revert update size Signed-off-by: alperozturk96 --- .../com/owncloud/android/operations/UploadFileOperation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1472c99a1b22..ec5aa8f563c0 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -414,6 +414,7 @@ public boolean isMissingPermissionThrown() { @Override @SuppressWarnings("PMD.AvoidDuplicateLiterals") protected RemoteOperationResult run(OwnCloudClient client) { + Log_OC.d(TAG, "------- Upload File Operation Started -------"); if (TextUtils.isEmpty(getStoragePath())) { Log_OC.e(TAG, "Upload cancelled for " + getStoragePath() + ": file path is null or empty."); return new RemoteOperationResult<>(new UploadFileException.EmptyOrNullFilePath()); From 4e0dfa5921aa06929a5528ca74bac43e4d752945 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 13:24:08 +0100 Subject: [PATCH 76/83] add auto upload logs Signed-off-by: alperozturk96 --- .../client/database/entity/UploadEntity.kt | 14 +++++ .../jobs/autoUpload/AutoUploadWorker.kt | 9 +++ .../network/ConnectivityServiceImpl.java | 45 +++++++++++++- .../extensions/SyncedFolderExtensions.kt | 61 +++++++++++++++++++ .../operations/UploadFileOperation.java | 25 ++++++-- 5 files changed, 149 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt index 7322a20e50a3..ff4ff9e91c6f 100644 --- a/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt +++ b/app/src/main/java/com/nextcloud/client/database/entity/UploadEntity.kt @@ -17,6 +17,7 @@ import com.owncloud.android.db.OCUpload import com.owncloud.android.db.ProviderMeta.ProviderTableMeta import com.owncloud.android.db.UploadResult import com.owncloud.android.files.services.NameCollisionPolicy +import com.owncloud.android.lib.common.utils.Log_OC import com.owncloud.android.lib.resources.status.OCCapability import java.lang.IllegalArgumentException @@ -71,6 +72,7 @@ fun UploadEntity.toOCUpload(capability: OCCapability? = null): OCUpload? { val upload = try { OCUpload(localPath, remotePath, accountName) } catch (_: IllegalArgumentException) { + Log_OC.e("UploadEntity", "OCUpload conversion failed") return null } @@ -92,9 +94,21 @@ fun UploadEntity.toOCUpload(capability: OCCapability? = null): OCUpload? { fun OCUpload.toUploadEntity(): UploadEntity { val id = if (uploadId == -1L) { + Log_OC.d( + "UploadEntity", + "UploadEntity: No existing ID provided (uploadId = -1). " + + "Will insert as NEW record and let Room auto-generate the primary key." + ) + // needed for the insert new records to the db so that insert DAO function returns new generated id null } else { + Log_OC.d( + "UploadEntity", + "UploadEntity: Using existing ID ($uploadId). " + + "This will update/replace the existing database record." + ) + uploadId } diff --git a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt index 01d78e1d9959..639a66ed8c75 100644 --- a/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt +++ b/app/src/main/java/com/nextcloud/client/jobs/autoUpload/AutoUploadWorker.kt @@ -23,6 +23,7 @@ import com.nextcloud.client.jobs.upload.FileUploadBroadcastManager import com.nextcloud.client.jobs.upload.FileUploadWorker import com.nextcloud.client.jobs.utils.UploadErrorNotificationManager import com.nextcloud.client.network.ConnectivityService +import com.nextcloud.utils.extensions.getLog import com.nextcloud.utils.extensions.isNonRetryable import com.nextcloud.utils.extensions.updateStatus import com.owncloud.android.R @@ -81,6 +82,8 @@ class AutoUploadWorker( syncedFolder = syncedFolderProvider.getSyncedFolderByID(syncFolderId) ?.takeIf { it.isEnabled } ?: return Result.failure() + Log_OC.d(TAG, syncedFolder.getLog()) + /** * Receives from [com.nextcloud.client.jobs.ContentObserverWork.checkAndTriggerAutoUpload] */ @@ -90,6 +93,10 @@ class AutoUploadWorker( return Result.retry() } + if (powerManagementService.isPowerSavingEnabled) { + Log_OC.w(TAG, "power saving mode enabled") + } + collectFileChangesFromContentObserverWork(contentUris) uploadFiles(syncedFolder) @@ -209,8 +216,10 @@ class AutoUploadWorker( withContext(Dispatchers.IO) { if (contentUris.isNullOrEmpty()) { + Log_OC.d(TAG, "inserting all entries") helper.insertEntries(syncedFolder, repository) } else { + Log_OC.d(TAG, "inserting changed entries") val isContentUrisStored = helper.insertChangedEntries(syncedFolder, contentUris, repository) if (!isContentUrisStored) { Log_OC.w( diff --git a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java index 713cea72d5cc..c986e115b61c 100644 --- a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java +++ b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java @@ -71,6 +71,7 @@ public void isNetworkAndServerAvailable(@NonNull GenericCallback callba if (hasInternet) { result = !isInternetWalled(); } else { + Log_OC.e(TAG, "network and server not available"); result = false; } @@ -84,6 +85,7 @@ public boolean isConnected() { NetworkCapabilities actNw = platformConnectivityManager.getNetworkCapabilities(nw); if (actNw == null) { + Log_OC.e(TAG, "network capabilities is null"); return false; } @@ -101,6 +103,7 @@ public boolean isConnected() { return true; } + Log_OC.e(TAG, "network is not connected"); return false; } @@ -108,6 +111,10 @@ public boolean isConnected() { public boolean isInternetWalled() { final Boolean cachedValue = walledCheckCache.getValue(); if (cachedValue != null) { + if (cachedValue) { + Log_OC.e(TAG, "network is walled, cached value is used"); + } + return cachedValue; } else { Server server = accountManager.getUser().getServer(); @@ -116,6 +123,8 @@ public boolean isInternetWalled() { boolean result; Connectivity c = getConnectivity(); if (c != null && c.isConnected() && c.isWifi() && !c.isMetered() && !baseServerAddress.isEmpty()) { + Log_OC.d(TAG, "checking network status"); + GetMethod get = requestBuilder.invoke(baseServerAddress + CONNECTIVITY_CHECK_ROUTE); PlainClient client = clientFactory.createPlainClient(); @@ -129,9 +138,29 @@ public boolean isInternetWalled() { " assuming connectivity is impaired"); } } else { + Log_OC.e(TAG, "cannot check network status, connectivity is not eligible"); + + if (c != null) { + if (c.isMetered()) { + Log_OC.e(TAG, "network is metered"); + } + + if (!c.isWifi()) { + Log_OC.e(TAG, "network is not connected to wi-fi"); + } + + if (!c.isConnected()) { + Log_OC.e(TAG, "network is not connected"); + } + } + result = (c != null && !c.isConnected()); } + if (result) { + Log_OC.e(TAG, "network is walled"); + } + walledCheckCache.setValue(result); return result; } @@ -143,7 +172,8 @@ public Connectivity getConnectivity() { try { networkInfo = platformConnectivityManager.getActiveNetworkInfo(); } catch (Throwable t) { - networkInfo = null; // no network available or no information (permission denied?) + Log_OC.e(TAG, "no network available or no information: ", t); + networkInfo = null; } if (networkInfo != null) { @@ -152,6 +182,19 @@ public Connectivity getConnectivity() { boolean isMetered; isMetered = isNetworkMetered(); boolean isWifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI || hasNonCellularConnectivity(); + + if (!isMetered) { + Log_OC.w(TAG, "getConnectivity(): network is metered"); + } + + if (!isWifi) { + Log_OC.w(TAG, "getConnectivity(): network is not wi-fi"); + } + + if (!isConnected) { + Log_OC.e(TAG, "getConnectivity(): network is not connected"); + } + return new Connectivity(isConnected, isMetered, isWifi, null); } else { return Connectivity.DISCONNECTED; diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt index 7f8cac806f2f..b4d309ae6893 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt @@ -11,6 +11,7 @@ import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.jobs.BackgroundJobManagerImpl import com.nextcloud.client.network.ConnectivityService import com.owncloud.android.R +import com.owncloud.android.datamodel.MediaFolderType import com.owncloud.android.datamodel.SyncedFolder import com.owncloud.android.datamodel.SyncedFolderDisplayItem import com.owncloud.android.lib.common.utils.Log_OC @@ -100,3 +101,63 @@ fun SyncedFolder.calculateScanInterval( else -> defaultIntervalMillis to null } } + +/** + * Builds a structured debug string of the SyncedFolder configuration. + * + * Important developer notes: + * + * uploadAction: + * Represents the UI option: + * 👉 "Original file will be..." + * (e.g., kept, deleted, moved after upload) + * + * nameCollisionPolicy: + * Represents the UI option: + * 👉 "What to do if the file already exists?" + * (e.g., rename, overwrite, skip) + * + * subfolderByDate: + * Represents the UI toggle: + * 👉 "Use subfolders" + * + * existing: + * Represents the UI option: + * 👉 "Also upload existing files" + * If false → only files created AFTER enabling are uploaded. + */ +fun SyncedFolder.getLog(): String { + val mediaType = when (type) { + MediaFolderType.IMAGE -> "🖼️ Images" + MediaFolderType.VIDEO -> "🎬 Videos" + MediaFolderType.CUSTOM -> "📁 Custom" + } + + return """ + 📦 Synced Folder + ───────────────────────── + 🆔 ID: $id + 👤 Account: $account + + 📂 Local: $localPath + ☁️ Remote: $remotePath + + $mediaType + 📅 Subfolder rule: ${subfolderRule ?: "None"} + 🗂️ By date: $isSubfolderByDate + 🙈 Exclude hidden: $isExcludeHidden + 👀 Hidden config: $isHidden + + 📶 Wi-Fi only: $isWifiOnly + 🔌 Charging only: $isChargingOnly + + 📤 Upload existing files: $isExisting + ⚙️ Upload action: $uploadAction + 🧩 Name collision: $nameCollisionPolicy + + ✅ Enabled: $isEnabled + 🕒 Enabled at: $enabledTimestampMs + 🔍 Last scan: $lastScanTimestampMs + ───────────────────────── + """.trimIndent() +} diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index ec5aa8f563c0..bae648167638 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -14,6 +14,7 @@ import android.content.Context; import android.net.Uri; import android.text.TextUtils; +import android.text.format.Formatter; import com.nextcloud.client.account.User; import com.nextcloud.client.device.BatteryStatus; @@ -1084,6 +1085,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { if (!result.isSuccess()) return result; if (temporalFile.length() != originalFile.length()) { + Log_OC.e(TAG, "temporal file and original file lengths are not same - result is LOCK_FAILED"); result = new RemoteOperationResult<>(ResultCode.LOCK_FAILED); } filePath = temporalFile.toPath(); @@ -1115,24 +1117,30 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { return result; } } + + final var formattedFileSize = Formatter.formatFileSize(mContext, size); updateSize(size); - Log_OC.d(TAG, "file size set to " + size); + Log_OC.d(TAG, "file size set to " + formattedFileSize); // decide whether chunked or not if (size > ChunkedFileUploadRemoteOperation.CHUNK_SIZE_MOBILE) { + Log_OC.d(TAG, "chunked upload operation will be used"); + boolean onWifiConnection = connectivityService.getConnectivity().isWifi(); mUploadOperation = new ChunkedFileUploadRemoteOperation( mFile.getStoragePath(), mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), lastModifiedTimestamp, creationTimestamp, onWifiConnection, mDisableRetries); } else { + Log_OC.d(TAG, "upload file operation will be used"); + mUploadOperation = new UploadFileRemoteOperation( mFile.getStoragePath(), mFile.getRemotePath(), mFile.getMimeType(), mFile.getEtagInConflict(), lastModifiedTimestamp, creationTimestamp, mDisableRetries); } - Log_OC.d(TAG, "upload operation determined"); + Log_OC.d(TAG, "upload type operation determined"); /** * Adds the onTransferProgress in FileUploadWorker @@ -1166,12 +1174,14 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } } } catch (FileNotFoundException e) { - Log_OC.e(TAG, mOriginalStoragePath + " not exists anymore"); + Log_OC.e(TAG, "normalupload(): file not found exception"); result = new RemoteOperationResult<>(ResultCode.LOCAL_FILE_NOT_FOUND); } catch (Exception e) { - Log_OC.e(TAG, "normal upload exception: ", e); + Log_OC.e(TAG, "normalupload(): exception: ", e); result = new RemoteOperationResult<>(e); } finally { + Log_OC.d(TAG, "normalupload(): finally block"); + mUploadStarted.set(false); // clean up temporal file if it exists @@ -1188,6 +1198,7 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } if (result == null) { + Log_OC.e(TAG, "result is null, UNKNOWN_ERROR"); result = new RemoteOperationResult<>(ResultCode.UNKNOWN_ERROR); } @@ -1201,6 +1212,8 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); } + Log_OC.d(TAG, "returning normalupload() result"); + return result; } @@ -1337,6 +1350,7 @@ private void handleLocalBehaviour(File temporalFile, switch (mLocalBehaviour) { case FileUploadWorker.LOCAL_BEHAVIOUR_DELETE: + Log_OC.d(TAG, "DELETE local behaviour will be handled"); try { Files.delete(originalFile.toPath()); } catch (IOException e) { @@ -1348,6 +1362,7 @@ private void handleLocalBehaviour(File temporalFile, break; case FileUploadWorker.LOCAL_BEHAVIOUR_COPY: + Log_OC.d(TAG, "COPY local behaviour will be handled"); if (temporalFile != null) { try { move(temporalFile, expectedFile); @@ -1372,6 +1387,7 @@ private void handleLocalBehaviour(File temporalFile, break; case FileUploadWorker.LOCAL_BEHAVIOUR_MOVE: + Log_OC.d(TAG, "MOVE local behaviour will be handled"); String expectedPath = FileStorageUtils.getDefaultSavePathFor(user.getAccountName(), mFile); File newFile = new File(expectedPath); @@ -1389,6 +1405,7 @@ private void handleLocalBehaviour(File temporalFile, break; default: + Log_OC.d(TAG, "DEFAULT local behaviour will be handled"); mFile.setStoragePath(""); saveUploadedFile(client); break; From f759ac3525e84bf5c01cc32e00f023d459492754 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 13:27:56 +0100 Subject: [PATCH 77/83] add auto upload logs Signed-off-by: alperozturk96 --- .../com/nextcloud/client/network/ConnectivityServiceImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java index c986e115b61c..ad6f07a0456b 100644 --- a/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java +++ b/app/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java @@ -183,7 +183,7 @@ public Connectivity getConnectivity() { isMetered = isNetworkMetered(); boolean isWifi = networkInfo.getType() == ConnectivityManager.TYPE_WIFI || hasNonCellularConnectivity(); - if (!isMetered) { + if (isMetered) { Log_OC.w(TAG, "getConnectivity(): network is metered"); } From e6df4b3d6af91dfce6e4edf98e138353e1049ec0 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 13:28:34 +0100 Subject: [PATCH 78/83] add auto upload logs Signed-off-by: alperozturk96 --- .../com/owncloud/android/operations/UploadFileOperation.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index bae648167638..cfe827731dee 100644 --- a/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/app/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1048,7 +1048,6 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { return result; } - Log_OC.d(TAG, "checking name collision"); final var collisionResult = checkNameCollision(null, client, null, false); if (collisionResult != null) { Log_OC.e(TAG, "name collision detected"); From b464e5363eb40767166f2884e2fcd0e6e0bbc8c5 Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Wed, 18 Feb 2026 13:31:18 +0100 Subject: [PATCH 79/83] add auto upload logs Signed-off-by: alperozturk96 --- .../com/nextcloud/utils/extensions/SyncedFolderExtensions.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt index b4d309ae6893..991b9b0ee904 100644 --- a/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt +++ b/app/src/main/java/com/nextcloud/utils/extensions/SyncedFolderExtensions.kt @@ -105,8 +105,6 @@ fun SyncedFolder.calculateScanInterval( /** * Builds a structured debug string of the SyncedFolder configuration. * - * Important developer notes: - * * uploadAction: * Represents the UI option: * 👉 "Original file will be..." From dab02afab6e20ccdbc18c0441c5c5a14a0a732b8 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 19 Feb 2026 03:16:13 +0000 Subject: [PATCH 80/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-uk/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2c67d919df76..35fe49aeae68 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -52,7 +52,7 @@ Дійсно вилучити це завдання? Вилучити завдання Спробуйте надіслати повідомлення, щоб розпочати розмову. - Привіт! Чим я можу вам сьогодні допомогти? + Вітаю! Чим я можу вам сьогодні допомогти? Виберіть завдання Надіслати повідомлення Відкрити список розмов From bf2fa009578eb022522253a5d27359b7a4d68a24 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Thu, 19 Feb 2026 09:05:28 +0100 Subject: [PATCH 81/83] Logs: allow to export Signed-off-by: tobiasKaminsky --- .../client/logger/ui/LogsActivity.kt | 1 + .../client/logger/ui/LogsEmailSender.kt | 73 +++++++++++++++++++ .../client/logger/ui/LogsViewModel.kt | 6 ++ app/src/main/res/menu/activity_logs.xml | 9 ++- app/src/main/res/values/strings.xml | 1 + 5 files changed, 89 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt index 214750cbcffc..cac5c86fca11 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt @@ -85,6 +85,7 @@ class LogsActivity : ToolbarActivity() { android.R.id.home -> finish() R.id.action_delete_logs -> vm.deleteAll() R.id.action_send_logs -> vm.send() + R.id.action_export_logs -> vm.export() R.id.action_refresh_logs -> vm.load() else -> retval = super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt index cbcdd2c8c734..9231e402dd5a 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt @@ -6,20 +6,28 @@ */ package com.nextcloud.client.logger.ui +import android.app.DownloadManager +import android.app.NotificationManager +import android.app.PendingIntent import android.content.ActivityNotFoundException import android.content.Context import android.content.Intent +import android.content.Intent.FLAG_ACTIVITY_NEW_TASK import android.net.Uri import android.os.Build import android.widget.Toast +import androidx.core.app.NotificationCompat import androidx.core.content.FileProvider import com.nextcloud.client.core.AsyncRunner import com.nextcloud.client.core.Cancellable import com.nextcloud.client.core.Clock import com.nextcloud.client.logger.LogEntry import com.owncloud.android.R +import com.owncloud.android.ui.notifications.NotificationUtils +import com.owncloud.android.utils.FileExportUtils import java.io.File import java.io.FileWriter +import java.security.SecureRandom import java.util.TimeZone class LogsEmailSender(private val context: Context, private val clock: Clock, private val runner: AsyncRunner) { @@ -59,6 +67,16 @@ class LogsEmailSender(private val context: Context, private val clock: Clock, pr } } + fun export(logs: List) { + if (task == null) { + val outFile = File(context.cacheDir, "attachments/logs.txt") + task = runner.postQuickTask(Task(context, logs, outFile, clock.tz), onResult = { + task = null + export(outFile) + }) + } + } + fun stop() { if (task != null) { task?.cancel() @@ -66,6 +84,61 @@ class LogsEmailSender(private val context: Context, private val clock: Clock, pr } } + private fun export(file: File) { + FileExportUtils().exportFile( + "Nextcloud Android Files Logs", + "text/plain", + context.contentResolver, + null, + file + ) + showSuccessNotification(1) + } + + fun showSuccessNotification(successfulExports: Int) { + showNotification( + context.resources.getQuantityString( + R.plurals.export_successful, + successfulExports, + successfulExports + ) + ) + } + + private fun showNotification(message: String) { + val notificationId = SecureRandom().nextInt() + + val notificationBuilder = NotificationCompat.Builder( + context, + NotificationUtils.NOTIFICATION_CHANNEL_DOWNLOAD + ) + .setSmallIcon(R.drawable.notification_icon) + .setContentTitle(message) + .setAutoCancel(true) + + val actionIntent = Intent(DownloadManager.ACTION_VIEW_DOWNLOADS).apply { + flags = FLAG_ACTIVITY_NEW_TASK + } + val actionPendingIntent = PendingIntent.getActivity( + context, + notificationId, + actionIntent, + PendingIntent.FLAG_CANCEL_CURRENT or + PendingIntent.FLAG_IMMUTABLE + ) + notificationBuilder.addAction( + NotificationCompat.Action( + null, + context.getString(R.string.locate_folder), + actionPendingIntent + ) + ) + + val notificationManager = context + .getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.notify(notificationId, notificationBuilder.build()) + } + private fun send(uri: Uri?) { task = null val intent = Intent(Intent.ACTION_SEND_MULTIPLE) diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt index 46e4a8cbd566..6831aed28f23 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsViewModel.kt @@ -48,6 +48,12 @@ class LogsViewModel @Inject constructor( } } + fun export() { + entries.value?.let { + sender.export(it) + } + } + fun load() { if (isLoading.value != true) { logsRepository.load(this::onLoaded) diff --git a/app/src/main/res/menu/activity_logs.xml b/app/src/main/res/menu/activity_logs.xml index 08a14cf62093..1fc5f4bbe9b6 100644 --- a/app/src/main/res/menu/activity_logs.xml +++ b/app/src/main/res/menu/activity_logs.xml @@ -25,7 +25,14 @@ android:title="@string/logs_menu_send" app:showAsAction="never" android:orderInCategory="200" - android:icon="@drawable/ic_send"/> + android:icon="@drawable/ic_send" /> + + Logs Refresh Send logs by email + Export logs Delete logs No app for sending logs found. Please install an email client. %1$s Android app logs From e31f00cb53dea1e81f6f8cc57158111d5b05c05f Mon Sep 17 00:00:00 2001 From: alperozturk96 Date: Thu, 19 Feb 2026 09:24:32 +0100 Subject: [PATCH 82/83] use output stream with auto close and check nullability of parent dir, remove unused values Signed-off-by: alperozturk96 --- .../client/logger/ui/LogsEmailSender.kt | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt b/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt index 9231e402dd5a..9055ee527f5e 100644 --- a/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt +++ b/app/src/main/java/com/nextcloud/client/logger/ui/LogsEmailSender.kt @@ -26,7 +26,6 @@ import com.owncloud.android.R import com.owncloud.android.ui.notifications.NotificationUtils import com.owncloud.android.utils.FileExportUtils import java.io.File -import java.io.FileWriter import java.security.SecureRandom import java.util.TimeZone @@ -44,13 +43,17 @@ class LogsEmailSender(private val context: Context, private val clock: Clock, pr ) : Function0 { override fun invoke(): Uri? { - file.parentFile.mkdirs() - val fo = FileWriter(file, false) - logs.forEach { - fo.write(it.toString(tz)) - fo.write("\n") + file.parentFile?.mkdirs() + + file.outputStream().use { outputStream -> + outputStream.writer(Charsets.UTF_8).buffered().use { writer -> + logs.forEach { + writer.write(it.toString(tz)) + writer.newLine() + } + } } - fo.close() + return FileProvider.getUriForFile(context, context.getString(R.string.file_provider_authority), file) } } @@ -149,12 +152,12 @@ class LogsEmailSender(private val context: Context, private val clock: Clock, pr intent.putExtra(Intent.EXTRA_TEXT, getPhoneInfo()) - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.flags = FLAG_ACTIVITY_NEW_TASK intent.type = LOGS_MIME_TYPE intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, arrayListOf(uri)) try { context.startActivity(intent) - } catch (ex: ActivityNotFoundException) { + } catch (_: ActivityNotFoundException) { Toast.makeText(context, R.string.log_send_no_mail_app, Toast.LENGTH_SHORT).show() } } From 30426153324c2aa27e2f0252b7d9e0ae76647835 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sat, 21 Feb 2026 03:03:34 +0000 Subject: [PATCH 83/83] fix(l10n): Update translations from Transifex Signed-off-by: Nextcloud bot --- app/src/main/res/values-b+en+001/strings.xml | 1 + app/src/main/res/values-cs-rCZ/strings.xml | 1 + app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values-es/strings.xml | 1 + app/src/main/res/values-gl/strings.xml | 1 + app/src/main/res/values-pl/strings.xml | 1 + app/src/main/res/values-uk/strings.xml | 1 + app/src/main/res/values-zh-rHK/strings.xml | 1 + 8 files changed, 8 insertions(+) diff --git a/app/src/main/res/values-b+en+001/strings.xml b/app/src/main/res/values-b+en+001/strings.xml index 08de0f690b24..10e3f74b5aeb 100644 --- a/app/src/main/res/values-b+en+001/strings.xml +++ b/app/src/main/res/values-b+en+001/strings.xml @@ -582,6 +582,7 @@ Log in The link to your %1$s web interface when you open it in the browser. Delete logs + Export logs Refresh Search logs Send logs by email diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 4a6de3185b37..f78074b036b4 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -582,6 +582,7 @@ Přihlásit Odkaz na webové rozhraní vámi využívané instance %1$s, když ho otevíráte ve webovém prohlížeči. Smazat záznamy událostí + Exportovat záznamy událostí Načíst znovu Prohledat záznamy událostí Posílat záznamy událostí e-mailem diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index f1a29ffab7c0..cd3a2dcd9b70 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -582,6 +582,7 @@ Anmelden Der Link zu Ihrer %1$s Webseite, wenn Sie diese im Browser öffnen. Lösche Logs + Protokolle exportieren Aktualisieren Suche Logs Protokolldateien per E-Mail versenden diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index 76d3fb24b8bb..5cdb36e79ed2 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -582,6 +582,7 @@ Iniciar sesión El enlace a tu interface web %1$s cuando lo abres en tu navegador. Eliminar registros + Exportar registros Recargar Buscar registros Enviar los registros por email diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml index 17a860fb3e45..fd0c7383b839 100644 --- a/app/src/main/res/values-gl/strings.xml +++ b/app/src/main/res/values-gl/strings.xml @@ -582,6 +582,7 @@ Acceder A ligazón á súa interface web %1$s cando a abre no navegador. Eliminar rexistros + Exportar os rexistros Actualizar Buscar rexistros Enviar rexistros por correo-e diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml index 5d90c39ba71c..eee6e3c1765c 100644 --- a/app/src/main/res/values-pl/strings.xml +++ b/app/src/main/res/values-pl/strings.xml @@ -582,6 +582,7 @@ Zaloguj Link do interfejsu internetowego %1$s, aby otworzyć w przeglądarce. Usuń dziennik + Eksportuj logi Odśwież Przeszukaj dziennik Wyślij dziennik e-mailem diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 35fe49aeae68..580b3f8f8d1a 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -582,6 +582,7 @@ Увійти Посилання на ваш веб-інтерфейс %1$s, коли ви відкриєте його у веб-переглядачі. Вилучити журнал + Експорт журналу Оновити Шукати у журналі Надіслати журнал ел.поштою diff --git a/app/src/main/res/values-zh-rHK/strings.xml b/app/src/main/res/values-zh-rHK/strings.xml index 57c8c1f17d12..4579bf190498 100644 --- a/app/src/main/res/values-zh-rHK/strings.xml +++ b/app/src/main/res/values-zh-rHK/strings.xml @@ -582,6 +582,7 @@ 登入 在瀏覽器中打開 %1$s 網絡界面的連結。 刪除紀錄檔案 + 導出記錄 重整 搜尋紀錄檔案 通過電郵發送紀錄