From: tobiasKaminsky Date: Sat, 31 Oct 2015 07:48:10 +0000 (+0100) Subject: Merge remote-tracking branch 'remotes/upstream/master' into beta X-Git-Tag: beta-20151122~66 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/495fd6e4041c6ed933022e49d1aee0a30a9d6f76?hp=1998cc68d6586c6a0b4bc4c9be42b48d17fa5ba7 Merge remote-tracking branch 'remotes/upstream/master' into beta --- diff --git a/owncloud-android-library b/owncloud-android-library index 652cd28b..59fb6160 160000 --- a/owncloud-android-library +++ b/owncloud-android-library @@ -1 +1 @@ -Subproject commit 652cd28bb15672eaedfe8c1d9a46cf293c909b89 +Subproject commit 59fb61601de4dd8bfcab1afb619e016e1a7b904d diff --git a/res/drawable/conflict_file_indicator.png b/res/drawable/conflict_file_indicator.png new file mode 100644 index 00000000..fd580caf Binary files /dev/null and b/res/drawable/conflict_file_indicator.png differ diff --git a/res/drawable/synchronizing_file_indicator.png b/res/drawable/synchronizing_file_indicator.png new file mode 100644 index 00000000..947cba73 Binary files /dev/null and b/res/drawable/synchronizing_file_indicator.png differ diff --git a/res/menu/file_actions_menu.xml b/res/menu/file_actions_menu.xml index 4a295d53..0a49e020 100644 --- a/res/menu/file_actions_menu.xml +++ b/res/menu/file_actions_menu.xml @@ -45,13 +45,8 @@ android:icon="@drawable/ic_action_refresh" android:orderInCategory="1" /> - انشئ في : عُدل في : تحميل - تحديث الملف تم تغيير اسم الملف إلى %1$s أثناء الرفع شارك الرابط الغاء مشاركة الرابط نعم لا تم - إلغاء التحميل - إلغاء الرفع إلغاء حفظ + خروج خطأ diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index 75b0faca..cff94283 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -72,15 +72,12 @@ Yaradıldı: Dəyişdirildi: Yüklə - Faylı yenilə Yüklənmə müddətində fayl buna %1$s yeniləndi Linki yayımla Link yayımlanmasını dayandır Bəli Xeyir Oldu - Endirimi dayandır - Yüklənməni dayandır Dayandır Saxla & Çıx Səhv @@ -288,7 +285,6 @@ inzibatçınızla əlaqə saxlayasınız. Anında yükləmələr Təhlükəsizlik Video ünvanını yüklə - Qovluğun endirilməsinin %1$s hissəsi tamamlana bilməz Qoşulmanı yenilə Server ünvanı diff --git a/res/values-bg-rBG/strings.xml b/res/values-bg-rBG/strings.xml index e0ef8416..e4f83808 100644 --- a/res/values-bg-rBG/strings.xml +++ b/res/values-bg-rBG/strings.xml @@ -78,7 +78,6 @@ Създаден на: Променен на: Изтегляне - Обновяване на файла Файлът беше преименуван на %1$s по време на качването. Списък с изгледи Връзка за споделяне @@ -86,8 +85,6 @@ Да Не ОК - Отказване на тегленето - Отказване на качването Отказ Запазване и изход Грешка @@ -304,7 +301,6 @@ Незабавно качване Сигурност Качване на видео път - Свалянето на директорията %1$s не може да бъде завършено споделен с теб %1$s споделен \"%2$s\" с теб diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index be8c7176..bb44ce1b 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -63,15 +63,12 @@ তৈরীর নির্ঘন্টঃ পরিবর্তিতঃ ডাউনলোড - ফাইল নবোদ্যম করুন আপলোডের সময় ফাইলের পূণঃনামকরণ করা হয়েছে %1$s লিংক ভাগাভাগি করেন লিংক ছিনন করেন হ্যাঁ না তথাস্তু - ডাউনলোড বাতিল করেন - আপলোড বাতিল কর বাতিল সংরক্ষণ কর এবং &প্রস্থান সমস্যা diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 8c01ccc4..6ab99c93 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -24,7 +24,6 @@ Da Ne Ok - Prekini učitavanje Odustani Greška Nepoznata greška diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 411a3838..6c197180 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -72,15 +72,12 @@ Creat: Modificat: Baixa - Actualitza el fitxer L\'arxiu s\'ha canviat de nom a %1$s durant la càrrega Enllaç de compartició Deixa de compartir l\'enllaç Sí No D\'acord - Cancelar la descàrrega - Cancel·la la pujada Cancel·la Desa & Surt Error diff --git a/res/values-cs-rCZ/strings.xml b/res/values-cs-rCZ/strings.xml index 02edb32a..88f7b5b7 100644 --- a/res/values-cs-rCZ/strings.xml +++ b/res/values-cs-rCZ/strings.xml @@ -79,7 +79,6 @@ Vytvořen: Upraven: Stáhnout - Obnovit soubor Soubor byl v průběhu odesílání přejmenován na %1$s Náhled seznamu Sdílet odkaz @@ -87,8 +86,6 @@ Ano Ne OK - Zrušit stahování - Zrušit odesílání Zrušit Uložit a ukončit Chyba @@ -315,7 +312,6 @@ správce systému. Okamžitá odesílání Zabezpečení Cesta pro nahrávání videí - Stažení adresáře %1$s nemohlo být dokončeno sdílené s vámi %1$s s vámi sdílí \"%2$s\" diff --git a/res/values-cy-rGB/strings.xml b/res/values-cy-rGB/strings.xml index e7e46fec..7a533cda 100644 --- a/res/values-cy-rGB/strings.xml +++ b/res/values-cy-rGB/strings.xml @@ -42,8 +42,6 @@ Ie Na Iawn - Diddymu llwytho i lawr - Diddymu llwytho i fyny Diddymu Cadw & Gadael Gwall diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index b6b80b61..fab5433d 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -78,7 +78,6 @@ Oprettet: Ændret: Hent - Genopfrisk fil Filen blev omdøbt til %1$s under upload Listevisning Del link @@ -86,8 +85,6 @@ Ja Nej OK - Afbryd download - Fortryd upload Annuller Gem & Afslut Fejl @@ -314,7 +311,6 @@ Øjeblikkelige uploads Sikkerhed Sti til videoupload - Download af %1$s mappe kunne ikke fuldføres delt med dig %1$s delte \"%2$s\" med dig diff --git a/res/values-de-rAT/strings.xml b/res/values-de-rAT/strings.xml index a99ab891..43963466 100644 --- a/res/values-de-rAT/strings.xml +++ b/res/values-de-rAT/strings.xml @@ -59,14 +59,11 @@ Erstellt am: Verändert am: Herunterladen - Datei neu laden Link teilen Link nicht mehr teilen Ja Nein OK - Herunterladen abbrechen - Hochladen abbrechen Abbrechen Speichern & Schließen Fehler diff --git a/res/values-de-rDE/strings.xml b/res/values-de-rDE/strings.xml index e1b85fa6..fe3762ad 100644 --- a/res/values-de-rDE/strings.xml +++ b/res/values-de-rDE/strings.xml @@ -78,7 +78,6 @@ Erstellt: Geändert: Herunterladen - Datei aktualisieren Datei wurde wärend des Uploads zu %1$s umbenannt Listen-Layout Link teilen @@ -86,8 +85,6 @@ Ja Nein OK - Download abbrechen - Upload abbrechen Abbrechen Speichern & Schließen Fehler @@ -307,7 +304,6 @@ Sofortiges Hochladen Sicherheit Verzeichnis zum Hochladen der Videos - Herunterladen des %1$s - Ordners konnte nicht abgeschlossen werden geteilt Mit Ihnen %1$s hat \"%2$s\" mit Ihnen geteilt diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index db368755..2ffd10f4 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -79,7 +79,6 @@ Erstellt: Geändert: Herunterladen - Datei aktualisieren Datei wurde wärend des Uploads zu %1$s umbenannt Listen-Layout Link teilen @@ -87,8 +86,6 @@ Ja Nein OK - Download abbrechen - Upload abbrechen Abbrechen Speichern & schließen Fehler @@ -316,7 +313,6 @@ Sofortiges Hochladen Sicherheit Verzeichnis zum Hochladen der Videos - Herunterladen des %1$s - Ordners konnte nicht abgeschlossen werden geteilt Mit Dir %1$s hat \"%2$s\" mit Dir geteilt diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index cf3f3a0f..ee5937eb 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -79,7 +79,6 @@ Δημιουργήθηκε: Τροποποιήθηκε: Λήψη - Ανανέωση αρχείου Το αρχείο μετονομάστηκε σε %1$s κατά τη μεταφόρτωση Διάταξη Λίστας Διαμοιρασμός συνδέσμου @@ -87,8 +86,6 @@ Ναι Όχι ΟΚ - Ακύρωση λήψης - Ακύρωση μεταφόρτωσης Άκυρο Αποθήκευση & Έξοδος Σφάλμα @@ -316,7 +313,6 @@ Στιγμιαίες Μεταφορτώσεις Ασφάλεια Διαδρομή Μεταφόρτωσης Βίντεο - Η λήψη του φακέλου %1$s δεν ολοκληρώθηκε με επιτυχία. διαμοιρασμένα με εσάς Ο %1$s διαμοιράστηκε το \"%2$s\" με εσάς diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index ee9fc15a..164b1541 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -73,15 +73,12 @@ Created: Modified: Download - Refresh file File was renamed to %1$s during upload Share link Unshare link Yes No OK - Cancel download - Cancel upload Cancel Save & Exit Error @@ -298,7 +295,6 @@ Instant Uploads Security Upload Video Path - Download of %1$s folder could not be completed Refresh connection Server address Not enough memory diff --git a/res/values-eo/strings.xml b/res/values-eo/strings.xml index 774ce690..392cd138 100644 --- a/res/values-eo/strings.xml +++ b/res/values-eo/strings.xml @@ -53,8 +53,6 @@ Jes Ne Akcepti - Nuligi elŝuton - Nuligi alŝuton Nuligi Konservi kaj forlasi Eraro diff --git a/res/values-es-rAR/strings.xml b/res/values-es-rAR/strings.xml index e4903a21..13f62744 100644 --- a/res/values-es-rAR/strings.xml +++ b/res/values-es-rAR/strings.xml @@ -72,15 +72,12 @@ Creado: Modificado: Descargar - Actualizar archivo El archivo fue renombrado como %1$s durante la subida Compartir vínculo Dejar de compartir vínculo Sí No Aceptar - Cancelar descarga - Cancelar subida Cancelar Guardar y salir Error @@ -278,6 +275,5 @@ Subida Instantánea Seguridad Dirección de subida del video - La descarga de la carpeta %1$s no pudo ser completada Dirección del servidor diff --git a/res/values-es-rCL/strings.xml b/res/values-es-rCL/strings.xml index 3aa399c8..441831cf 100644 --- a/res/values-es-rCL/strings.xml +++ b/res/values-es-rCL/strings.xml @@ -43,13 +43,10 @@ Creado: Modificado: Descargar - refrescar archivo El archivo fue renombrado a %1$s durante la subida Si No OK - cancelar bajada - cancelar subida Cancelar Guardar&Salir Error diff --git a/res/values-es-rMX/strings.xml b/res/values-es-rMX/strings.xml index 84ab10c6..4a76217c 100644 --- a/res/values-es-rMX/strings.xml +++ b/res/values-es-rMX/strings.xml @@ -58,14 +58,11 @@ Creado: Modificado: Descargar - Actualizar archivo El archivo fue renombrado como %1$s durante la subida Enlace compartido Sí No Aceptar - Cancelar descarga - Cancelar subida Cancelar Guardar & Salir Error diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 64e89328..ced37370 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -79,7 +79,6 @@ Creado: Modificado: Descargar - Actualizar archivo El fichero fue renombrado como %1$s durante la subida Diseño de lista Compartir con enlace @@ -87,8 +86,6 @@ Sí No Aceptar - Cancelar descarga - Cancelar subida Cancelar Guardar & Salir Error @@ -316,7 +313,6 @@ Subidas instantáneas Seguridad Guardar videos subidos en la carpeta: - La descarga de la carpeta %1$s no ha podido ser completada compartido con usted %1$s compartió \"%2$s\" conmigo diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index 93954750..fc6d949b 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -78,7 +78,6 @@ Loodud: Muudetud: Lae alla - Värskenda faili Fail nimetati üleslaadimise käigus ümber %1$ Nimekirja paigutus Jaga linki @@ -86,8 +85,6 @@ Jah Ei OK - Tühista allalaadimine - Tühista üleslaadimine Loobu Salvesta & Välju Viga @@ -319,7 +316,6 @@ Allpool on loend kohalikest failidest ning serveris asuvatest failidest %5$s, mi Kohesed üleslaadimised Turvalisus Video üleslaadimise asukoht - Kausta %1$s allalaadimine ei õnnestunud jagatud sinuga %1$s jagas sinuga \"%2$s\" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index fd4172e8..3f9e5cf0 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -70,15 +70,12 @@ Sortuta: Aldatuta: Deskargatu - Freskatu fitxaegia Fitxategiaren izena %1$sra aldatu da igotzean Elkarbanatu lotura Lotura partekatzeari utzi Bai Ez Ados - Utzi deskarga bertan behera - Ezeztatu igoera Ezeztatu Gorde eta Irten Errorea @@ -278,6 +275,5 @@ Mesedez, baimendu berriz Berehalako Igoerak Segurtasuna Bideo Igoera Bidea - %1$s karpetaren deskarga ezin izan da burutu Zerbitzariaren helbidea diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 3f833b33..42024954 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -70,15 +70,12 @@ ایجاد شده توسط: تغییر یافته توسط: بارگیری - بازنمایی فایل فایل در هنگام بارگزاری به %1$s تغییر نام یافت اشتراک گذاشتن لینک لغو اشتراک گذاشتن لینک بله نه باشه - قطع دانلود - متوقف کردن بار گذاری منصرف شدن ذخیره سازی و خروج خطا @@ -282,7 +279,6 @@ آپلودهای فوری امنیت مسیر آپلود ویدئو - امکان تکمیل دانلود پوشه %1$s وجود ندارد به اشتراک گذاشته شد با تو \"%2$s\" توسط %1$s با شما به اشتراک گذاشته شد diff --git a/res/values-fi-rFI/strings.xml b/res/values-fi-rFI/strings.xml index c9ef9134..873055e1 100644 --- a/res/values-fi-rFI/strings.xml +++ b/res/values-fi-rFI/strings.xml @@ -79,7 +79,6 @@ Luotu: Muokattu: Lataa - Päivitä tiedosto Tiedoston nimeksi muutettiin %1$s siirron yhteydessä Luettelon asettelu Jaa linkki @@ -87,8 +86,6 @@ Kyllä Ei OK - Peru lataus - Peru lähetys Peru Tallenna ja poistu Virhe diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index f91bb679..f8a303ab 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -80,7 +80,6 @@ Téléchargez-le ici : %2$s Créé le : Modifié le : Télécharger - Actualiser le fichier Le fichier a été renommé en %s pendant le téléversement Affichage en liste Partager le lien @@ -88,8 +87,6 @@ Téléchargez-le ici : %2$s Oui Non OK - Annuler le téléchargement - Annuler le téléversement Annuler Sauvegarder & Quitter Erreur @@ -320,7 +317,6 @@ Ci-dessous la liste des fichiers locaux, et les fichiers distants dans %5$s auxq Téléversement immédiat Sécurité Répertoire de téléversement des vidéos - Le téléchargement du dossier %1$s n\'a pas pu être achevé a partagé avec vous %1$s a partagé \"%2$s\" avec vous diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 2a70987e..5c336896 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -79,7 +79,6 @@ Descárgueo de aquí: %2$s Creado: Modificado: Descargar - Actualizar o ficheiro O ficheiro foi renomeado a %1$s durante o envío Deseño da lista Ligazón para compartir @@ -87,8 +86,6 @@ Descárgueo de aquí: %2$s Si Non Aceptar - Cancelar a descarga - Cancelar o envío Cancelar Gardar e saír Erro @@ -311,7 +308,6 @@ Descárgueo de aquí: %2$s Envío instantáneo Seguridade Enviar a ruta do vídeo - Non foi posíbel completar a descarga do cartafol %1$s compartido con vostede %1$s compartiu «%2$s» con vostede diff --git a/res/values-he/strings.xml b/res/values-he/strings.xml index a8a6cfa8..09d3d11b 100644 --- a/res/values-he/strings.xml +++ b/res/values-he/strings.xml @@ -63,15 +63,12 @@ מועד היצירה: מועד השינוי: הורדה - רענון קובץ שם הקובץ השתנה ל־ %1$s במהלך ההעלאה קישור לשיתוף ביטול קישור לשיתוף כן לא אישור - ביטול ההורדה - ביטול ההעלאה ביטול לשמור ולצאת שגיאה diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 12e7ef43..13403e8e 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -47,7 +47,6 @@ Da Ne U redu - Prekini upload Odustani Greška Nepoznata pogreška diff --git a/res/values-hu-rHU/strings.xml b/res/values-hu-rHU/strings.xml index a94165e9..7315af5a 100644 --- a/res/values-hu-rHU/strings.xml +++ b/res/values-hu-rHU/strings.xml @@ -74,15 +74,12 @@ Készült: Módosítva: Letöltés - File frissítése A feltöltés során az állmányt erre neveztük át: %1$s Megosztás hivatkozással Megosztás visszavonása Igen Nem OK - A letöltés megszakítása - A feltöltés megszakítása Mégsem Mentés & Kilépés Hiba diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 64bbe0be..1ed55167 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -1,5 +1,6 @@ + տարբերակ %1$s Ֆայլեր Նոր պանակ Ուղարկել @@ -9,15 +10,31 @@ + Փակել Բացել Գաղտնաբառ Ֆայլեր Նոր պանակ + վրկ. առաջ + Չափս. Բեռնել Կիսվել հղմամբ + Այո + Ոչ Չեղարկել Վերանվանել Երկիր. + Ումից. + Ում. + 389 ԿԲ Ուղարկել + 1 պանակ + %1$d պանակ + 1 ֆայլ + 1 ֆայլ, 1 պանակ + 1 ֆայլ, %1$d պանակ + %1$d ֆայլ + %1$d ֆայլ, 1 պանակ + %1$d ֆայլ, %2$d պանակ diff --git a/res/values-ia/strings.xml b/res/values-ia/strings.xml index 3685fdff..abaae5c4 100644 --- a/res/values-ia/strings.xml +++ b/res/values-ia/strings.xml @@ -33,7 +33,6 @@ Si No Ok - Cancellar discarga Cancellar Error Error Incognite diff --git a/res/values-id/strings.xml b/res/values-id/strings.xml index 3ba812c3..1d919f4a 100644 --- a/res/values-id/strings.xml +++ b/res/values-id/strings.xml @@ -79,7 +79,6 @@ Dibuat: Diubah: Unduh - Segarkan berkas Berkas diubah namanya menjadi %1$s saat pengunggahan Daftar Tata Letak Bagikan tautan @@ -87,8 +86,6 @@ Ya Tidak Oke - Batal mengunduh - Batal mengunggah Batal Simpan & Keluar Kesalahan @@ -316,7 +313,6 @@ Unggah Cepat Keamanan Unggah Lokasi Video - Mengunduh folder %1$s tidak selesai dibagikan kepada Anda %1$s dibagikan \"%2$s\" kepada Anda diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 2d50d6fd..30fe19e8 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -24,7 +24,6 @@ Já Nei Í lagi - Hætta við innsendingu Hætta við Villa Breyta lykilorði diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 8efdd438..815e82f3 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -79,7 +79,6 @@ Creato: Modificato: Scarica - Aggiorna file Il file è stato rinominato in %1$s durante il caricamento Struttura elenco Condividi collegamento @@ -87,8 +86,6 @@ Sì No OK - Annulla lo scaricamento - Annulla caricamento Annulla Salva ed esci Errore @@ -316,7 +313,6 @@ Caricamenti istantanei Protezione Percorso di caricamento video - Lo scaricamento della cartella %1$s non può essere completato condiviso con te %1$s ha condiviso \"%2$s\" con te diff --git a/res/values-ja-rJP/strings.xml b/res/values-ja-rJP/strings.xml index 10d74054..e33664af 100644 --- a/res/values-ja-rJP/strings.xml +++ b/res/values-ja-rJP/strings.xml @@ -79,7 +79,6 @@ 作成: 更新: ダウンロード - ファイルを同期 アップロード中にファイル名を %1$s に変更しました リストレイアウト URLで共有 @@ -87,8 +86,6 @@ はい いいえ OK - ダウンロードをキャンセル - アップロードをキャンセル キャンセル 保存して終了 エラー @@ -308,7 +305,6 @@ 自動アップロード セキュリティ 動画のアップロードパス - %1$s フォルダーのダウンロードが完了しませんでした 共有中 あなたと %1$s は \"%2$s\" をあなたと共有しました diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 0d7d8601..b7b7c7bb 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -46,8 +46,6 @@ კი არა დიახ - ჩამოტვირთვის შეჩერება - ატვირთვის გაუქმება გაუქმება შენახვა &გამოსვლა შეცდომა diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index 68b39212..1c2cd314 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -58,8 +58,6 @@ ព្រម ទេ OK - បោះបង់ការទាញយក - បោះបង់​ការ​ផ្ទុកឡើង លើកលែង រក្សាទុក & ចាកចេញ កំហុស diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index ef5e51c7..51535d9d 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -25,7 +25,6 @@ ಹೌದು ಇಲ್ಲ ಸರಿ - ವರ್ಗಾವಣೆ ರದ್ದು ಮಾಡಿ ರದ್ದು ತಪ್ಪಾಗಿದೆ ಗೊತ್ತಿಲ್ಲದ ದೋಷ diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 2b1e0000..cbde7a66 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -79,7 +79,6 @@ 만든 날짜: 수정한 날짜: 다운로드 - 파일 새로 고침 업로드 중 파일 이름을 %1$s(으)로 변경하였습니다 목록 레이아웃 링크 공유 @@ -87,8 +86,6 @@ 예 아니요 확인 - 다운로드 취소 - 업로드 취소 취소 저장하고 끝내기 오류 @@ -315,7 +312,6 @@ 즉시 업로드 보안 동영상 업로드 경로 - 폴더 %1$s을(를) 다운로드할 수 없음 공유됨 여러분과 %1$s 님이 \"%2$s\" 항목을 여러분과 공유하였습니다 diff --git a/res/values-lb/strings.xml b/res/values-lb/strings.xml index 5366088a..5c4010d0 100644 --- a/res/values-lb/strings.xml +++ b/res/values-lb/strings.xml @@ -62,13 +62,10 @@ Erstallt: Geännert: Eroflueden - Fichier opfrëschen Link deelen Jo Nee OK - Eroflueden ofbriechen - Eroplueden ofbriechen Ofbriechen Späicheren an Zoumaachen Feeler @@ -230,7 +227,6 @@ Direkt eropgeluede Fichieren Sécherheet Pad fir d\'Eropluede vun de Videoen - D\'Erofluede vum %1$s-Dossier konnt net ofgeschloss ginn Connectioun opfrëschen Server-Adress diff --git a/res/values-lt-rLT/strings.xml b/res/values-lt-rLT/strings.xml index 37e1752a..4f29cf69 100644 --- a/res/values-lt-rLT/strings.xml +++ b/res/values-lt-rLT/strings.xml @@ -79,7 +79,6 @@ Sukurta: Modifikuota: Atsisiųsti - Atnaujinti failą Įkėlimo metu failas buvo pervadintas į %1$s Sąrašo išdėstymas Dalintis nuoroda @@ -87,8 +86,6 @@ Taip Ne Gerai - Atšaukti parsiuntimą - Atšaukti siuntimą Atšaukti Išsaugoti ir Išeiti Klaida @@ -313,7 +310,6 @@ Momentinis įkėlimas Saugumas Vaizdo įrašų įkėlimo kelias - Nepavyko baigti %1$s atsiuntimo Dalinamasi su jumis %1$s dalinamasi \"%2$s\" su jumis diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 13f65be4..291d1d0d 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -69,15 +69,12 @@ Izveidota: Modificēta: Lejupielādēt - Atsvaidzināt failu Datne tika pārsaukta uz %1$s augšupielādes laikā Dalīt saiti Pārtraukt dalīt saiti Jā Nē Labi - Atcelt lejupielādi - Atcelt augšupielādi Atcelt Saglabāt un iziet Kļūda diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index b819ebdb..2a5ecce0 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -73,15 +73,12 @@ Создадено: Изменето: Преземање - Освежи ја датотеката Датотеката беше преименувана во %1$s за време на префрлањето Сподели ја врската Тргнете го споделувањето на врската Да Не Во ред - Откажи превземање - Откажи прикачување Откажи Сними & Излез Грешка @@ -295,7 +292,6 @@ Инстант прикачувања Безбедност Прикачи патека на видео - Превземањето на папката %1$s не може да се заврши Освежи ја конекцијата Адреса на сервер diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 4ac4d952..f60887a3 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -41,7 +41,6 @@ Ya Tidak OK - Batal muat naik Batal Simpan & Keluar Ralat diff --git a/res/values-nb-rNO/strings.xml b/res/values-nb-rNO/strings.xml index 3ea006cb..79bf0b5d 100644 --- a/res/values-nb-rNO/strings.xml +++ b/res/values-nb-rNO/strings.xml @@ -79,7 +79,6 @@ Opprettet: Endret: Last ned - Oppdater fil Filnavnet ble endret til %1$s under opplasting Listeoppsett Del lenke @@ -87,8 +86,6 @@ Ja Nei OK - Avbryt nedlasting - Avbryt opplasting Avbryt Lagre og avslutt Feil @@ -316,7 +313,6 @@ Umiddelbare opplastinger Sikkerhet Sti til video-opplasting - Nedlasting av %1$s mappen kunne ikke fullføres delte med deg %1$s delte \"%2$s\" med deg diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 17040b84..3ff8c607 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -80,7 +80,6 @@ Download hier: %2$s Aangemaakt: Aangepast: Download - Bestand verversen Bestand is tijdens het uploaden hernoemd naar %1$s Lijst layout Deel link @@ -88,8 +87,6 @@ Download hier: %2$s Ja Nee OK - Annuleer download - Upload afbreken Annuleren Opslaan & Afsluiten Fout @@ -319,7 +316,6 @@ Hieronder staan de lokale bestanden en de externe bestanden in %5$s waar ze naar Directe uploads Beveiliging Upload Video Pad - Download van %1$s map kon niet worden voltooid gedeeld met u %1$s deelde \"%2$s\" met u diff --git a/res/values-nn-rNO/strings.xml b/res/values-nn-rNO/strings.xml index 2a12cc08..9225e19c 100644 --- a/res/values-nn-rNO/strings.xml +++ b/res/values-nn-rNO/strings.xml @@ -62,8 +62,6 @@ Ja Nei Greitt - Avbryt nedlasting - Avbryt opplasting Avbryt Lagra & avslutt Feil diff --git a/res/values-oc/strings.xml b/res/values-oc/strings.xml index e4914e63..54d70618 100644 --- a/res/values-oc/strings.xml +++ b/res/values-oc/strings.xml @@ -80,7 +80,6 @@ Telecargatz-lo aicí : %2$s Creat lo : Modificat lo : Telecargar - Actualizar lo fichièr Lo fichièr es estat renomenat en %s pendent lo mandadís Afichatge en lista Partejar lo ligam @@ -88,8 +87,6 @@ Telecargatz-lo aicí : %2$s Òc Non D\'acòrdi - Anullar lo telecargament - Anullar lo mandadís Anullar Salvar & Quitar Error @@ -320,7 +317,6 @@ En rason d\'aquesta modificacion, totes los fichièrs mandats amb de versions an Mandadís immediat Seguretat Repertòri de mandadís de las vidèos - Lo telecargament del dorsièr %1$s a pas pogut èsser acabat a partejat amb vos %1$s a partejat \"%2$s\" amb vos diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index f68a9c64..3e5f13e0 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -40,12 +40,9 @@ ਬਣਾਈ: ਸੋਧ ਕੀਤੀ: ਡਾਊਨਲੋਡ - ਫਾਇਲ ਤਾਜ਼ਾ ਕਰੋ ਹਾਂ ਨਹੀਂ ਠੀਕ ਹੈ - ਡਾਊਨਲੋਡ ਕਰਨਾ ਰੱਦ ਕਰੋ - ਅੱਪਲੋਡ ਰੱਦ ਕਰੋ ਰੱਦ ਕਰੋ ਸੰਭਾਲੋ ਅਤੇ ਬੰਦ ਕਰੋ ਗਲਤੀ diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index add7a952..8f0d6f8f 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -78,7 +78,6 @@ Utworzono: Zmodyfikowano: Pobierz - Odśwież plik Podczas wysyłania nazwa pliku została zmieniona na %1$s Lista szablonów wyglądu Udostępnij link @@ -86,8 +85,6 @@ Tak Nie OK - Anuluj pobieranie - Anuluj wysyłanie Anuluj Zapisz i wyjdź Błąd @@ -308,7 +305,6 @@ Automatyczne wysyłanie Bezpieczeństwo Katalog wysyłania dla wideo - Pobieranie %1$s katalogu nie może zostać ukończone udostępniony z tobą Odśwież połączenie diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml index 887543b2..0e97855e 100644 --- a/res/values-pt-rBR/strings.xml +++ b/res/values-pt-rBR/strings.xml @@ -79,7 +79,6 @@ Criado: Modificado: Baixar - Atualizar arquivo Arquivo foi renomeado para %1$s durante o envio Lista de Layout Compartilhar link @@ -87,8 +86,6 @@ Sim Não OK - Cancelar o download - Cancelar envio Cancelar Salvar & Sair Erro @@ -316,7 +313,6 @@ Envios Instantâneos Segurança Enviar o Caminho do Vídeo - Baixar %1$s da pasta não pode ser completado compartilhado com você %1$s compartilhado \"%2$s\" com você diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index e1698029..9541e2c6 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -71,6 +71,7 @@ segundos atrás Aqui não existe nada. Envie alguma coisa! A carregar... + Nenhuma App encontrada por tipo de ficheiro! Não existem ficheiros nesta pasta. Toque num ficheiro para visualizar a informação adicional. Tamanho: @@ -78,7 +79,6 @@ Criado: Modificado: Transferir - Atualizar ficheiro O ficheiro foi renomeado para %1$s durante o envio. Apresentação da Lista Partilhar a hiperligação @@ -86,8 +86,6 @@ Sim Não ACEITAR - Cancelar a transferência - Cancelar o envio Cancelar Guardar & Sair Erro @@ -313,7 +311,6 @@ Envios Instantâneos Segurança Envio do Caminho do Vídeo - Não foi possível completar o download da pasta %1$s partilhado consigo %1$s partilhou \"%2$s\" consigo diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index d2d6e3c5..5e9ae3fb 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -78,7 +78,6 @@ Creat: Modificat: Descarcă - Împrospătare fișier Fișierul a fost redenumit %1$s în timpul încărcării Aspect listă Partajază legătură @@ -86,8 +85,6 @@ Da Nu OK - Anulează descărcarea - Anulează încărcarea Anulează Salvare și ieșire Eroare @@ -309,7 +306,6 @@ Încărcări instante Securitate Calea de încărcare Video - Descărcarea fișierului %1$s nu s-a finisat partajat cu tine %1$s a partajat fișierul \"%2$s\" cu tine diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index ef994285..05af6892 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -80,7 +80,6 @@ Создан: Изменён: Скачать - Обновить файл Файл был переименован в %1$s во время загрузки Макет списка Поделиться ссылкой @@ -88,8 +87,6 @@ Да Нет ОК - Отменить скачивание - Отменить загрузку Отмена Сохранить и выйти Ошибка @@ -317,7 +314,6 @@ Мгновенные загрузки Безопасность Путь для загрузки Видео - Не удалось завершить скачивание каталога %1$s поделился с вами %1$s предоставил вам доступ к \"%2$s\" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index 8116b724..6dca1df8 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -36,7 +36,6 @@ ඔව් එපා හරි - උඩුගත කිරීම අත් හරින්න එපා සුරැක & පිටවන්න දෝශය diff --git a/res/values-sk-rSK/strings.xml b/res/values-sk-rSK/strings.xml index aee2919b..c30b24b4 100644 --- a/res/values-sk-rSK/strings.xml +++ b/res/values-sk-rSK/strings.xml @@ -78,7 +78,6 @@ Vytvorený: Zmenený: Stiahnuť - Obnoviť súbor Súbor bol premenovaný na %1$s počas nahrávania Rozvrhnutie zoznamu Zdieľať linku @@ -86,8 +85,6 @@ Áno Nie OK - Zrušiť sťahovanie - Zrušiť odosielanie Zrušiť Uložiť a ukončiť Chyba @@ -315,7 +312,6 @@ Okamžité nahratie Zabezpečenie Cesta pre nahrávanie videí - Sťahovanie %1$s priečinka nebolo dokončené zdieľané s vami %1$s vám zdieľal \"%2$s\" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 5fef3d14..eecc9bfb 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -78,7 +78,6 @@ Ustvarjeno: Spremenjeno: Prejmi - Osveži datoteko Datoteka je bila med nalaganjem preimenovana v %1$s Postavitev seznama Povezava za souporabo @@ -86,8 +85,6 @@ Da Ne V redu - Prekliči prejem - Prekliči pošiljanje Prekliči Shrani in končaj Napaka @@ -311,7 +308,6 @@ Takojšnje pošiljanje v oblak Varnost Pot videa za pošiljanje - Imenika %1$s ni mogoče prejeti v celoti v souporabi z vami Uporabnik %1$s je omogočil souporabo \"%2$s\" z vami diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index 0bde9ed4..473689f8 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -79,7 +79,6 @@ Krijuar më: Ndryshuar më: Shkarkoje - Rifreskoje kartelën Kartela u riemërtua si %1$s gjatë ngarkimit Skemë Liste Ndajeni lidhjen me të tjerët @@ -87,8 +86,6 @@ Po Jo OK - Anulojeni shkarkimin - Anulojeni ngarkimin Anuloje Ruaje & Dil Gabim @@ -312,7 +309,6 @@ Ngarkime të Menjëhershme Siguri Shteg Ngarkimi Videosh - S\’u plotësua dot shkarkimi i dosjes %1$s ndarë me ju %1$s ndau me ju \"%2$s\" diff --git a/res/values-sr-rSP/strings.xml b/res/values-sr-rSP/strings.xml index a8200978..b7aa6a76 100644 --- a/res/values-sr-rSP/strings.xml +++ b/res/values-sr-rSP/strings.xml @@ -34,7 +34,6 @@ Da Ne Ok - Otkaži otpremanje Otkaži Greška Nepoznata greška diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 0c27de85..e207263d 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -78,7 +78,6 @@ Направљен: Измењен: Преузми - Освежи фајл Фајл је преименован у %1$s током отпремања Распоред листе Веза дељења @@ -86,8 +85,6 @@ Да Не У реду - Откажи преузимање - Откажи отпремање Откажи Сачувај и изађи Грешка @@ -305,7 +302,6 @@ Тренутна отпремања Безбедност Путања отпремања видеа - Преузимање фасцикле %1$s не може бити довршено дељено са вама %1$s подели „%2$s“ са вама diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index e1eb0492..49887f05 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -72,15 +72,12 @@ Skapad: Ändrad: Ladda ner - Ladda om fil Filen bytte namn till %1$s under uppladdningen Dela länk Sluta dela länk Ja Nej OK - Avbryt nedladdning - Avbryt uppladdning Avbryt Spara & Avsluta Fel @@ -284,6 +281,5 @@ Direktuppladning Säkerhet Uppladdnings-sökväg för video - Neddladning utav %1$s mappen kunde inte slutföras Serveradress diff --git a/res/values-ta-rLK/strings.xml b/res/values-ta-rLK/strings.xml index 2f30505d..f5f91dce 100644 --- a/res/values-ta-rLK/strings.xml +++ b/res/values-ta-rLK/strings.xml @@ -42,8 +42,6 @@ ஆம் இல்லை சரி - பதிவிறக்கலை இரத்துசெய்க - பதிவேற்றலை இரத்து செய்க இரத்து செய்க சேமிக்க மற்றும் amp; வெளியேறு வழு diff --git a/res/values-th-rTH/strings.xml b/res/values-th-rTH/strings.xml index e7783952..459fa6e4 100644 --- a/res/values-th-rTH/strings.xml +++ b/res/values-th-rTH/strings.xml @@ -79,7 +79,6 @@ สร้างเมื่อ: แก้ไขเมื่อ: ดาวน์โหลด - ฟื้นฟูไฟล์ ไฟล์ได้ถูกเปลี่ยนชื่อเป็น %1$s ในระหว่างการอัพโหลด เค้าโครงรายการ แชร์ลิงค์ @@ -87,8 +86,6 @@ ตกลง ไม่ตกลง ตกลง - ยกเลิกการดาวน์โหลด - ยกเลิกการอัพโหลด ยกเลิก บันทึก & ออก ข้อผิดพลาด @@ -314,7 +311,6 @@ อัพโหลดทันที ความปลอดภัย อัพโหลดเส้นทางวิดีโอ - การดาวน์โหลดโฟลเดอร์ %1$s อาจไม่สำเร็จ ถูกแชร์ กับคุณ %1$s ได้แชร์ \"%2$s\" กับคุณ diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 090aec60..5a12caa4 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -78,7 +78,6 @@ Oluşturulma: Değiştirilme: İndir - Dosyayı yenile Dosya adı, yükleme sırasında %1$s olarak değiştirildi Liste Yerleşimi Paylaşma bağlantısı @@ -86,8 +85,6 @@ Evet Hayır Tamam - İndirmeyi iptal et - Yüklemeyi iptal et İptal Kaydet ve Çık Hata @@ -315,7 +312,6 @@ Anında Yüklemeler Güvenlik Video Yükleme Yolu - %1$s klasörün indirilmesi tamamlanamadı sizinle paylaştı %1$s, sizinle \"%2$s\" paylaşımını yaptı diff --git a/res/values-ug/strings.xml b/res/values-ug/strings.xml index 4573d713..a129982e 100644 --- a/res/values-ug/strings.xml +++ b/res/values-ug/strings.xml @@ -36,7 +36,6 @@ ھەئە ياق جەزملە - يۈكلەشتىن ۋاز كەچ ۋاز كەچ ساقلاپ چېكىن خاتالىق diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 44b450da..50fce4c5 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -79,7 +79,6 @@ Створено: Змінено: Завантажити - Оновити файл Файл був переіменований в %1$s протягом вивантаження Вигляд списку Опублікувати посилання @@ -87,8 +86,6 @@ Так Ні OK - Скасувати завантаження - Перервати завантаження Відмінити Зберегти & Вихід Помилка @@ -315,7 +312,6 @@ Миттєво завантаження Безпека Шлях завантаження відео - Скачування теки %1$s не може бути завершено поширений з Вами %1$s поділився \"%2$s\" з вами diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index bdccc82a..3323430d 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -57,15 +57,12 @@ Đã tạo: Đã chỉnh sửa: Tải về - Cập nhật lại tập tin Tập tin đã bị đổi tên thành %1$s trong quá trình tải lên Chia sẻ liên kết Liên kết không chia sẻ Yes Không Chấp nhận - Hủy tải về - Hủy upload Hủy Lưu & Thoát Lỗi diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 47aed775..e22d86e7 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -79,7 +79,6 @@ 创建于: 修改于: 下载 - 刷新文件 上传过程中文件被更名为了 %1$s 列表布局 分享链接 @@ -87,8 +86,6 @@ 是 否 确定 - 取消下载 - 取消上传 取消 保存并退出 错误 @@ -315,7 +312,6 @@ 即时上传 安全 视频上传路径 - %1$s 文件夹的下载无法完成 已共享 与你 %1$s和你分享了“%2$s” diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index 7810b88c..c286d0cb 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -43,8 +43,6 @@ 是 否 確定 - 取消下戴 - 取消上戴 取消 儲存並離開 錯誤 diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 18e47c63..8dd7201f 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -78,7 +78,6 @@ 建立: 修改: 下載 - 更新檔案列表 檔案名稱在上傳時已被更改為 %1$s 列表版型 分享連結 @@ -86,8 +85,6 @@ 是 否 好 - 取消下載 - 取消上傳 取消 儲存並離開 錯誤 @@ -310,7 +307,6 @@ 即時上傳 安全性 影片上傳路徑 - %1$s 目錄的下載未完成 以分享的 與你 %1$s 分享了 \"%2$s\" 給您 diff --git a/res/values/strings.xml b/res/values/strings.xml index d73dd573..b0ff1feb 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -81,7 +81,7 @@ Created: Modified: Download - Refresh file + Synchronize File was renamed to %1$s during upload List Layout Share link @@ -89,8 +89,7 @@ Yes No OK - Cancel download - Cancel upload + Cancel synchronization Cancel Save & Exit Error @@ -345,6 +344,7 @@ Upload video path Download of %1$s folder could not be completed + Synchronization of %1$s folder could not be completed shared with you diff --git a/src/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/com/owncloud/android/datamodel/FileDataStorageManager.java index 46a4d854..7d014b03 100644 --- a/src/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -24,8 +24,10 @@ import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.Vector; import android.accounts.Account; @@ -88,18 +90,10 @@ public class FileDataStorageManager { return mAccount; } - public void setContentResolver(ContentResolver cr) { - mContentResolver = cr; - } - public ContentResolver getContentResolver() { return mContentResolver; } - public void setContentProviderClient(ContentProviderClient cp) { - mContentProviderClient = cp; - } - public ContentProviderClient getContentProviderClient() { return mContentProviderClient; } @@ -186,7 +180,6 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, file.getFileLength()); cv.put(ProviderTableMeta.FILE_CONTENT_TYPE, file.getMimetype()); cv.put(ProviderTableMeta.FILE_NAME, file.getFileName()); - //if (file.getParentId() != DataStorageManager.ROOT_PARENT_ID) cv.put(ProviderTableMeta.FILE_PARENT, file.getParentId()); cv.put(ProviderTableMeta.FILE_PATH, file.getRemotePath()); if (!file.isFolder()) @@ -202,11 +195,12 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail()); cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading()); - + cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict()); + boolean sameRemotePath = fileExists(file.getRemotePath()); if (sameRemotePath || fileExists(file.getFileId())) { // for renamed files; no more delete and create - OCFile oldFile = null; + OCFile oldFile; if (sameRemotePath) { oldFile = getFileByPath(file.getRemotePath()); file.setFileId(oldFile.getFileId()); @@ -252,12 +246,6 @@ public class FileDataStorageManager { } } -// if (file.isFolder()) { -// updateFolderSize(file.getFileId()); -// } else { -// updateFolderSize(file.getParentId()); -// } - return overriden; } @@ -311,6 +299,7 @@ public class FileDataStorageManager { cv.put(ProviderTableMeta.FILE_REMOTE_ID, file.getRemoteId()); cv.put(ProviderTableMeta.FILE_UPDATE_THUMBNAIL, file.needsUpdateThumbnail()); cv.put(ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading()); + cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict()); boolean existsByPath = fileExists(file.getRemotePath()); if (existsByPath || fileExists(file.getFileId())) { @@ -335,7 +324,6 @@ public class FileDataStorageManager { for (OCFile file : filesToRemove) { if (file.getParentId() == folder.getFileId()) { whereArgs = new String[]{mAccount.name, file.getRemotePath()}; - //Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, "" + file.getFileId()); if (file.isFolder()) { operations.add(ContentProviderOperation.newDelete( ContentUris.withAppendedId( @@ -432,43 +420,9 @@ public class FileDataStorageManager { } } - //updateFolderSize(folder.getFileId()); - } -// /** -// * -// * @param id -// */ -// private void updateFolderSize(long id) { -// if (id > FileDataStorageManager.ROOT_PARENT_ID) { -// Log_OC.d(TAG, "Updating size of " + id); -// if (getContentResolver() != null) { -// getContentResolver().update(ProviderTableMeta.CONTENT_URI_DIR, -// new ContentValues(), - // won't be used, but cannot be null; crashes in KLP -// ProviderTableMeta._ID + "=?", -// new String[] { String.valueOf(id) }); -// } else { -// try { -// getContentProviderClient().update(ProviderTableMeta.CONTENT_URI_DIR, -// new ContentValues(), - // won't be used, but cannot be null; crashes in KLP -// ProviderTableMeta._ID + "=?", -// new String[] { String.valueOf(id) }); -// -// } catch (RemoteException e) { -// Log_OC.e( -// TAG, "Exception in update of folder size through compatibility patch " + e.getMessage()); -// } -// } -// } else { -// Log_OC.e(TAG, "not updating size for folder " + id); -// } -// } - - public boolean removeFile(OCFile file, boolean removeDBData, boolean removeLocalCopy) { boolean success = true; if (file != null) { @@ -503,6 +457,7 @@ public class FileDataStorageManager { // maybe unnecessary, but should be checked TODO remove if unnecessary file.setStoragePath(null); saveFile(file); + saveConflict(file, null); } } } @@ -938,44 +893,12 @@ public class FileDataStorageManager { c.getColumnIndex(ProviderTableMeta.FILE_UPDATE_THUMBNAIL)) == 1 ? true : false); file.setDownloading(c.getInt( c.getColumnIndex(ProviderTableMeta.FILE_IS_DOWNLOADING)) == 1 ? true : false); - - } - return file; - } - - /** - * Returns if the file/folder is shared by link or not - * - * @param path Path of the file/folder - * @return - */ - public boolean isShareByLink(String path) { - Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path); - OCFile file = null; - if (c.moveToFirst()) { - file = createFileInstance(c); - } - c.close(); - return file.isShareByLink(); - } + file.setEtagInConflict(c.getString(c.getColumnIndex(ProviderTableMeta.FILE_ETAG_IN_CONFLICT))); - /** - * Returns the public link of the file/folder - * - * @param path Path of the file/folder - * @return - */ - public String getPublicLink(String path) { - Cursor c = getCursorForValue(ProviderTableMeta.FILE_STORAGE_PATH, path); - OCFile file = null; - if (c.moveToFirst()) { - file = createFileInstance(c); } - c.close(); - return file.getPublicLink(); + return file; } - // Methods for Shares public boolean saveShare(OCShare share) { boolean overriden = false; @@ -1306,6 +1229,7 @@ public class FileDataStorageManager { ProviderTableMeta.FILE_IS_DOWNLOADING, file.isDownloading() ? 1 : 0 ); + cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, file.getEtagInConflict()); boolean existsByPath = fileExists(file.getRemotePath()); if (existsByPath || fileExists(file.getFileId())) { @@ -1485,44 +1409,6 @@ public class FileDataStorageManager { } } return preparedOperations; - - /* - if (operations.size() > 0) { - try { - if (getContentResolver() != null) { - getContentResolver().applyBatch(MainApp.getAuthority(), operations); - - } else { - getContentProviderClient().applyBatch(operations); - } - - } catch (OperationApplicationException e) { - Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage()); - - } catch (RemoteException e) { - Log_OC.e(TAG, "Exception in batch of operations " + e.getMessage()); - } - } - */ - - /* - if (getContentResolver() != null) { - - getContentResolver().delete(ProviderTableMeta.CONTENT_URI_SHARE, - where, - whereArgs); - } else { - try { - getContentProviderClient().delete( ProviderTableMeta.CONTENT_URI_SHARE, - where, - whereArgs); - - } catch (RemoteException e) { - Log_OC.e(TAG, "Exception deleting shares in a folder " + e.getMessage()); - } - } - */ - //} } public static void triggerMediaScan(String path) { @@ -1573,4 +1459,138 @@ public class FileDataStorageManager { } + public void saveConflict(OCFile file, String etagInConflict) { + if (!file.isDown()) { + etagInConflict = null; + } + ContentValues cv = new ContentValues(); + cv.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, etagInConflict); + int updated = 0; + if (getContentResolver() != null) { + updated = getContentResolver().update( + ProviderTableMeta.CONTENT_URI_FILE, + cv, + ProviderTableMeta._ID + "=?", + new String[] { String.valueOf(file.getFileId())} + ); + } else { + try { + updated = getContentProviderClient().update( + ProviderTableMeta.CONTENT_URI_FILE, + cv, + ProviderTableMeta._ID + "=?", + new String[]{String.valueOf(file.getFileId())} + ); + } catch (RemoteException e) { + Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage()); + } + } + + Log_OC.d(TAG, "Number of files updated with CONFLICT: " + updated); + + if (updated > 0) { + if (etagInConflict != null) { + /// set conflict in all ancestor folders + + long parentId = file.getParentId(); + Set ancestorIds = new HashSet(); + while (parentId != FileDataStorageManager.ROOT_PARENT_ID) { + ancestorIds.add(Long.toString(parentId)); + parentId = getFileById(parentId).getParentId(); + } + + if (ancestorIds.size() > 0) { + StringBuffer whereBuffer = new StringBuffer(); + whereBuffer.append(ProviderTableMeta._ID).append(" IN ("); + for (int i = 0; i < ancestorIds.size() - 1; i++) { + whereBuffer.append("?,"); + } + whereBuffer.append("?"); + whereBuffer.append(")"); + + if (getContentResolver() != null) { + updated = getContentResolver().update( + ProviderTableMeta.CONTENT_URI_FILE, + cv, + whereBuffer.toString(), + ancestorIds.toArray(new String[]{}) + ); + } else { + try { + updated = getContentProviderClient().update( + ProviderTableMeta.CONTENT_URI_FILE, + cv, + whereBuffer.toString(), + ancestorIds.toArray(new String[]{}) + ); + } catch (RemoteException e) { + Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage()); + } + } + } // else file is ROOT folder, no parent to set in conflict + + } else { + /// update conflict in ancestor folders + // (not directly unset; maybe there are more conflicts below them) + String parentPath = file.getRemotePath(); + if (parentPath.endsWith(OCFile.PATH_SEPARATOR)) { + parentPath = parentPath.substring(0, parentPath.length() - 1); + } + parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1); + + Log_OC.d(TAG, "checking parents to remove conflict; STARTING with " + parentPath); + while (parentPath.length() > 0) { + + String where = + ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " IS NOT NULL AND " + + ProviderTableMeta.FILE_CONTENT_TYPE + " != 'DIR' AND " + + ProviderTableMeta.FILE_ACCOUNT_OWNER + " = ? AND " + + ProviderTableMeta.FILE_PATH + " LIKE ?"; + Cursor descendentsInConflict = getContentResolver().query( + ProviderTableMeta.CONTENT_URI_FILE, + new String[]{ProviderTableMeta._ID}, + where, + new String[]{mAccount.name, parentPath + "%"}, + null + ); + if (descendentsInConflict == null || descendentsInConflict.getCount() == 0) { + Log_OC.d(TAG, "NO MORE conflicts in " + parentPath); + if (getContentResolver() != null) { + updated = getContentResolver().update( + ProviderTableMeta.CONTENT_URI_FILE, + cv, + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + + ProviderTableMeta.FILE_PATH + "=?", + new String[]{mAccount.name, parentPath} + ); + } else { + try { + updated = getContentProviderClient().update( + ProviderTableMeta.CONTENT_URI_FILE, + cv, + ProviderTableMeta.FILE_ACCOUNT_OWNER + "=? AND " + + ProviderTableMeta.FILE_PATH + "=?" + , new String[]{mAccount.name, parentPath} + ); + } catch (RemoteException e) { + Log_OC.e(TAG, "Failed saving conflict in database " + e.getMessage()); + } + } + + } else { + Log_OC.d(TAG, "STILL " + descendentsInConflict.getCount() + " in " + parentPath); + } + + if (descendentsInConflict != null) { + descendentsInConflict.close(); + } + + parentPath = parentPath.substring(0, parentPath.length() - 1); // trim last / + parentPath = parentPath.substring(0, parentPath.lastIndexOf(OCFile.PATH_SEPARATOR) + 1); + Log_OC.d(TAG, "checking parents to remove conflict; NEXT " + parentPath); + } + } + } + + } } diff --git a/src/com/owncloud/android/datamodel/OCFile.java b/src/com/owncloud/android/datamodel/OCFile.java index 1c2ec265..bc1d7e21 100644 --- a/src/com/owncloud/android/datamodel/OCFile.java +++ b/src/com/owncloud/android/datamodel/OCFile.java @@ -74,6 +74,8 @@ public class OCFile implements Parcelable, Comparable { private boolean mShowGridView; + private String mEtagInConflict; // Save file etag in the server, when there is a conflict. No conflict = null + /** * Create new {@link OCFile} with given path. @@ -115,8 +117,9 @@ public class OCFile implements Parcelable, Comparable { mPublicLink = source.readString(); mPermissions = source.readString(); mRemoteId = source.readString(); - mNeedsUpdateThumbnail = source.readInt() == 0; - mIsDownloading = source.readInt() == 0; + mNeedsUpdateThumbnail = source.readInt() == 1; + mIsDownloading = source.readInt() == 1; + mEtagInConflict = source.readString(); } @@ -142,6 +145,7 @@ public class OCFile implements Parcelable, Comparable { dest.writeString(mRemoteId); dest.writeInt(mNeedsUpdateThumbnail ? 1 : 0); dest.writeInt(mIsDownloading ? 1 : 0); + dest.writeString(mEtagInConflict); } /** @@ -316,24 +320,6 @@ public class OCFile implements Parcelable, Comparable { } /** - * Adds a file to this directory. If this file is not a directory, an - * exception gets thrown. - * - * @param file to add - * @throws IllegalStateException if you try to add a something and this is - * not a directory - */ - public void addFile(OCFile file) throws IllegalStateException { - if (isFolder()) { - file.mParentId = mId; - mNeedsUpdating = true; - return; - } - throw new IllegalStateException( - "This is not a directory where you can add stuff to!"); - } - - /** * Used internally. Reset all file properties */ private void resetData() { @@ -357,6 +343,7 @@ public class OCFile implements Parcelable, Comparable { mRemoteId = null; mNeedsUpdateThumbnail = false; mIsDownloading = false; + mEtagInConflict = null; } /** @@ -498,10 +485,9 @@ public class OCFile implements Parcelable, Comparable { } public void setEtag(String etag) { - this.mEtag = etag; + this.mEtag = (etag != null ? etag : ""); } - public boolean isShareByLink() { return mShareByLink; } @@ -598,8 +584,11 @@ public class OCFile implements Parcelable, Comparable { this.mIsDownloading = isDownloading; } - public boolean isSynchronizing() { - // TODO real implementation - return false; + public String getEtagInConflict() { + return mEtagInConflict; + } + + public void setEtagInConflict(String etagInConflict) { + mEtagInConflict = etagInConflict; } } diff --git a/src/com/owncloud/android/db/ProviderMeta.java b/src/com/owncloud/android/db/ProviderMeta.java index 25e8fbd1..f15dd9ec 100644 --- a/src/com/owncloud/android/db/ProviderMeta.java +++ b/src/com/owncloud/android/db/ProviderMeta.java @@ -31,7 +31,7 @@ import com.owncloud.android.MainApp; public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 10; + public static final int DB_VERSION = 11; private ProviderMeta() { } @@ -72,6 +72,7 @@ public class ProviderMeta { public static final String FILE_REMOTE_ID = "remote_id"; public static final String FILE_UPDATE_THUMBNAIL = "update_thumbnail"; public static final String FILE_IS_DOWNLOADING= "is_downloading"; + public static final String FILE_ETAG_IN_CONFLICT = "etag_in_conflict"; public static final String FILE_DEFAULT_SORT_ORDER = FILE_NAME + " collate nocase asc"; @@ -94,7 +95,7 @@ public class ProviderMeta { public static final String OCSHARES_DEFAULT_SORT_ORDER = OCSHARES_FILE_SOURCE + " collate nocase asc"; - + } } diff --git a/src/com/owncloud/android/files/FileMenuFilter.java b/src/com/owncloud/android/files/FileMenuFilter.java index 1d5ca454..065ab898 100644 --- a/src/com/owncloud/android/files/FileMenuFilter.java +++ b/src/com/owncloud/android/files/FileMenuFilter.java @@ -34,7 +34,6 @@ import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileDownloader.FileDownloaderBinder; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.files.services.FileUploader.FileUploaderBinder; -import com.owncloud.android.services.OperationsService; import com.owncloud.android.services.OperationsService.OperationsServiceBinder; import com.owncloud.android.ui.activity.ComponentsGetter; @@ -106,21 +105,25 @@ public class FileMenuFilter { * @param toHide List to save the options that must be shown in the menu. */ private void filter(List toShow, List toHide) { - boolean downloading = false; - boolean uploading = false; + boolean synchronizing = false; if (mComponentsGetter != null && mFile != null && mAccount != null) { - FileDownloaderBinder downloaderBinder = mComponentsGetter.getFileDownloaderBinder(); - downloading = (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)); OperationsServiceBinder opsBinder = mComponentsGetter.getOperationsServiceBinder(); - downloading |= (opsBinder != null && opsBinder.isSynchronizing(mAccount, mFile.getRemotePath())); FileUploaderBinder uploaderBinder = mComponentsGetter.getFileUploaderBinder(); - uploading = (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)); + FileDownloaderBinder downloaderBinder = mComponentsGetter.getFileDownloaderBinder(); + synchronizing = ( + // comparing local and remote + (opsBinder != null && opsBinder.isSynchronizing(mAccount, mFile.getRemotePath())) || + // downloading + (downloaderBinder != null && downloaderBinder.isDownloading(mAccount, mFile)) || + // uploading + (uploaderBinder != null && uploaderBinder.isUploading(mAccount, mFile)) + ); } /// decision is taken for each possible action on a file in the menu // DOWNLOAD - if (mFile == null || mFile.isDown() || downloading || uploading) { + if (mFile == null || mFile.isDown() || mFile.isFolder() || synchronizing) { toHide.add(R.id.action_download_file); } else { @@ -128,7 +131,7 @@ public class FileMenuFilter { } // RENAME - if (mFile == null || downloading || uploading) { + if (mFile == null || synchronizing) { toHide.add(R.id.action_rename_file); } else { @@ -136,7 +139,7 @@ public class FileMenuFilter { } // MOVE & COPY - if (mFile == null || downloading || uploading) { + if (mFile == null || synchronizing) { toHide.add(R.id.action_move); toHide.add(R.id.action_copy); } else { @@ -145,7 +148,7 @@ public class FileMenuFilter { } // REMOVE - if (mFile == null || downloading || uploading) { + if (mFile == null || synchronizing) { toHide.add(R.id.action_remove_file); } else { @@ -153,31 +156,25 @@ public class FileMenuFilter { } // OPEN WITH (different to preview!) - if (mFile == null || mFile.isFolder() || !mFile.isDown() || downloading || uploading) { + if (mFile == null || mFile.isFolder() || !mFile.isDown() || synchronizing) { toHide.add(R.id.action_open_file_with); } else { toShow.add(R.id.action_open_file_with); } + // CANCEL SYNCHRONIZATION + if (mFile == null || !synchronizing) { + toHide.add(R.id.action_cancel_sync); - // CANCEL DOWNLOAD - if (mFile == null || !downloading) { - toHide.add(R.id.action_cancel_download); - } else { - toShow.add(R.id.action_cancel_download); - } - - // CANCEL UPLOAD - if (mFile == null || !uploading || mFile.isFolder()) { - toHide.add(R.id.action_cancel_upload); } else { - toShow.add(R.id.action_cancel_upload); + toShow.add(R.id.action_cancel_sync); } - // SYNC FILE CONTENTS - if (mFile == null || mFile.isFolder() || !mFile.isDown() || downloading || uploading) { + // SYNC CONTENTS (BOTH FILE AND FOLDER) + if (mFile == null || (!mFile.isFolder() && !mFile.isDown()) || synchronizing) { toHide.add(R.id.action_sync_file); + } else { toShow.add(R.id.action_sync_file); } @@ -210,21 +207,21 @@ public class FileMenuFilter { // SEND boolean sendAllowed = (mContext != null && mContext.getString(R.string.send_files_to_other_apps).equalsIgnoreCase("on")); - if (mFile == null || !sendAllowed || mFile.isFolder() || uploading || downloading) { + if (mFile == null || !sendAllowed || mFile.isFolder() || synchronizing) { toHide.add(R.id.action_send_file); } else { toShow.add(R.id.action_send_file); } // FAVORITES - if (mFile == null || downloading || uploading || mFile.isFolder() || mFile.isFavorite()) { + if (mFile == null || synchronizing || mFile.isFolder() || mFile.isFavorite()) { toHide.add(R.id.action_favorite_file); } else { toShow.add(R.id.action_favorite_file); } // UNFAVORITES - if (mFile == null || downloading || uploading || mFile.isFolder() || !mFile.isFavorite()) { + if (mFile == null || synchronizing || mFile.isFolder() || !mFile.isFavorite()) { toHide.add(R.id.action_unfavorite_file); } else { toShow.add(R.id.action_unfavorite_file); diff --git a/src/com/owncloud/android/files/FileOperationsHelper.java b/src/com/owncloud/android/files/FileOperationsHelper.java index 51d447e1..e799e176 100644 --- a/src/com/owncloud/android/files/FileOperationsHelper.java +++ b/src/com/owncloud/android/files/FileOperationsHelper.java @@ -272,8 +272,13 @@ public class FileOperationsHelper { } } + /** + * Request the synchronization of a file or folder with the OC server, including its contents. + * + * @param file The file or folder to synchronize + */ public void syncFile(OCFile file) { - if (!file.isFolder()) { + if (!file.isFolder()){ Intent intent = new Intent(mFileActivity, OperationsService.class); intent.setAction(OperationsService.ACTION_SYNC_FILE); intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); @@ -288,6 +293,7 @@ public class FileOperationsHelper { intent.putExtra(OperationsService.EXTRA_ACCOUNT, mFileActivity.getAccount()); intent.putExtra(OperationsService.EXTRA_REMOTE_PATH, file.getRemotePath()); mFileActivity.startService(intent); + } } @@ -368,19 +374,11 @@ public class FileOperationsHelper { // for both files and folders FileDownloaderBinder downloaderBinder = mFileActivity.getFileDownloaderBinder(); - FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder(); if (downloaderBinder != null && downloaderBinder.isDownloading(account, file)) { downloaderBinder.cancel(account, file); - - // TODO - review why is this here, and solve in a better way - // Remove etag for parent, if file is a favorite - if (file.isFavorite()) { - OCFile parent = mFileActivity.getStorageManager().getFileById(file.getParentId()); - parent.setEtag(""); - mFileActivity.getStorageManager().saveFile(parent); - } - - } else if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) { + } + FileUploaderBinder uploaderBinder = mFileActivity.getFileUploaderBinder(); + if (uploaderBinder != null && uploaderBinder.isUploading(account, file)) { uploaderBinder.cancel(account, file); } } diff --git a/src/com/owncloud/android/files/services/FileDownloader.java b/src/com/owncloud/android/files/services/FileDownloader.java index f6d3b50f..b6f21a5c 100644 --- a/src/com/owncloud/android/files/services/FileDownloader.java +++ b/src/com/owncloud/android/files/services/FileDownloader.java @@ -21,14 +21,12 @@ package com.owncloud.android.files.services; import java.io.File; -import java.io.IOException; import java.util.AbstractList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; -import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; import com.owncloud.android.authentication.AuthenticatorActivity; @@ -54,7 +52,6 @@ import com.owncloud.android.utils.ErrorMessageAdapter; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountsException; import android.accounts.OnAccountsUpdateListener; import android.app.NotificationManager; import android.app.PendingIntent; @@ -153,7 +150,7 @@ public class FileDownloader extends Service /** * Entry point to add one or several files to the queue of downloads. - *

+ * * New downloads are added calling to startService(), resulting in a call to this method. * This ensures the service will keep on working although the caller activity goes away. */ @@ -169,12 +166,6 @@ public class FileDownloader extends Service } else { final Account account = intent.getParcelableExtra(EXTRA_ACCOUNT); final OCFile file = intent.getParcelableExtra(EXTRA_FILE); - - /*Log_OC.v( - "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Received request to download file" - );*/ - AbstractList requestedDownloads = new Vector(); try { DownloadFileOperation newDownload = new DownloadFileOperation(account, file); @@ -185,20 +176,6 @@ public class FileDownloader extends Service ); String downloadKey = putResult.first; requestedDownloads.add(downloadKey); - /*Log_OC.v( - "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Download on " + file.getRemotePath() + " added to queue" - );*/ - - // Store file on db with state 'downloading' - /* - TODO - check if helps with UI responsiveness, - letting only folders use FileDownloaderBinder to check - FileDataStorageManager storageManager = - new FileDataStorageManager(account, getContentResolver()); - file.setDownloading(true); - storageManager.saveFile(file); - */ sendBroadcastNewDownload(newDownload, putResult.second); @@ -275,34 +252,23 @@ public class FileDownloader extends Service * @param file A file in the queue of pending downloads */ public void cancel(Account account, OCFile file) { - /*Log_OC.v( - "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Received request to cancel download of " + file.getRemotePath() - ); - Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Removing download of " + file.getRemotePath());*/ - Pair removeResult = - mPendingDownloads.remove(account, file.getRemotePath()); + Pair removeResult = mPendingDownloads.remove(account, file.getRemotePath()); DownloadFileOperation download = removeResult.first; if (download != null) { - /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Canceling returned download of " + file.getRemotePath());*/ download.cancel(); } else { if (mCurrentDownload != null && mCurrentAccount != null && mCurrentDownload.getRemotePath().startsWith(file.getRemotePath()) && account.name.equals(mCurrentAccount.name)) { - /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Canceling current sync as descendant: " + mCurrentDownload.getRemotePath());*/ mCurrentDownload.cancel(); } } } /** - * Cancels a pending or current upload for an account + * Cancels all the downloads for an account * - * @param account Owncloud accountName where the remote file will be stored. + * @param account ownCloud account. */ public void cancel(Account account) { Log_OC.d(TAG, "Account= " + account.name); @@ -325,7 +291,7 @@ public class FileDownloader extends Service /** * Returns True when the file described by 'file' in the ownCloud account 'account' * is downloading or waiting to download. - *

+ * * If 'file' is a directory, returns 'true' if any of its descendant files is downloading or * waiting to download. * @@ -349,7 +315,6 @@ public class FileDownloader extends Service OnDatatransferProgressListener listener, Account account, OCFile file ) { if (account == null || file == null || listener == null) return; - //String targetKey = buildKey(account, file.getRemotePath()); mBoundListeners.put(file.getFileId(), listener); } @@ -357,15 +322,14 @@ public class FileDownloader extends Service /** * Removes a listener interested in the progress of the download for a concrete file. * - * @param listener Object to notify about progress of transfer. - * @param account ownCloud account holding the file of interest. - * @param file {@link OCFile} of interest for listener. + * @param listener Object to notify about progress of transfer. + * @param account ownCloud account holding the file of interest. + * @param file {@link OCFile} of interest for listener. */ public void removeDatatransferProgressListener( OnDatatransferProgressListener listener, Account account, OCFile file ) { if (account == null || file == null || listener == null) return; - //String targetKey = buildKey(account, file.getRemotePath()); Long fileId = file.getFileId(); if (mBoundListeners.get(fileId) == listener) { mBoundListeners.remove(fileId); @@ -375,8 +339,6 @@ public class FileDownloader extends Service @Override public void onTransferProgress(long progressRate, long totalTransferredSoFar, long totalToTransfer, String fileName) { - //String key = buildKey(mCurrentDownload.getAccount(), - // mCurrentDownload.getFile().getRemotePath()); OnDatatransferProgressListener boundListener = mBoundListeners.get(mCurrentDownload.getFile().getFileId()); if (boundListener != null) { @@ -385,23 +347,12 @@ public class FileDownloader extends Service } } - /** - * Review downloads and cancel it if its account doesn't exist - */ - public void checkAccountOfCurrentDownload() { - if (mCurrentDownload != null && - !AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) { - mCurrentDownload.cancel(); - } - // The rest of downloads are cancelled when they try to start - } - } /** * Download worker. Performs the pending downloads in the order they were requested. - *

+ * Created with the Looper of a new thread, started in {@link FileUploader#onCreate()}. */ private static class ServiceHandler extends Handler { @@ -440,14 +391,13 @@ public class FileDownloader extends Service */ private void downloadFile(String downloadKey) { - /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Getting download of " + downloadKey);*/ mCurrentDownload = mPendingDownloads.get(downloadKey); if (mCurrentDownload != null) { // Detect if the account exists if (AccountUtils.exists(mCurrentDownload.getAccount(), getApplicationContext())) { Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().name + " exists"); + notifyDownloadStart(mCurrentDownload); RemoteOperationResult downloadResult = null; @@ -470,26 +420,16 @@ public class FileDownloader extends Service /// perform the download - /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Executing download of " + mCurrentDownload.getRemotePath());*/ downloadResult = mCurrentDownload.execute(mDownloadClient); if (downloadResult.isSuccess()) { saveDownloadedFile(); } - } catch (AccountsException e) { - Log_OC.e(TAG, "Error while trying to get authorization for " - + mCurrentAccount.name, e); - downloadResult = new RemoteOperationResult(e); - } catch (IOException e) { - Log_OC.e(TAG, "Error while trying to get authorization for " - + mCurrentAccount.name, e); + } catch (Exception e) { + Log_OC.e(TAG, "Error downloading", e); downloadResult = new RemoteOperationResult(e); } finally { - /*Log_OC.v( "NOW " + TAG + ", thread " + Thread.currentThread().getName(), - "Removing payload " + mCurrentDownload.getRemotePath());*/ - Pair removeResult = mPendingDownloads.removePayload(mCurrentAccount, mCurrentDownload.getRemotePath()); @@ -497,9 +437,9 @@ public class FileDownloader extends Service /// notify result notifyDownloadResult(mCurrentDownload, downloadResult); - sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, - removeResult.second); + sendBroadcastDownloadFinished(mCurrentDownload, downloadResult, removeResult.second); } + } else { // Cancel the transfer Log_OC.d(TAG, "Account " + mCurrentDownload.getAccount().toString() + @@ -513,6 +453,8 @@ public class FileDownloader extends Service /** * Updates the OC File after a successful download. + * + * TODO move to DownloadFileOperation */ private void saveDownloadedFile() { OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId()); @@ -522,26 +464,17 @@ public class FileDownloader extends Service file.setNeedsUpdateThumbnail(true); file.setModificationTimestamp(mCurrentDownload.getModificationTimestamp()); file.setModificationTimestampAtLastSyncForData(mCurrentDownload.getModificationTimestamp()); - // file.setEtag(mCurrentDownload.getEtag()); // TODO Etag, where available + file.setEtag(mCurrentDownload.getEtag()); file.setMimetype(mCurrentDownload.getMimeType()); file.setStoragePath(mCurrentDownload.getSavePath()); file.setFileLength((new File(mCurrentDownload.getSavePath()).length())); file.setRemoteId(mCurrentDownload.getFile().getRemoteId()); mStorageManager.saveFile(file); mStorageManager.triggerMediaScan(file.getStoragePath()); + mStorageManager.saveConflict(file, null); } /** - * Update the OC File after a unsuccessful download - */ - private void updateUnsuccessfulDownloadedFile() { - OCFile file = mStorageManager.getFileById(mCurrentDownload.getFile().getFileId()); - file.setDownloading(false); - mStorageManager.saveFile(file); - } - - - /** * Creates a status notification to show the download progress * * @param download Download operation starting. @@ -683,6 +616,7 @@ public class FileDownloader extends Service DownloadFileOperation download, RemoteOperationResult downloadResult, String unlinkedFromRemotePath) { + Intent end = new Intent(getDownloadFinishMessage()); end.putExtra(EXTRA_DOWNLOAD_RESULT, downloadResult.isSuccess()); end.putExtra(ACCOUNT_NAME, download.getAccount().name); diff --git a/src/com/owncloud/android/files/services/FileUploader.java b/src/com/owncloud/android/files/services/FileUploader.java index 6aca4937..df4dccda 100644 --- a/src/com/owncloud/android/files/services/FileUploader.java +++ b/src/com/owncloud/android/files/services/FileUploader.java @@ -21,18 +21,14 @@ package com.owncloud.android.files.services; import java.io.File; -import java.io.IOException; import java.util.AbstractList; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Vector; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AccountsException; import android.accounts.OnAccountsUpdateListener; import android.app.NotificationManager; import android.app.PendingIntent; @@ -46,6 +42,7 @@ import android.os.Looper; import android.os.Message; import android.os.Process; import android.support.v4.app.NotificationCompat; +import android.util.Pair; import android.webkit.MimeTypeMap; import com.owncloud.android.R; @@ -86,6 +83,7 @@ public class FileUploader extends Service public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_OLD_REMOTE_PATH = "OLD_REMOTE_PATH"; public static final String EXTRA_OLD_FILE_PATH = "OLD_FILE_PATH"; + public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO"; public static final String ACCOUNT_NAME = "ACCOUNT_NAME"; public static final String KEY_FILE = "FILE"; @@ -114,11 +112,10 @@ public class FileUploader extends Service private ServiceHandler mServiceHandler; private IBinder mBinder; private OwnCloudClient mUploadClient = null; - private Account mLastAccount = null; + private Account mCurrentAccount = null; private FileDataStorageManager mStorageManager; - private ConcurrentMap mPendingUploads = - new ConcurrentHashMap(); + private IndexedForest mPendingUploads = new IndexedForest(); private UploadFileOperation mCurrentUpload = null; private NotificationManager mNotificationManager; @@ -134,26 +131,14 @@ public class FileUploader extends Service } /** - * Builds a key for mPendingUploads from the account and file to upload - * - * @param account Account where the file to upload is stored - * @param file File to upload - */ - private String buildRemoteName(Account account, OCFile file) { - return account.name + file.getRemotePath(); - } - - private String buildRemoteName(Account account, String remotePath) { - return account.name + remotePath; - } - - /** * Checks if an ownCloud server version should support chunked uploads. * * @param version OwnCloud version instance corresponding to an ownCloud * server. * @return 'True' if the ownCloud server with version supports chunked * uploads. + * + * TODO - move to OCClient */ private static boolean chunkedUploadIsSupported(OwnCloudVersion version) { return (version != null && version.compareTo(OwnCloudVersion.owncloud_v4_5) >= 0); @@ -283,7 +268,7 @@ public class FileUploader extends Service files = new OCFile[localPaths.length]; for (int i = 0; i < localPaths.length; i++) { files[i] = obtainNewOCFileToUpload(remotePaths[i], localPaths[i], - ((mimeTypes != null) ? mimeTypes[i] : null), storageManager); + ((mimeTypes != null) ? mimeTypes[i] : null)); if (files[i] == null) { // TODO @andomaex add failure Notification return Service.START_NOT_STICKY; @@ -299,18 +284,23 @@ public class FileUploader extends Service UploadFileOperation newUpload = null; try { for (int i = 0; i < files.length; i++) { - uploadKey = buildRemoteName(account, files[i].getRemotePath()); - newUpload = new UploadFileOperation(account, files[i], chunked, isInstant, + newUpload = new UploadFileOperation( + account, + files[i], + chunked, + isInstant, forceOverwrite, localAction, - getApplicationContext()); + getApplicationContext() + ); if (isInstant) { newUpload.setRemoteFolderToBeCreated(); } - // Grants that the file only upload once time - mPendingUploads.putIfAbsent(uploadKey, newUpload); - newUpload.addDatatransferProgressListener(this); - newUpload.addDatatransferProgressListener((FileUploaderBinder)mBinder); + newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder); + Pair putResult = mPendingUploads.putIfAbsent( + account, files[i].getRemotePath(), newUpload + ); + uploadKey = putResult.first; requestedUploads.add(uploadKey); } @@ -334,7 +324,6 @@ public class FileUploader extends Service msg.obj = requestedUploads; mServiceHandler.sendMessage(msg); } - Log_OC.i(TAG, "mPendingUploads size:" + mPendingUploads.size()); return Service.START_NOT_STICKY; } @@ -387,23 +376,27 @@ public class FileUploader extends Service /** * Cancels a pending or current upload of a remote file. * - * @param account Owncloud account where the remote file will be stored. - * @param file A file in the queue of pending uploads + * @param account ownCloud account where the remote file will be stored. + * @param file A file in the queue of pending uploads */ public void cancel(Account account, OCFile file) { - UploadFileOperation upload; - synchronized (mPendingUploads) { - upload = mPendingUploads.remove(buildRemoteName(account, file)); - } + Pair removeResult = mPendingUploads.remove(account, file.getRemotePath()); + UploadFileOperation upload = removeResult.first; if (upload != null) { upload.cancel(); + } else { + if (mCurrentUpload != null && mCurrentAccount != null && + mCurrentUpload.getRemotePath().startsWith(file.getRemotePath()) && + account.name.equals(mCurrentAccount.name)) { + mCurrentUpload.cancel(); + } } } /** - * Cancels a pending or current upload for an account + * Cancels all the uploads for an account * - * @param account Owncloud accountName where the remote file will be stored. + * @param account ownCloud account. */ public void cancel(Account account) { Log_OC.d(TAG, "Account= " + account.name); @@ -415,13 +408,14 @@ public class FileUploader extends Service } } // Cancel pending uploads - cancelUploadForAccount(account.name); + cancelUploadsForAccount(account); } public void clearListeners() { mBoundListeners.clear(); } + /** * Returns True when the file described by 'file' is being uploaded to * the ownCloud account 'account' or waiting for it @@ -433,22 +427,8 @@ public class FileUploader extends Service * @param file A file that could be in the queue of pending uploads */ public boolean isUploading(Account account, OCFile file) { - if (account == null || file == null) - return false; - String targetKey = buildRemoteName(account, file); - synchronized (mPendingUploads) { - if (file.isFolder()) { - // this can be slow if there are many uploads :( - Iterator it = mPendingUploads.keySet().iterator(); - boolean found = false; - while (it.hasNext() && !found) { - found = it.next().startsWith(targetKey); - } - return found; - } else { - return (mPendingUploads.containsKey(targetKey)); - } - } + if (account == null || file == null) return false; + return (mPendingUploads.contains(account, file.getRemotePath())); } @@ -497,15 +477,19 @@ public class FileUploader extends Service } /** - * Review uploads and cancel it if its account doesn't exist + * Builds a key for the map of listeners. + * + * TODO remove and replace key with file.getFileId() after changing current policy (upload file, then + * add to local database) to better policy (add to local database, then upload) + * + * @param account ownCloud account where the file to upload belongs. + * @param file File to upload + * @return Key */ - public void checkAccountOfCurrentUpload() { - if (mCurrentUpload != null && - !AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { - mCurrentUpload.cancel(); - } - // The rest of uploads are cancelled when they try to start + private String buildRemoteName(Account account, OCFile file) { + return account.name + file.getRemotePath(); } + } /** @@ -545,17 +529,13 @@ public class FileUploader extends Service /** * Core upload method: sends the file(s) to upload * - * @param uploadKey Key to access the upload to perform, contained in - * mPendingUploads + * @param uploadKey Key to access the upload to perform, contained in mPendingUploads */ public void uploadFile(String uploadKey) { - synchronized (mPendingUploads) { - mCurrentUpload = mPendingUploads.get(uploadKey); - } + mCurrentUpload = mPendingUploads.get(uploadKey); if (mCurrentUpload != null) { - // Detect if the account exists if (AccountUtils.exists(mCurrentUpload.getAccount(), getApplicationContext())) { Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().name + " exists"); @@ -565,16 +545,20 @@ public class FileUploader extends Service RemoteOperationResult uploadResult = null, grantResult; try { - /// prepare client object to send requests to the ownCloud server - if (mUploadClient == null || - !mLastAccount.equals(mCurrentUpload.getAccount())) { - mLastAccount = mCurrentUpload.getAccount(); - mStorageManager = - new FileDataStorageManager(mLastAccount, getContentResolver()); - OwnCloudAccount ocAccount = new OwnCloudAccount(mLastAccount, this); - mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, this); - } + /// prepare client object to send the request to the ownCloud server + if (mCurrentAccount == null || !mCurrentAccount.equals(mCurrentUpload.getAccount())) { + mCurrentAccount = mCurrentUpload.getAccount(); + mStorageManager = new FileDataStorageManager( + mCurrentAccount, + getContentResolver() + ); + } // else, reuse storage manager from previous operation + + // always get client from client manager, to get fresh credentials in case of update + OwnCloudAccount ocAccount = new OwnCloudAccount(mCurrentAccount, this); + mUploadClient = OwnCloudClientManagerFactory.getDefaultSingleton(). + getClientFor(ocAccount, this); + /// check the existence of the parent folder for the file to upload String remoteParentPath = new File(mCurrentUpload.getRemotePath()).getParent(); @@ -589,43 +573,44 @@ public class FileUploader extends Service uploadResult = mCurrentUpload.execute(mUploadClient); if (uploadResult.isSuccess()) { saveUploadedFile(); + + } else if (uploadResult.getCode() == ResultCode.SYNC_CONFLICT) { + mStorageManager.saveConflict(mCurrentUpload.getFile(), + mCurrentUpload.getFile().getEtagInConflict()); } } else { uploadResult = grantResult; } - } catch (AccountsException e) { - Log_OC.e(TAG, "Error while trying to get autorization for " + - mLastAccount.name, e); - uploadResult = new RemoteOperationResult(e); - - } catch (IOException e) { - Log_OC.e(TAG, "Error while trying to get autorization for " + - mLastAccount.name, e); + } catch (Exception e) { + Log_OC.e(TAG, "Error uploading", e); uploadResult = new RemoteOperationResult(e); } finally { - synchronized (mPendingUploads) { - mPendingUploads.remove(uploadKey); - Log_OC.i(TAG, "Remove CurrentUploadItem from pending upload Item Map."); - } - if (uploadResult != null && uploadResult.isException()) { - // enforce the creation of a new client object for next uploads; - // this grant that a new socket will be created in the future if - // the current exception is due to an abrupt lose of network connection - mUploadClient = null; + Pair removeResult; + if (mCurrentUpload.wasRenamed()) { + removeResult = mPendingUploads.removePayload( + mCurrentAccount, + mCurrentUpload.getOldFile().getRemotePath() + ); + } else { + removeResult = mPendingUploads.removePayload( + mCurrentAccount, + mCurrentUpload.getRemotePath() + ); } - } - /// notify result - notifyUploadResult(uploadResult, mCurrentUpload); - sendFinalBroadcast(mCurrentUpload, uploadResult); + /// notify result + notifyUploadResult(mCurrentUpload, uploadResult); + + sendBroadcastUploadFinished(mCurrentUpload, uploadResult, removeResult.second); + } } else { // Cancel the transfer Log_OC.d(TAG, "Account " + mCurrentUpload.getAccount().toString() + " doesn't exist"); - cancelUploadForAccount(mCurrentUpload.getAccount().name); + cancelUploadsForAccount(mCurrentUpload.getAccount()); } } @@ -692,7 +677,7 @@ public class FileUploader extends Service * synchronized with the server, specially the modification time and Etag * (where available) * - * TODO refactor this ugly thing + * TODO move into UploadFileOperation */ private void saveUploadedFile() { OCFile file = mCurrentUpload.getFile(); @@ -710,6 +695,8 @@ public class FileUploader extends Service if (result.isSuccess()) { updateOCFile(file, (RemoteFile) result.getData().get(0)); file.setLastSyncDateForProperties(syncDate); + } else { + Log_OC.e(TAG, "Error reading properties of file after successful upload; this is gonna hurt..."); } // / maybe this would be better as part of UploadFileOperation... or @@ -719,6 +706,7 @@ public class FileUploader extends Service if (oldFile.fileExists()) { oldFile.setStoragePath(null); mStorageManager.saveFile(oldFile); + mStorageManager.saveConflict(oldFile, null); } // else: it was just an automatic renaming due to a name // coincidence; nothing else is needed, the storagePath is right @@ -726,7 +714,10 @@ public class FileUploader extends Service } file.setNeedsUpdateThumbnail(true); mStorageManager.saveFile(file); + mStorageManager.saveConflict(file, null); + mStorageManager.triggerMediaScan(file.getStoragePath()); + } private void updateOCFile(OCFile file, RemoteFile remoteFile) { @@ -735,12 +726,11 @@ public class FileUploader extends Service file.setMimetype(remoteFile.getMimeType()); file.setModificationTimestamp(remoteFile.getModifiedTimestamp()); file.setModificationTimestampAtLastSyncForData(remoteFile.getModifiedTimestamp()); - // file.setEtag(remoteFile.getEtag()); // TODO Etag, where available + file.setEtag(remoteFile.getEtag()); file.setRemoteId(remoteFile.getRemoteId()); } - private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType, - FileDataStorageManager storageManager) { + private OCFile obtainNewOCFileToUpload(String remotePath, String localPath, String mimeType) { // MIME type if (mimeType == null || mimeType.length() <= 0) { @@ -831,11 +821,11 @@ public class FileUploader extends Service /** * Updates the status notification with the result of an upload operation. * - * @param uploadResult Result of the upload operation. - * @param upload Finished upload operation + * @param uploadResult Result of the upload operation. + * @param upload Finished upload operation */ - private void notifyUploadResult( - RemoteOperationResult uploadResult, UploadFileOperation upload) { + private void notifyUploadResult(UploadFileOperation upload, + RemoteOperationResult uploadResult) { Log_OC.d(TAG, "NotifyUploadResult with resultCode: " + uploadResult.getCode()); // / cancelled operation or success -> silent removal of progress notification mNotificationManager.cancel(R.string.uploader_upload_in_progress_ticker); @@ -942,10 +932,15 @@ public class FileUploader extends Service * Sends a broadcast in order to the interested activities can update their * view * - * @param upload Finished upload operation - * @param uploadResult Result of the upload operation + * @param upload Finished upload operation + * @param uploadResult Result of the upload operation + * @param unlinkedFromRemotePath Path in the uploads tree where the upload was unlinked from */ - private void sendFinalBroadcast(UploadFileOperation upload, RemoteOperationResult uploadResult) { + private void sendBroadcastUploadFinished( + UploadFileOperation upload, + RemoteOperationResult uploadResult, + String unlinkedFromRemotePath) { + Intent end = new Intent(getUploadFinishMessage()); end.putExtra(EXTRA_REMOTE_PATH, upload.getRemotePath()); // real remote // path, after @@ -958,6 +953,10 @@ public class FileUploader extends Service end.putExtra(EXTRA_OLD_FILE_PATH, upload.getOriginalStoragePath()); end.putExtra(ACCOUNT_NAME, upload.getAccount().name); end.putExtra(EXTRA_UPLOAD_RESULT, uploadResult.isSuccess()); + if (unlinkedFromRemotePath != null) { + end.putExtra(EXTRA_LINKED_TO_PATH, unlinkedFromRemotePath); + } + sendStickyBroadcast(end); } @@ -967,6 +966,8 @@ public class FileUploader extends Service * @param localPath Full path to a file in the local file system. * @param mimeType MIME type of the file. * @return true if is needed to add the pdf file extension to the file + * + * TODO - move to OCFile or Utils class */ private boolean isPdfFileFromContentProviderWithoutExtension(String localPath, String mimeType) { @@ -977,20 +978,11 @@ public class FileUploader extends Service /** * Remove uploads of an account - * @param accountName Name of an OC account + * + * @param account Downloads account to remove */ - private void cancelUploadForAccount(String accountName){ - // this can be slow if there are many uploads :( - Iterator it = mPendingUploads.keySet().iterator(); - Log_OC.d(TAG, "Number of pending updloads= " + mPendingUploads.size()); - while (it.hasNext()) { - String key = it.next(); - Log_OC.d(TAG, "mPendingUploads CANCELLED " + key); - if (key.startsWith(accountName)) { - synchronized (mPendingUploads) { - mPendingUploads.remove(key); - } - } - } + private void cancelUploadsForAccount(Account account){ + // Cancel pending uploads + mPendingUploads.remove(account); } } diff --git a/src/com/owncloud/android/operations/DownloadFileOperation.java b/src/com/owncloud/android/operations/DownloadFileOperation.java index 0cb303ce..dff8aef1 100644 --- a/src/com/owncloud/android/operations/DownloadFileOperation.java +++ b/src/com/owncloud/android/operations/DownloadFileOperation.java @@ -27,7 +27,6 @@ import java.util.Iterator; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; -import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.lib.common.OwnCloudClient; @@ -52,6 +51,7 @@ public class DownloadFileOperation extends RemoteOperation { private OCFile mFile; private Set mDataTransferListeners = new HashSet(); private long mModificationTimestamp = 0; + private String mEtag = ""; private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); private DownloadRemoteFileOperation mDownloadOperation; @@ -127,11 +127,15 @@ public class DownloadFileOperation extends RemoteOperation { mFile.getModificationTimestamp(); } + public String getEtag() { + return mEtag; + } + @Override protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; - File newFile = null; - boolean moved = true; + RemoteOperationResult result; + File newFile; + boolean moved; /// download will be performed to a temporal file, then moved to the final location File tmpFile = new File(getTmpPath()); @@ -154,6 +158,7 @@ public class DownloadFileOperation extends RemoteOperation { if (result.isSuccess()) { mModificationTimestamp = mDownloadOperation.getModificationTimestamp(); + mEtag = mDownloadOperation.getEtag(); newFile = new File(getSavePath()); newFile.getParentFile().mkdirs(); moved = tmpFile.renameTo(newFile); diff --git a/src/com/owncloud/android/operations/RefreshFolderOperation.java b/src/com/owncloud/android/operations/RefreshFolderOperation.java index dade4d97..ddd843de 100644 --- a/src/com/owncloud/android/operations/RefreshFolderOperation.java +++ b/src/com/owncloud/android/operations/RefreshFolderOperation.java @@ -20,26 +20,17 @@ package com.owncloud.android.operations; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Vector; -import org.apache.http.HttpStatus; import android.accounts.Account; import android.content.Context; import android.content.Intent; import android.util.Log; -//import android.support.v4.content.LocalBroadcastManager; -import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -50,7 +41,6 @@ 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; import com.owncloud.android.lib.resources.shares.GetRemoteSharesForFileOperation; -import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation; import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation; import com.owncloud.android.lib.resources.files.RemoteFile; @@ -120,7 +110,10 @@ public class RefreshFolderOperation extends RemoteOperation { /** 'True' means that Etag will be ignored */ private boolean mIgnoreETag; - + private List mFilesToSyncContents; + // this will be used for every file when 'folder synchronization' replaces 'folder download' + + /** * Creates a new instance of {@link RefreshFolderOperation}. * @@ -154,6 +147,7 @@ public class RefreshFolderOperation extends RemoteOperation { mForgottenLocalFiles = new HashMap(); mRemoteFolderChanged = false; mIgnoreETag = ignoreETag; + mFilesToSyncContents = new Vector(); } @@ -191,7 +185,7 @@ public class RefreshFolderOperation extends RemoteOperation { mConflictsFound = 0; mForgottenLocalFiles.clear(); - if (FileUtils.PATH_SEPARATOR.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) { + if (OCFile.ROOT_PATH.equals(mLocalFolder.getRemotePath()) && !mSyncFullAccount) { updateOCVersion(client); } @@ -201,8 +195,14 @@ public class RefreshFolderOperation extends RemoteOperation { if (mRemoteFolderChanged) { result = fetchAndSyncRemoteFolder(client); } else { + fetchFavoritesToSyncFromLocalData(); mChildren = mStorageManager.getFolderContent(mLocalFolder, false); } + + if (result.isSuccess()) { + // request for the synchronization of KEPT-IN-SYNC file contents + startContentSynchronizations(mFilesToSyncContents, client); + } } if (!mSyncFullAccount) { @@ -238,9 +238,8 @@ public class RefreshFolderOperation extends RemoteOperation { private RemoteOperationResult checkForChanges(OwnCloudClient client) { mRemoteFolderChanged = true; RemoteOperationResult result = null; - String remotePath = null; + String remotePath = mLocalFolder.getRemotePath(); - remotePath = mLocalFolder.getRemotePath(); Log_OC.d(TAG, "Checking changes in " + mAccount.name + remotePath); // remote request @@ -263,7 +262,7 @@ public class RefreshFolderOperation extends RemoteOperation { result = new RemoteOperationResult(ResultCode.OK); - Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + + Log_OC.i(TAG, "Checked " + mAccount.name + remotePath + " : " + (mRemoteFolderChanged ? "changed" : "not changed")); } else { @@ -336,15 +335,15 @@ public class RefreshFolderOperation extends RemoteOperation { mLocalFolder = mStorageManager.getFileByPath(mLocalFolder.getRemotePath()); // parse data from remote folder - OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0)); + OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) folderAndFiles.get(0)); remoteFolder.setParentId(mLocalFolder.getParentId()); remoteFolder.setFileId(mLocalFolder.getFileId()); - Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + Log_OC.d(TAG, "Remote folder " + mLocalFolder.getRemotePath() + " changed - starting update of local data "); List updatedFiles = new Vector(folderAndFiles.size() - 1); - List filesToSyncContents = new Vector(); + mFilesToSyncContents.clear(); // get current data about local contents of the folder to synchronize List localFiles = mStorageManager.getFolderContent(mLocalFolder, false); @@ -354,54 +353,55 @@ public class RefreshFolderOperation extends RemoteOperation { } // loop to update every child - OCFile remoteFile = null, localFile = null; + OCFile remoteFile = null, localFile = null, updatedFile = null; + RemoteFile r; for (int i=1; i 0){ - out.write(buf, 0, len); - } - file.setStoragePath(expectedPath); - - } catch (Exception e) { - Log_OC.e(TAG, "Exception while copying foreign file " + expectedPath, e); - mForgottenLocalFiles.put(file.getRemotePath(), storagePath); - file.setStoragePath(null); - - } finally { - try { - if (in != null) in.close(); - } catch (Exception e) { - Log_OC.d(TAG, "Weird exception while closing input stream for " - + storagePath + " (ignoring)", e); - } - try { - if (out != null) out.close(); - } catch (Exception e) { - Log_OC.d(TAG, "Weird exception while closing output stream for " - + expectedPath + " (ignoring)", e); - } - } - } - } - } - - private RemoteOperationResult refreshSharesForFolder(OwnCloudClient client) { RemoteOperationResult result = null; @@ -571,24 +477,6 @@ public class RefreshFolderOperation extends RemoteOperation { /** - * Scans the default location for saving local copies of files searching for - * a 'lost' file with the same full name as the {@link OCFile} received as - * parameter. - * - * @param file File to associate a possible 'lost' local file. - */ - private void searchForLocalFileInDefaultPath(OCFile file) { - if (file.getStoragePath() == null && !file.isFolder()) { - File f = new File(FileStorageUtils.getDefaultSavePathFor(mAccount.name, file)); - if (f.exists()) { - file.setStoragePath(f.getAbsolutePath()); - file.setLastSyncDateForData(f.lastModified()); - } - } - } - - - /** * Sends a message to any application component interested in the progress * of the synchronization. * @@ -612,8 +500,20 @@ public class RefreshFolderOperation extends RemoteOperation { } - public boolean getRemoteFolderChanged() { - return mRemoteFolderChanged; + private void fetchFavoritesToSyncFromLocalData() { + List children = mStorageManager.getFolderContent(mLocalFolder, false); + for (OCFile child : children) { + if (!child.isFolder() && child.isFavorite()) { + SynchronizeFileOperation operation = new SynchronizeFileOperation( + child, + child, // cheating with the remote file to get an update to server; to refactor + mAccount, + true, + mContext + ); + mFilesToSyncContents.add(operation); + } + } } } diff --git a/src/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/com/owncloud/android/operations/SynchronizeFileOperation.java index f94adb15..bc0caf19 100644 --- a/src/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -22,7 +22,6 @@ package com.owncloud.android.operations; -import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; @@ -208,15 +207,13 @@ public class SynchronizeFileOperation extends SyncOperation { /// check changes in server and local file boolean serverChanged = false; - /* time for eTag is coming, but not yet - if (mServerFile.getEtag() != null) { - serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); - } else { */ - serverChanged = ( - mServerFile.getModificationTimestamp() != - mLocalFile.getModificationTimestampAtLastSyncForData() - ); - //} + if (mLocalFile.getEtag() == null || mLocalFile.getEtag().length() == 0) { + // file uploaded (null) or downloaded ("") before upgrade to version 1.8.0; check the old condition + serverChanged = mServerFile.getModificationTimestamp() != + mLocalFile.getModificationTimestampAtLastSyncForData(); + } else { + serverChanged = (!mServerFile.getEtag().equals(mLocalFile.getEtag())); + } boolean localChanged = ( mLocalFile.getLocalModificationTimestamp() > mLocalFile.getLastSyncDateForData() ); @@ -225,6 +222,7 @@ public class SynchronizeFileOperation extends SyncOperation { //if (!mLocalFile.getEtag().isEmpty() && localChanged && serverChanged) { if (localChanged && serverChanged) { result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + getStorageManager().saveConflict(mLocalFile, mServerFile.getEtag()); } else if (localChanged) { if (mSyncFileContents && mAllowUploads) { @@ -254,6 +252,7 @@ public class SynchronizeFileOperation extends SyncOperation { mServerFile.setLastSyncDateForData(mLocalFile.getLastSyncDateForData()); mServerFile.setStoragePath(mLocalFile.getStoragePath()); mServerFile.setParentId(mLocalFile.getParentId()); + mServerFile.setEtag(mLocalFile.getEtag()); getStorageManager().saveFile(mServerFile); } @@ -264,7 +263,11 @@ public class SynchronizeFileOperation extends SyncOperation { result = new RemoteOperationResult(ResultCode.OK); } - } + // safe blanket: sync'ing a not in-conflict file will clean wrong conflict markers in ancestors + if (result.getCode() != ResultCode.SYNC_CONFLICT) { + getStorageManager().saveConflict(mLocalFile, null); + } + } } diff --git a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java index d2d6c373..e4f96620 100644 --- a/src/com/owncloud/android/operations/SynchronizeFolderOperation.java +++ b/src/com/owncloud/android/operations/SynchronizeFolderOperation.java @@ -60,7 +60,8 @@ import java.util.concurrent.atomic.AtomicBoolean; * Fetches the list and properties of the files contained in the given folder, including their * properties, and updates the local database with them. * - * Does NOT enter in the child folders to synchronize their contents also. + * Does NOT enter in the child folders to synchronize their contents also, BUT requests for a new operation instance + * doing so. */ public class SynchronizeFolderOperation extends SyncOperation { @@ -96,10 +97,7 @@ public class SynchronizeFolderOperation extends SyncOperation { private List mFilesForDirectDownload; // to avoid extra PROPFINDs when there was no change in the folder - private List mFilesToSyncContentsWithoutUpload; - // this will go out when 'folder synchronization' replaces 'folder download'; step by step - - private List mFavouriteFilesToSyncContents; + private List mFilesToSyncContents; // this will be used for every file when 'folder synchronization' replaces 'folder download' private final AtomicBoolean mCancellationRequested; @@ -120,8 +118,7 @@ public class SynchronizeFolderOperation extends SyncOperation { mContext = context; mRemoteFolderChanged = false; mFilesForDirectDownload = new Vector(); - mFilesToSyncContentsWithoutUpload = new Vector(); - mFavouriteFilesToSyncContents = new Vector(); + mFilesToSyncContents = new Vector(); mCancellationRequested = new AtomicBoolean(false); } @@ -281,7 +278,7 @@ public class SynchronizeFolderOperation extends SyncOperation { FileDataStorageManager storageManager = getStorageManager(); // parse data from remote folder - OCFile remoteFolder = fillOCFile((RemoteFile)folderAndFiles.get(0)); + OCFile remoteFolder = FileStorageUtils.fillOCFile((RemoteFile) folderAndFiles.get(0)); remoteFolder.setParentId(mLocalFolder.getParentId()); remoteFolder.setFileId(mLocalFolder.getFileId()); @@ -290,8 +287,7 @@ public class SynchronizeFolderOperation extends SyncOperation { List updatedFiles = new Vector(folderAndFiles.size() - 1); mFilesForDirectDownload.clear(); - mFilesToSyncContentsWithoutUpload.clear(); - mFavouriteFilesToSyncContents.clear(); + mFilesToSyncContents.clear(); if (mCancellationRequested.get()) { throw new OperationCancelledException(); @@ -305,85 +301,77 @@ public class SynchronizeFolderOperation extends SyncOperation { } // loop to synchronize every child - OCFile remoteFile = null, localFile = null; + OCFile remoteFile = null, localFile = null, updatedFile = null; + RemoteFile r; for (int i=1; i mDataTransferListeners = new HashSet(); private AtomicBoolean mCancellationRequested = new AtomicBoolean(false); private Context mContext; @@ -313,65 +312,63 @@ public class UploadFileOperation extends RemoteOperation { (new File(mFile.getStoragePath())).length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE ) { mUploadOperation = new ChunkedUploadRemoteFileOperation(mFile.getStoragePath(), - mFile.getRemotePath(), mFile.getMimetype()); + mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict()); } else { mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(), - mFile.getRemotePath(), mFile.getMimetype()); + mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict()); } Iterator listener = mDataTransferListeners.iterator(); while (listener.hasNext()) { mUploadOperation.addDatatransferProgressListener(listener.next()); } - if (!mCancellationRequested.get()) { - result = mUploadOperation.execute(client); - - /// move local temporal file or original file to its corresponding - // location in the ownCloud local folder - if (result.isSuccess()) { - if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) { - mFile.setStoragePath(null); - } else if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_REMOVE){ - mFile.setStoragePath(null); - originalFile.delete(); - } else { - mFile.setStoragePath(expectedPath); - File fileToMove = null; - if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY - // ; see where temporalFile was - // set - fileToMove = temporalFile; - } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE - fileToMove = originalFile; - } - if (!expectedFile.equals(fileToMove)) { - File expectedFolder = expectedFile.getParentFile(); - expectedFolder.mkdirs(); - if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) { - mFile.setStoragePath(null); // forget the local file - // by now, treat this as a success; the file was - // uploaded; the user won't like that the local file - // is not linked, but this should be a very rare - // fail; - // the best option could be show a warning message - // (but not a fail) - // result = new - // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED); - // return result; - } + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } + + result = mUploadOperation.execute(client); + + /// move local temporal file or original file to its corresponding + // location in the ownCloud local folder + if (result.isSuccess()) { + if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) { + mFile.setStoragePath(null); + + } else { + mFile.setStoragePath(expectedPath); + File fileToMove = null; + if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY + // ; see where temporalFile was + // set + fileToMove = temporalFile; + } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE + fileToMove = originalFile; + } + if (!expectedFile.equals(fileToMove)) { + File expectedFolder = expectedFile.getParentFile(); + expectedFolder.mkdirs(); + if (!expectedFolder.isDirectory() || !fileToMove.renameTo(expectedFile)) { + mFile.setStoragePath(null); // forget the local file + // by now, treat this as a success; the file was + // uploaded; the user won't like that the local file + // is not linked, but this should be a very rare + // fail; + // the best option could be show a warning message + // (but not a fail) + // result = new + // RemoteOperationResult(ResultCode.LOCAL_STORAGE_NOT_MOVED); + // return result; } } FileDataStorageManager.triggerMediaScan(originalFile.getAbsolutePath()); FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath()); } + + } else if (result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED ) { + result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } } catch (Exception e) { - // TODO something cleaner with cancellations - if (mCancellationRequested.get()) { - result = new RemoteOperationResult(new OperationCancelledException()); - } else { - result = new RemoteOperationResult(e); - } + result = new RemoteOperationResult(e); } finally { if (temporalFile != null && !originalFile.equals(temporalFile)) { @@ -411,7 +408,7 @@ public class UploadFileOperation extends RemoteOperation { newFile.setModificationTimestamp(mFile.getModificationTimestamp()); newFile.setModificationTimestampAtLastSyncForData( mFile.getModificationTimestampAtLastSyncForData()); - // newFile.setEtag(mFile.getEtag()) + newFile.setEtag(mFile.getEtag()); newFile.setFavorite(mFile.isFavorite()); newFile.setLastSyncDateForProperties(mFile.getLastSyncDateForProperties()); newFile.setLastSyncDateForData(mFile.getLastSyncDateForData()); diff --git a/src/com/owncloud/android/providers/FileContentProvider.java b/src/com/owncloud/android/providers/FileContentProvider.java index 70cd8976..609c07ca 100644 --- a/src/com/owncloud/android/providers/FileContentProvider.java +++ b/src/com/owncloud/android/providers/FileContentProvider.java @@ -23,7 +23,6 @@ package com.owncloud.android.providers; import java.io.File; -import java.security.Provider; import java.util.ArrayList; import java.util.HashMap; @@ -107,6 +106,8 @@ public class FileContentProvider extends ContentProvider { ProviderTableMeta.FILE_UPDATE_THUMBNAIL); mFileProjectionMap.put(ProviderTableMeta.FILE_IS_DOWNLOADING, ProviderTableMeta.FILE_IS_DOWNLOADING); + mFileProjectionMap.put(ProviderTableMeta.FILE_ETAG_IN_CONFLICT, + ProviderTableMeta.FILE_ETAG_IN_CONFLICT); } private static final int SINGLE_FILE = 1; @@ -527,59 +528,6 @@ public class FileContentProvider extends ContentProvider { } } - /* - private int updateFolderSize(SQLiteDatabase db, String folderId) { - int count = 0; - String [] whereArgs = new String[] { folderId }; - - // read current size saved for the folder - long folderSize = 0; - long folderParentId = -1; - Uri selectFolderUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_FILE, folderId); - String[] folderProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT}; - String folderWhere = ProviderTableMeta._ID + "=?"; - Cursor folderCursor = query(db, selectFolderUri, folderProjection, folderWhere, whereArgs, null); - if (folderCursor != null && folderCursor.moveToFirst()) { - folderSize = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH));; - folderParentId = folderCursor.getLong(folderCursor.getColumnIndex(ProviderTableMeta.FILE_PARENT));; - } - folderCursor.close(); - - // read and sum sizes of children - long childrenSize = 0; - Uri selectChildrenUri = Uri.withAppendedPath(ProviderTableMeta.CONTENT_URI_DIR, folderId); - String[] childrenProjection = new String[] { ProviderTableMeta.FILE_CONTENT_LENGTH, ProviderTableMeta.FILE_PARENT}; - String childrenWhere = ProviderTableMeta.FILE_PARENT + "=?"; - Cursor childrenCursor = query(db, selectChildrenUri, childrenProjection, childrenWhere, whereArgs, null); - if (childrenCursor != null && childrenCursor.moveToFirst()) { - while (!childrenCursor.isAfterLast()) { - childrenSize += childrenCursor.getLong(childrenCursor.getColumnIndex(ProviderTableMeta.FILE_CONTENT_LENGTH)); - childrenCursor.moveToNext(); - } - } - childrenCursor.close(); - - // update if needed - if (folderSize != childrenSize) { - Log_OC.d("FileContentProvider", "Updating " + folderSize + " to " + childrenSize); - ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.FILE_CONTENT_LENGTH, childrenSize); - count = db.update(ProviderTableMeta.FILE_TABLE_NAME, cv, folderWhere, whereArgs); - - // propagate update until root - if (folderParentId > FileDataStorageManager.ROOT_PARENT_ID) { - Log_OC.d("FileContentProvider", "Propagating update to " + folderParentId); - updateFolderSize(db, String.valueOf(folderParentId)); - } else { - Log_OC.d("FileContentProvider", "NOT propagating to " + folderParentId); - } - } else { - Log_OC.d("FileContentProvider", "NOT updating, sizes are " + folderSize + " and " + childrenSize); - } - return count; - } -*/ - @Override public ContentProviderResult[] applyBatch (ArrayList operations) throws OperationApplicationException { @@ -636,7 +584,8 @@ public class FileContentProvider extends ContentProvider { + ProviderTableMeta.FILE_PERMISSIONS + " TEXT null," + ProviderTableMeta.FILE_REMOTE_ID + " TEXT null," + ProviderTableMeta.FILE_UPDATE_THUMBNAIL + " INTEGER," //boolean - + ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER);" //boolean + + ProviderTableMeta.FILE_IS_DOWNLOADING + " INTEGER," //boolean + + ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT);" ); // Create table ocshares @@ -836,6 +785,24 @@ public class FileContentProvider extends ContentProvider { if (!upgraded) Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + ", newVersion == " + newVersion); + + if (oldVersion < 11 && newVersion >= 11) { + Log_OC.i("SQL", "Entering in the #11 ADD in onUpgrade"); + db.beginTransaction(); + try { + db .execSQL("ALTER TABLE " + ProviderTableMeta.FILE_TABLE_NAME + + " ADD COLUMN " + ProviderTableMeta.FILE_ETAG_IN_CONFLICT + " TEXT " + + " DEFAULT NULL"); + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + if (!upgraded) + Log_OC.i("SQL", "OUT of the ADD in onUpgrade; oldVersion == " + oldVersion + + ", newVersion == " + newVersion); + } } diff --git a/src/com/owncloud/android/services/OperationsService.java b/src/com/owncloud/android/services/OperationsService.java index 099bd087..c1228170 100644 --- a/src/com/owncloud/android/services/OperationsService.java +++ b/src/com/owncloud/android/services/OperationsService.java @@ -100,7 +100,7 @@ public class OperationsService extends Service { public static final String ACTION_REMOVE = "REMOVE"; public static final String ACTION_CREATE_FOLDER = "CREATE_FOLDER"; public static final String ACTION_SYNC_FILE = "SYNC_FILE"; - public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER";//for the moment, just to download + public static final String ACTION_SYNC_FOLDER = "SYNC_FOLDER"; public static final String ACTION_MOVE_FILE = "MOVE_FILE"; public static final String ACTION_COPY_FILE = "COPY_FILE"; @@ -234,7 +234,6 @@ public class OperationsService extends Service { */ @Override public IBinder onBind(Intent intent) { - //Log_OC.wtf(TAG, "onBind" ); return mOperationsBinder; } @@ -615,7 +614,7 @@ public class OperationsService extends Service { ); } else if (action.equals(ACTION_SYNC_FOLDER)) { - // Sync file + // Sync folder (all its descendant files are sync'ed) String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); operation = new SynchronizeFolderOperation( this, // TODO remove this dependency from construction time @@ -623,7 +622,7 @@ public class OperationsService extends Service { account, System.currentTimeMillis() // TODO remove this dependency from construction time ); - + } else if (action.equals(ACTION_MOVE_FILE)) { // Move file/folder String remotePath = operationIntent.getStringExtra(EXTRA_REMOTE_PATH); @@ -726,7 +725,6 @@ public class OperationsService extends Service { } } if (count == 0) { - //mOperationResults.put(operation.hashCode(), result); Pair undispatched = new Pair(operation, result); mUndispatchedFinishedOperations.put(((Runnable) operation).hashCode(), undispatched); diff --git a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java index 7f8b0db1..7dfe5201 100644 --- a/src/com/owncloud/android/syncadapter/FileSyncAdapter.java +++ b/src/com/owncloud/android/syncadapter/FileSyncAdapter.java @@ -30,7 +30,6 @@ import java.util.Map; import org.apache.jackrabbit.webdav.DavException; -import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -59,7 +58,7 @@ import android.support.v4.app.NotificationCompat; * Implementation of {@link AbstractThreadedSyncAdapter} responsible for synchronizing * ownCloud files. * - * Performs a full synchronization of the account recieved in {@link #onPerformSync(Account, Bundle, + * Performs a full synchronization of the account received in {@link #onPerformSync(Account, Bundle, * String, ContentProviderClient, SyncResult)}. */ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { @@ -77,9 +76,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { ".EVENT_FULL_SYNC_END"; public static final String EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED = FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_CONTENTS_SYNCED"; - //public static final String EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED = - // FileSyncAdapter.class.getName() + ".EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED"; - + public static final String EXTRA_ACCOUNT_NAME = FileSyncAdapter.class.getName() + ".EXTRA_ACCOUNT_NAME"; public static final String EXTRA_FOLDER_PATH = FileSyncAdapter.class.getName() + @@ -268,16 +265,6 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { if (mFailedResultsCounter > MAX_FAILED_RESULTS || isFinisher(mLastFailedResult)) return; - /* - OCFile folder, - long currentSyncTime, - boolean updateFolderProperties, - boolean syncFullAccount, - DataStorageManager dataStorageManager, - Account account, - Context context ) { - } - */ // folder synchronization RefreshFolderOperation synchFolderOp = new RefreshFolderOperation( folder, mCurrentSyncTime, @@ -308,7 +295,7 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { // synchronize children folders List children = synchFolderOp.getChildren(); // beware of the 'hidden' recursion here! - fetchChildren(folder, children, synchFolderOp.getRemoteFolderChanged()); + syncChildren(children); } } else { @@ -351,25 +338,19 @@ public class FileSyncAdapter extends AbstractOwnCloudSyncAdapter { /** * Triggers the synchronization of any folder contained in the list of received files. + * + * No consideration of etag here because it MUST walk down anyway, in case that kept-in-sync files + * have local changes. * * @param files Files to recursively synchronize. */ - private void fetchChildren(OCFile parent, List files, boolean parentEtagChanged) { + private void syncChildren(List files) { int i; - OCFile newFile = null; - //String etag = null; - //boolean syncDown = false; + OCFile newFile; for (i=0; i < files.size() && !mCancellation; i++) { newFile = files.get(i); if (newFile.isFolder()) { - /* - etag = newFile.getEtag(); - syncDown = (parentEtagChanged || etag == null || etag.length() == 0); - if(syncDown) { */ - synchronizeFolder(newFile); - //sendLocalBroadcast(EVENT_FULL_SYNC_FOLDER_SIZE_SYNCED, parent.getRemotePath(), - // null); - //} + synchronizeFolder(newFile); } } diff --git a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java index ec214a5e..5debcd43 100644 --- a/src/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1131,7 +1131,11 @@ public class FileDisplayActivity extends HookActivity (uploadedRemotePath.startsWith(currentDir.getRemotePath())); if (sameAccount && isDescendant) { - refreshListOfFilesFragment(); + String linkedToRemotePath = + intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH); + if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { + refreshListOfFilesFragment(); + } } boolean uploadWasFine = intent.getBooleanExtra(FileUploader.EXTRA_UPLOAD_RESULT, @@ -1184,6 +1188,16 @@ public class FileDisplayActivity extends HookActivity } + // TODO refactor this receiver, and maybe DownloadFinishReceiver; this method is duplicated :S + private boolean isAscendant(String linkedToRemotePath) { + OCFile currentDir = getCurrentDir(); + return ( + currentDir != null && + currentDir.getRemotePath().startsWith(linkedToRemotePath) + ); + } + + } @@ -1195,11 +1209,10 @@ public class FileDisplayActivity extends HookActivity */ private class DownloadFinishReceiver extends BroadcastReceiver { - //int refreshCounter = 0; @Override public void onReceive(Context context, Intent intent) { try { - boolean sameAccount = isSameAccount(context, intent); + boolean sameAccount = isSameAccount(intent); String downloadedRemotePath = intent.getStringExtra(FileDownloader.EXTRA_REMOTE_PATH); boolean isDescendant = isDescendant(downloadedRemotePath); @@ -1208,7 +1221,6 @@ public class FileDisplayActivity extends HookActivity String linkedToRemotePath = intent.getStringExtra(FileDownloader.EXTRA_LINKED_TO_PATH); if (linkedToRemotePath == null || isAscendant(linkedToRemotePath)) { - //Log_OC.v(TAG, "refresh #" + ++refreshCounter); refreshListOfFilesFragment(); } refreshSecondFragment( @@ -1250,7 +1262,7 @@ public class FileDisplayActivity extends HookActivity ); } - private boolean isSameAccount(Context context, Intent intent) { + private boolean isSameAccount(Intent intent) { String accountName = intent.getStringExtra(FileDownloader.ACCOUNT_NAME); return (accountName != null && getAccount() != null && accountName.equals(getAccount().name)); diff --git a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java index 3aecd7e8..956c7d1f 100644 --- a/src/com/owncloud/android/ui/adapter/FileListListAdapter.java +++ b/src/com/owncloud/android/ui/adapter/FileListListAdapter.java @@ -257,24 +257,47 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { mTransferServiceGetter.getFileDownloaderBinder(); FileUploaderBinder uploaderBinder = mTransferServiceGetter.getFileUploaderBinder(); - boolean downloading = (downloaderBinder != null && - downloaderBinder.isDownloading(mAccount, file)); OperationsServiceBinder opsBinder = mTransferServiceGetter.getOperationsServiceBinder(); - downloading |= (opsBinder != null && - opsBinder.isSynchronizing(mAccount, file.getRemotePath())); - if (downloading) { - localStateView.setImageResource(R.drawable.downloading_file_indicator); + + localStateView.setVisibility(View.INVISIBLE); // default first + + if ( //synchronizing + opsBinder != null && + opsBinder.isSynchronizing(mAccount, file.getRemotePath()) + ) { + localStateView.setImageResource(R.drawable.synchronizing_file_indicator); + localStateView.setVisibility(View.VISIBLE); + + } else if ( // downloading + downloaderBinder != null && + downloaderBinder.isDownloading(mAccount, file) + ) { + localStateView.setImageResource( + file.isFolder() ? + R.drawable.synchronizing_file_indicator : + R.drawable.downloading_file_indicator + ); localStateView.setVisibility(View.VISIBLE); - } else if (uploaderBinder != null && - uploaderBinder.isUploading(mAccount, file)) { - localStateView.setImageResource(R.drawable.uploading_file_indicator); + + } else if ( //uploading + uploaderBinder != null && + uploaderBinder.isUploading(mAccount, file) + ) { + localStateView.setImageResource( + file.isFolder() ? + R.drawable.synchronizing_file_indicator : + R.drawable.uploading_file_indicator + ); + localStateView.setVisibility(View.VISIBLE); + + } else if (file.getEtagInConflict() != null) { // conflict + localStateView.setImageResource(R.drawable.conflict_file_indicator); localStateView.setVisibility(View.VISIBLE); + } else if (file.isDown()) { localStateView.setImageResource(R.drawable.local_file_indicator); localStateView.setVisibility(View.VISIBLE); - } else { - localStateView.setVisibility(View.INVISIBLE); } // share with me icon @@ -377,46 +400,6 @@ public class FileListListAdapter extends BaseAdapter implements ListAdapter { return view; } - /** - * Local Folder size in human readable format - * - * @param path - * String - * @return Size in human readable format - */ - private String getFolderSizeHuman(String path) { - - File dir = new File(path); - - if (dir.exists()) { - long bytes = FileStorageUtils.getFolderSize(dir); - return DisplayUtils.bytesToHumanReadable(bytes); - } - - return "0 B"; - } - - /** - * Local Folder size - * @param dir File - * @return Size in bytes - */ - private long getFolderSize(File dir) { - if (dir.exists()) { - long result = 0; - File[] fileList = dir.listFiles(); - for(int i = 0; i < fileList.length; i++) { - if(fileList[i].isDirectory()) { - result += getFolderSize(fileList[i]); - } else { - result += fileList[i].length(); - } - } - return result; - } - return 0; - } - @Override public int getViewTypeCount() { return 1; diff --git a/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java b/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java index 0b0883c2..d42cf465 100644 --- a/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java +++ b/src/com/owncloud/android/ui/dialog/RemoveFileDialogFragment.java @@ -25,7 +25,6 @@ package com.owncloud.android.ui.dialog; * * Triggers the removal according to the user response. */ -import java.util.Vector; import android.app.Dialog; import android.os.Bundle; @@ -106,33 +105,6 @@ implements ConfirmationDialogFragmentListener { public void onCancel(String callerTag) { ComponentsGetter cg = (ComponentsGetter)getActivity(); cg.getFileOperationsHelper().removeFile(mTargetFile, true); - - FileDataStorageManager storageManager = cg.getStorageManager(); - - boolean containsFavorite = false; - if (mTargetFile.isFolder()) { - Vector files = storageManager.getFolderContent(mTargetFile, false); - for(OCFile file: files) { - containsFavorite = file.isFavorite() || containsFavorite; - - if (containsFavorite) - break; - } - } - - // Remove etag for parent, if file is a favorite - // or is a folder and contains favorite - if (mTargetFile.isFavorite() || containsFavorite) { - OCFile folder = null; - if (mTargetFile.isFolder()) { - folder = mTargetFile; - } else { - folder = storageManager.getFileById(mTargetFile.getParentId()); - } - - folder.setEtag(""); - storageManager.saveFile(folder); - } } @Override diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index b2e6464c..6aafbf76 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -248,9 +248,8 @@ public class FileDetailFragment extends FileFragment implements OnClickListener dialog.show(getFragmentManager(), FTAG_RENAME_FILE); return true; } - case R.id.action_cancel_download: - case R.id.action_cancel_upload: { - ((FileDisplayActivity) mContainerActivity).cancelTransference(getFile()); + case R.id.action_cancel_sync: { + ((FileDisplayActivity)mContainerActivity).cancelTransference(getFile()); return true; } case R.id.action_download_file: @@ -300,7 +299,6 @@ public class FileDetailFragment extends FileFragment implements OnClickListener } } - /** * Check if the fragment was created with an empty layout. An empty fragment can't show file details, must be replaced. * diff --git a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java index c639525e..804d5074 100644 --- a/src/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -578,8 +578,7 @@ public class OCFileListFragment extends ExtendedListFragment { mContainerActivity.getFileOperationsHelper().syncFile(mTargetFile); return true; } - case R.id.action_cancel_download: - case R.id.action_cancel_upload: { + case R.id.action_cancel_sync: { ((FileDisplayActivity) mContainerActivity).cancelTransference(mTargetFile); return true; } diff --git a/src/com/owncloud/android/utils/DisplayUtils.java b/src/com/owncloud/android/utils/DisplayUtils.java index f63d7ec4..25cc55a4 100644 --- a/src/com/owncloud/android/utils/DisplayUtils.java +++ b/src/com/owncloud/android/utils/DisplayUtils.java @@ -213,19 +213,7 @@ public class DisplayUtils { else if ((System.currentTimeMillis() - time) < 60 * 1000) { return c.getString(R.string.file_list_seconds_ago); } else { - // Workaround 2.x bug (see https://github.com/owncloud/android/issues/716) - if ( Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB && - (System.currentTimeMillis() - time) > 24 * 60 * 60 * 1000 ) { - Date date = new Date(time); - date.setHours(0); - date.setMinutes(0); - date.setSeconds(0); - dateString = DateUtils.getRelativeDateTimeString( - c, date.getTime(), minResolution, transitionResolution, flags - ); - } else { - dateString = DateUtils.getRelativeDateTimeString(c, time, minResolution, transitionResolution, flags); - } + dateString = DateUtils.getRelativeDateTimeString(c, time, minResolution, transitionResolution, flags); } String[] parts = dateString.toString().split(","); @@ -236,8 +224,8 @@ public class DisplayUtils { return parts[1]; } } - //dateString contains unexpected format. use localized, absolute date. - return DisplayUtils.unixTimeToHumanReadable(time); + //dateString contains unexpected format. fallback: use relative date time string from android api as is. + return dateString.toString(); } /** diff --git a/src/com/owncloud/android/utils/ErrorMessageAdapter.java b/src/com/owncloud/android/utils/ErrorMessageAdapter.java index b80cc573..945f70f8 100644 --- a/src/com/owncloud/android/utils/ErrorMessageAdapter.java +++ b/src/com/owncloud/android/utils/ErrorMessageAdapter.java @@ -238,7 +238,7 @@ public class ErrorMessageAdapter { } else { // Generic error // Show a Message, operation finished without success - message = String.format(res.getString(R.string.download_folder_failed_content), + message = String.format(res.getString(R.string.sync_folder_failed_content), folderPathName); } } diff --git a/src/com/owncloud/android/utils/FileStorageUtils.java b/src/com/owncloud/android/utils/FileStorageUtils.java index 757f896c..47a46d2c 100644 --- a/src/com/owncloud/android/utils/FileStorageUtils.java +++ b/src/com/owncloud/android/utils/FileStorageUtils.java @@ -35,6 +35,7 @@ import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.resources.files.RemoteFile; +import android.accounts.Account; import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; @@ -56,8 +57,6 @@ public class FileStorageUtils { public static Boolean mSortAscending = true; - //private static final String LOG_TAG = "FileStorageUtils"; - public static final String getSavePath(String accountName) { File sdCard = Environment.getExternalStorageDirectory(); return sdCard.getAbsolutePath() + "/" + MainApp.getDataFolder() + "/" + Uri.encode(accountName, "@"); @@ -123,7 +122,7 @@ public class FileStorageUtils { * Creates and populates a new {@link OCFile} object with the data read from the server. * * @param remote remote file read from the server (remote file or folder). - * @return New OCFile instance representing the remote resource described by we. + * @return New OCFile instance representing the remote resource described by remote. */ public static OCFile fillOCFile(RemoteFile remote) { OCFile file = new OCFile(remote.getRemotePath()); @@ -393,5 +392,33 @@ public class FileStorageUtils { String result = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()); return (result != null) ? result : ""; } - + + /** + * Scans the default location for saving local copies of files searching for + * a 'lost' file with the same full name as the {@link OCFile} received as + * parameter. + * + * This method helps to keep linked local copies of the files when the app is uninstalled, and then + * reinstalled in the device. OR after the cache of the app was deleted in system settings. + * + * The method is assuming that all the local changes in the file where synchronized in the past. This is dangerous, + * but assuming the contrary could lead to massive unnecessary synchronizations of downloaded file after deleting + * the app cache. + * + * This should be changed in the near future to avoid any chance of data loss, but we need to add some options + * to limit hard automatic synchronizations to wifi, unless the user wants otherwise. + * + * @param file File to associate a possible 'lost' local file. + * @param account Account holding file. + */ + public static void searchForLocalFileInDefaultPath(OCFile file, Account account) { + if (file.getStoragePath() == null && !file.isFolder()) { + File f = new File(FileStorageUtils.getDefaultSavePathFor(account.name, file)); + if (f.exists()) { + file.setStoragePath(f.getAbsolutePath()); + file.setLastSyncDateForData(f.lastModified()); + } + } + } + }