From 207f8947cfaec99a476d0d2b68e32ea09ec48b1f Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 4 Nov 2019 03:10:40 +0000 Subject: [PATCH 01/69] [tx-robot] updated from transifex --- src/main/res/values-eo/strings.xml | 60 +++++++++++++++--------------- src/main/res/values-pl/strings.xml | 4 ++ src/main/res/values-sl/strings.xml | 20 +++++++++- src/main/res/values-sv/strings.xml | 8 ++-- 4 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/main/res/values-eo/strings.xml b/src/main/res/values-eo/strings.xml index 373ea5947ee7..815998906024 100644 --- a/src/main/res/values-eo/strings.xml +++ b/src/main/res/values-eo/strings.xml @@ -57,9 +57,9 @@ Sekura konekto stariĝis SSL-ekigo malsukcesis Ne eblis kontroli SSL-servilan identecon - Provante konekton + Provado de la konekto La servilo tro malfruis por respondi - Provante ensaluti... + Provo ensaluti... Malĝusta uzantonomo aŭ pasvorto Nekonata eraro: %1$s Nekonata HTTP-eraro okazis! @@ -98,7 +98,7 @@ Eraro Nesufiĉa memoro Nekonata eraro - Ŝargante... + Ŝargado... Ne Akcepti Pritraktotaj @@ -142,7 +142,7 @@ Kopii aŭ movi en ĉifritan dosierujon ne estas subtenata momente. Kopii al... Ne eblis krei dosierujon - Kreante dosieron el ŝablono... + Kreado de dosiero el ŝablono... Krei novan dokumenton Krei novan dosierujon Krei novan prezentaĵon @@ -165,8 +165,8 @@ Elŝuto malsukcesis, bonvolu re-ensaluti Elŝuto malsukcesis La dosiero ne plu disponeblas en la servilo - %1$d%% Elŝutante %2$s - Elŝutante... + %1$d%% Elŝutado %2$s + Elŝutado... %1$s elŝutiĝis Elŝutita Ankoraŭ ne elŝutita @@ -198,17 +198,17 @@ modifi Difini kiel ĉifrita Agordi ĉifradon - Malĉifrante... + Malĉifrado... Fermi Bonvolu entajpi pasvorton por malĉifri privatan ŝlosilon. Tiu dosierujo ne malplenas. - Generante novajn ŝlosilojn... + Genero de novaj ŝlosiloj... Tiu 12-vorta frazo estas tre fortika pasvorto, kiu permesas nur al vi vidi kaj uzi viajn ĉifritajn dosierojn. Bonvolu noti tiun frazon kaj konservi ĝin en fidinda loko. Tutvoja ĉifrado malebligita en la servilo. Ĉifrado funkcias nur ekde KitKat (Android versio 4.4). Notu vian 12-vortan ĉifran pasfrazon Pasvorto... - Ricevante ŝlosilojn... + Ricevo de ŝlosiloj... Konservado de la ŝlosiloj Agordi ĉifradon Ne povis konservi ŝlosilojn, bonvolu reprovi. @@ -251,12 +251,12 @@ Alŝutu kelkajn videaĵojn aŭ aktivigu aŭtomatan alŝuton. Neniu videaĵo. dosierujo - Ŝargante... + Ŝargado... Neniu aplikaĵo agordita por malfermi tiun dosiertipon. sekundoj antaŭe - Kontrolante la celon... - Purigante... - Ĝisdatigante la vojon al konservejo + Kontrolado de la celo... + Purigado... + Ĝisdatigo de la vojo al konservejo Datumdosierujo jam ekzistas. Elektu unu opcion: Nextcloud-dosierujo jam ekzistas Pli da diskospaco bezonata @@ -264,17 +264,17 @@ Ne eblis skribi al la celdosiero Malsukceso dum datummigrado Ne eblis ĝisdatigi la indekson - Movante datumojn... + Movo de datumoj... Finita Anstataŭigi - Preparante la datummigradon... - Restaŭrante la kontagordon... - Konservante la kontagordon... + Preparado de la datummigrado... + Restaŭrado de la kontagordo... + Konservado de la kontagordo... Ĉu vi plu volas ŝanĝi la konservejan lokon al %1$s?\n\nNotu: ĉiuj datumoj estos denove elŝutitaj. Fontodosierujo ne legeblas! - Ĝisdatigante la indekson... + Ĝisdatigo de la indekso... Uzi - Atendante la plenan sinkronigon... + Atendo de la plena sinkronigo... Netrovita dosiero La dosiero ne eblis sinkroniĝi. Nun montriĝas lasta disponebla versio. Alinomi @@ -304,8 +304,8 @@ forigi ĉi tiun dosieron movi ĉi tiun dosieron alinomi ĉi tiun dosieron - Elŝutante dosierojn... - Alŝutante dosierojn... + Elŝutado de dosieroj... + Alŝutado de dosieroj... Kelkaj dosieroj ne eblis moviĝi Loka: %1$s Movi ĉion @@ -390,7 +390,7 @@ Montras alŝutoprogreson Kanalo por sciigi alŝuton Piktogramo pri sciigoj - Ŝargante sciigojn... + Ŝargo de la sciigoj... Neniu sciigo Bonvolu rekontroli poste. Neniu reta konekto @@ -624,7 +624,7 @@ Dosieroj Butono pri agordoj Agordi dosierujojn - Ŝargante dosierujojn... + Ŝargado de dosierujoj... Ni tute elrenovigis tuj-alŝutadon. Reagordu vian aŭtomatan alŝuton pere de la ĉefa menuo.\n\nUzu la novan, kaj pli kapablan, aŭtomatan alŝuton. Neniu aŭdovida dosierujo troveblas Agordoj pri aŭtomata alŝuto @@ -695,7 +695,7 @@ Forigi la dosieron el la origina dosierujo alŝuti al ĉi tiu dosierujo %1$d%% Alŝutante %2$s - Alŝutante… + Alŝutado… %1$s alŝutiĝis Forlasi Agordi @@ -704,7 +704,7 @@ Nuna Malsukcesa aŭ atendante reprovadon Alŝutita - Atendante por alŝuti + Atendo por alŝuti Alŝutoj Nuligita Konflikto @@ -719,9 +719,9 @@ Plenumita Nekonata eraro Komputila viruso eltrovita. Alŝuto ne povas plui! - Atendante eliron el energiŝpara reĝimo - Atendante ŝargon de via aparato - Atendante sendratan reton („Wi-Fi“) + Atendo de eliro el energiŝpara reĝimo + Atendo de la ŝargo de via aparato + Atendado de sendrata reto („Wi-Fi“) Uzanto Adreso Retpoŝtadreso @@ -734,8 +734,8 @@ Uzantonomo Elŝuti Atendu momenton... - Kontrolante konservitajn akreditilojn - Kopiante dosieron el malpublika konservejo + Kontrolado de konservitaj akreditiloj + Kopio de dosiero el malpublika konservejo Bildo pri Kio nova Preterpasi Novaĵoj en %1$s diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 031a663454bc..dd3c6a2e7823 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -265,6 +265,7 @@ Dodaj do ulubionych Ulubiony Brak aplikacji do wysyłania wiadomości! + plik Usuń Błąd podczas pobierania aktywności dla pliku Nie udało się załadować szczegółów @@ -334,6 +335,7 @@ Bezpieczna współpraca i wymiana plików Łatwy w użyciu klient poczty, kalendarz i kontakty Udostępnianie ekranu, spotkania i konferencje online + katalog Katalog już istnieje Utwórz Ikona katalogu @@ -589,6 +591,8 @@ Udostępnij %1$s Pobierz link %1$s (grupa) + Udostępnij link wewnętrzny + Działa tylko dla użytkowników mających dostęp do %1$s %1$s ( w %2$s ) Musisz wprowadzić hasło Wystąpił błąd podczas udostępniania tego pliku lub katalogu diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index 18526e933564..c4d189bafaab 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -204,6 +204,7 @@ Skupnost Trenutni račun Zadnji račun + Slika ozadja glave bočnega okna Dejavnosti Vse datoteke Priljubljene @@ -256,7 +257,9 @@ Dodaj med priljubljene Priljubljeno Program za pošiljanje elektronskih sporočil ni določen! + datoteka Izbriši + Napaka pridobivanja dejavnosti za datoteko Nalaganje podrobnosti je spodletelo. Datoteka Ohrani @@ -307,6 +310,7 @@ Uporabi V čakanju na polno uskladitev … Datoteke ni mogoče najti + Datoteke ni mogoče uskladiti. Prikazana je zadnja razpoložljiva različica. Preimenuj Prišlo je do napake med obnavljanjem različice datoteke! Različica datoteke je uspešno obnovljena. @@ -318,6 +322,7 @@ Nedovoljeni znaki: / \\ < > : \" | ? * Ime datoteke vsebuje vsaj en neveljaven znak. Ime datoteke + mapa Mapa že obstaja Ustvari Ikona mape @@ -480,8 +485,11 @@ Pokaži skrite datoteke Pridobi izvorno kodo Mesto shrambe - Lokalna mapa + Krajevna mapa Mapa na strežniku + Tema + Temna + Svetla Predogled slike Ni krajevne datoteke za predogled Te slike ni mogoče prikazati @@ -489,6 +497,7 @@ Poskusite %1$s na vaši napravi! Želim priporočiti program %1$s!\nPrejeti ga je mogoče prek: %2$s %1$s ali %2$s + Iskanje datoteke je spodletelo! Brisanje je spodletelo Odstranjevanje obvestil je spodletelo. Odstrani @@ -496,6 +505,8 @@ Vnesite novo ime Ni mogoče preimenovati krajevne kopije. Poskusite vpisati drugačno ime. Preimenovanje ni mogoče. Ime je že uporabljeno. + Osveževanje ni dovoljena + Osveževanje ni dovoljeno Obnovi datoteko Obnovi izbrisano datoteko Poteka pridobivanje datoteke ... @@ -503,6 +514,7 @@ Zahtevano je geslo Napačno geslo Prijava s kodo QR + Brskanje in souporaba Vsi vaši računi na enem mestu Samodejno pošiljanje @@ -533,12 +545,14 @@ Nastavi datum preteka Nastavi geslo Ni datotek, ki bi jih omogočili za souporabo z drugimi + Zaščiteno z geslom lahko ureja lahko spremeni lahko ustvari lahko izbriše lahko omogoči souporabo %1$s (oddaljeno) + Sporočilo za prejemnika Dovoli urejanje Nastavi datum preteka Skrij prejem @@ -549,6 +563,7 @@ Omogoči souporabo z %1$s Omogoči souporabo … Omogoči souporabo s skupino ali z uporabnikom + Prijava prek ponudnika Razvrsti najprej najnovejše najprej najstarejše @@ -590,6 +605,7 @@ Video Glasba Slike + Pretakaj s programom ... Notranji pretok ni mogoč \"%1$s\" vam je oddan v souporabo Uporabnik %1$s je omogočil souporabo \"%2$s\" @@ -619,7 +635,9 @@ Izbrisane datoteke Ni izbrisanih datotek Datoteke %1$s ni mogoče obnoviti! + Nalaganje smeti je spodletelo! Odstrani šifriranje + Odstrani iz priljubljenih Prišlo je do napake med poskusom odstranjevanja souporabe te datoteke ali mape Ni mogoče prekiniti souporabe. Preverite, ali datoteka obstaja. za preklic souporabe datoteke. diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index f2043310aad7..1cd8320f0e76 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -408,7 +408,7 @@ Ett fel uppstod vid försök att flytta denna fil eller mapp Det är inte möjligt att flytta en mapp till en av dess undermappar Filen finns redan i målmappen - Kunde inte flytta fil. Vänligen kolla om den finns + Kan inte flytta fil. Vänligen kontrollera om det finns Flytta till… Ett fel uppstod i väntan på servern. Kunde inte slutföra operationen Ett fel uppstod under anslutningen till servern @@ -443,7 +443,7 @@ Meddelandeikon Läser in aviseringar… Inga aviseringar - Vänligen kolla igen senare. + Vänligen kontrollera igen senare. Ingen internetanslutning Operationen har avbrutits Servern har nått slutet av livet, vänligen uppgradera! @@ -728,7 +728,7 @@ Sluta dela misslyckades Åtkomst via opålitlig domän. Se dokumentation för ytterligare information. Ett fel inträffade när delningen skulle uppdateras - Kunde inte uppdatera. Vänligen kolla om filen existerar. + Det går inte att uppdatera. Vänligen kontrollera om filen finns att uppdatera denna delning Uppdatera delning misslyckades Kan inte skapa lokal fil @@ -757,7 +757,7 @@ Mottagen data innehåller ingen giltig fil. %1$s har inte tillåtelse att läsa en mottagen fil Kunde inte kopiera filen till en temporär mapp. Prova att skicka den igen. - Filen som valdes för uppladdning kan inte hittas. Vänligen dubbelkolla att den existerar. + Filen vald för uppladdning hittades inte. Vänligen kontrollera om filen finns. Filen kan inte laddas upp Ingen fil att ladda upp Mappnamn From b0563b931015ee8f0a12207989149a95d5e991b1 Mon Sep 17 00:00:00 2001 From: Daniel Bailey Date: Mon, 4 Nov 2019 14:47:40 +0000 Subject: [PATCH 02/69] Theming: Fix date picker button text colour Signed-off-by: Daniel Bailey --- .../ui/dialog/ExpirationDatePickerDialogFragment.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java index c5800ffd52ea..fb9d941f90a5 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ExpirationDatePickerDialogFragment.java @@ -142,9 +142,9 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { }); dialog.show(); - dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL).setTextColor(ThemeUtils.primaryColor(getContext())); - dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE).setTextColor(ThemeUtils.primaryColor(getContext())); - dialog.getButton(DatePickerDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryColor(getContext())); + dialog.getButton(DatePickerDialog.BUTTON_NEUTRAL).setTextColor(ThemeUtils.primaryColor(getContext(), true)); + dialog.getButton(DatePickerDialog.BUTTON_NEGATIVE).setTextColor(ThemeUtils.primaryColor(getContext(), true)); + dialog.getButton(DatePickerDialog.BUTTON_POSITIVE).setTextColor(ThemeUtils.primaryColor(getContext(), true)); // Prevent days in the past may be chosen DatePicker picker = dialog.getDatePicker(); From a3ae32129d759d2011d6f9815c9ee30f452aa893 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2019 02:19:42 +0000 Subject: [PATCH 03/69] Bump gradle from 3.5.1 to 3.5.2 Bumps gradle from 3.5.1 to 3.5.2. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index a8a862e339ef..799c4e5f2e9e 100644 --- a/build.gradle +++ b/build.gradle @@ -20,7 +20,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.5.1' + classpath 'com.android.tools.build:gradle:3.5.2' classpath('com.dicedmelon.gradle:jacoco-android:0.1.4') { exclude group: 'org.codehaus.groovy', module: 'groovy-all' } From 7a8aee3e22abb56217918284b8335f94c5918353 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 5 Nov 2019 03:07:49 +0000 Subject: [PATCH 04/69] [tx-robot] updated from transifex --- src/main/res/values-zh-rCN/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index af76761b07c0..213c25099318 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -538,6 +538,7 @@ 在你的设备上尝试%1$s! 我想邀请你在你的设备上使用%1$s。\n这里下载:%2$s %1$s或%2$s + 查找文件失败! 删除失败 移除通知失败 移除 From 6b01382e7f934b8416e95bbc93d0717ae6c1f9f7 Mon Sep 17 00:00:00 2001 From: Chris Narkiewicz Date: Mon, 4 Nov 2019 15:55:34 +0000 Subject: [PATCH 05/69] Replace BaseActivity.sharedPreferences with app preferences - Eliminate access to shared preferences when setting dark theme. - Extend AppPreferences interface to allow subscription for preference changes - Improve preferences interface documentation Fixes #4791 Signed-off-by: Chris Narkiewicz --- .../client/preferences/AppPreferences.java | 46 ++++++ .../preferences/AppPreferencesImpl.java | 80 ++++++++- .../android/ui/activity/BaseActivity.java | 29 ++-- .../android/ui/activity/SettingsActivity.java | 12 +- .../ui/activity/ThemedPreferenceActivity.java | 34 ++-- src/main/res/xml/preferences.xml | 3 +- .../preferences/TestAppPreferences.java | 156 ++++++++++++++++++ .../preferences/TestPreferenceManager.java | 58 ------- 8 files changed, 319 insertions(+), 99 deletions(-) create mode 100644 src/test/java/com/nextcloud/client/preferences/TestAppPreferences.java delete mode 100644 src/test/java/com/nextcloud/client/preferences/TestPreferenceManager.java diff --git a/src/main/java/com/nextcloud/client/preferences/AppPreferences.java b/src/main/java/com/nextcloud/client/preferences/AppPreferences.java index aad701982c7a..5c6d9107a6ef 100644 --- a/src/main/java/com/nextcloud/client/preferences/AppPreferences.java +++ b/src/main/java/com/nextcloud/client/preferences/AppPreferences.java @@ -23,8 +23,42 @@ import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.utils.FileSortOrder; +import androidx.annotation.Nullable; + +/** + * This interface provides single point of entry for access to all application + * preferences and allows clients to subscribe for specific configuration + * changes. + */ public interface AppPreferences { + /** + * Preferences listener. Callbacks should be invoked on main thread. + * + * Miantainers should extend this interface with callbacks for specific + * events. + */ + interface Listener { + default void onDarkThemeEnabledChanged(boolean enabled) { + /* default empty implementation */ + }; + } + + /** + * Registers preferences listener. It no-ops if listener + * is already registered. + * + * @param listener application preferences listener + */ + void addListener(@Nullable Listener listener); + + /** + * Unregister listener. It no-ops if listener is not registered. + * + * @param listener application preferences listener + */ + void removeListener(@Nullable Listener listener); + void setKeysReInitEnabled(); boolean isKeysReInitEnabled(); @@ -239,6 +273,18 @@ public interface AppPreferences { */ int getUploaderBehaviour(); + /** + * Enable dark theme. + * + * This is reactive property. Listeners will be invoked if registered. + * + * @param enabled true to turn dark theme on, false to turn it off + */ + void setDarkThemeEnabled(boolean enabled); + + /** + * @return true if application uses dark UI theme, false otherwise + */ boolean isDarkThemeEnabled(); /** diff --git a/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index a78ee9aa2f6c..1ac83f887386 100644 --- a/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -35,12 +35,21 @@ import com.owncloud.android.ui.activity.SettingsActivity; import com.owncloud.android.utils.FileSortOrder; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import androidx.annotation.Nullable; + import static com.owncloud.android.ui.fragment.OCFileListFragment.FOLDER_LAYOUT_LIST; /** - * Helper to simplify reading of Preferences all around the app + * Implementation of application-wide preferences using {@link SharedPreferences}. + * + * Users should not use this class directly. Please use {@link AppPreferences} interafce + * instead. */ public final class AppPreferencesImpl implements AppPreferences { + /** * Constant to access value of last path selected by the user to upload a file shared from other app. * Value handled by the app without direct access in the UI. @@ -48,6 +57,7 @@ public final class AppPreferencesImpl implements AppPreferences { public static final String AUTO_PREF__LAST_SEEN_VERSION_CODE = "lastSeenVersionCode"; public static final String STORAGE_PATH = "storage_path"; public static final float DEFAULT_GRID_COLUMN = 4.0f; + private static final String AUTO_PREF__LAST_UPLOAD_PATH = "last_upload_path"; private static final String AUTO_PREF__UPLOAD_FROM_LOCAL_LAST_PATH = "upload_from_local_last_path"; private static final String AUTO_PREF__UPLOAD_FILE_EXTENSION_MAP_URL = "prefs_upload_file_extension_map_url"; @@ -68,7 +78,7 @@ public final class AppPreferencesImpl implements AppPreferences { private static final String PREF__AUTO_UPLOAD_INIT = "autoUploadInit"; private static final String PREF__FOLDER_SORT_ORDER = "folder_sort_order"; private static final String PREF__FOLDER_LAYOUT = "folder_layout"; - public static final String PREF__THEME = "darkTheme"; + static final String PREF__DARK_THEME_ENABLED = "dark_theme_enabled"; private static final String PREF__LOCK_TIMESTAMP = "lock_timestamp"; private static final String PREF__SHOW_MEDIA_SCAN_NOTIFICATIONS = "show_media_scan_notifications"; @@ -81,7 +91,54 @@ public final class AppPreferencesImpl implements AppPreferences { private final Context context; private final SharedPreferences preferences; private final CurrentAccountProvider currentAccountProvider; + private final ListenerRegistry listeners; + + /** + * Adapter delegating raw {@link SharedPreferences.OnSharedPreferenceChangeListener} calls + * with key-value pairs to respective {@link com.nextcloud.client.preferences.AppPreferences.Listener} method. + */ + static class ListenerRegistry implements SharedPreferences.OnSharedPreferenceChangeListener { + private final AppPreferences preferences; + private final Set listeners; + + ListenerRegistry(AppPreferences preferences) { + this.preferences = preferences; + this.listeners = new CopyOnWriteArraySet<>(); + } + + void add(@Nullable final Listener listener) { + if (listener != null) { + listeners.add(listener); + } + } + + void remove(@Nullable final Listener listener) { + if (listener != null) { + listeners.remove(listener); + } + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { + if(PREF__DARK_THEME_ENABLED.equals(key)) { + boolean enabled = preferences.isDarkThemeEnabled(); + for(Listener l : listeners) { + l.onDarkThemeEnabledChanged(enabled); + } + } + } + } + /** + * This is a temporary workaround to access app preferences in places that cannot use + * dependency injection yet. Use injected component via {@link AppPreferences} interface. + * + * WARNING: this creates new instance! it does not return app-wide singleton + * + * @param context Context used to create shared preferences + * @return New instance of app preferences component + */ + @Deprecated public static AppPreferences fromContext(Context context) { final CurrentAccountProvider currentAccountProvider = UserAccountManagerImpl.fromContext(context); final SharedPreferences prefs = android.preference.PreferenceManager.getDefaultSharedPreferences(context); @@ -92,6 +149,18 @@ public static AppPreferences fromContext(Context context) { this.context = appContext; this.preferences = preferences; this.currentAccountProvider = currentAccountProvider; + this.listeners = new ListenerRegistry(this); + this.preferences.registerOnSharedPreferenceChangeListener(listeners); + } + + @Override + public void addListener(@Nullable AppPreferences.Listener listener) { + this.listeners.add(listener); + } + + @Override + public void removeListener(@Nullable AppPreferences.Listener listener) { + this.listeners.remove(listener); } @Override @@ -342,9 +411,14 @@ public int getUploaderBehaviour() { return preferences.getInt(AUTO_PREF__UPLOADER_BEHAVIOR, 1); } + @Override + public void setDarkThemeEnabled(boolean enabled) { + preferences.edit().putBoolean(PREF__DARK_THEME_ENABLED, enabled).apply(); + } + @Override public boolean isDarkThemeEnabled() { - return preferences.getBoolean(PREF__THEME, false); + return preferences.getBoolean(PREF__DARK_THEME_ENABLED, false); } @Override diff --git a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java index f10bc8a29051..e65b10316091 100644 --- a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java @@ -12,6 +12,7 @@ import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -28,9 +29,7 @@ /** * Base activity with common behaviour for activities dealing with ownCloud {@link Account}s . */ -public abstract class BaseActivity - extends AppCompatActivity - implements Injectable, SharedPreferences.OnSharedPreferenceChangeListener { +public abstract class BaseActivity extends AppCompatActivity implements Injectable { private static final String TAG = BaseActivity.class.getSimpleName(); @@ -56,7 +55,14 @@ public abstract class BaseActivity private boolean paused; @Inject UserAccountManager accountManager; - @Inject SharedPreferences sharedPreferences; + @Inject AppPreferences preferences; + + private AppPreferences.Listener onPreferencesChanged = new AppPreferences.Listener() { + @Override + public void onDarkThemeEnabledChanged(boolean enabled) { + BaseActivity.this.onThemeSettingsChanged(); + } + }; public UserAccountManager getUserAccountManager() { return accountManager; @@ -65,13 +71,13 @@ public UserAccountManager getUserAccountManager() { @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - sharedPreferences.registerOnSharedPreferenceChangeListener(this); + preferences.addListener(onPreferencesChanged); } @Override protected void onDestroy() { super.onDestroy(); - sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + preferences.removeListener(onPreferencesChanged); } @Override @@ -122,17 +128,12 @@ protected void onRestart() { Log_OC.v(TAG, "onRestart() end"); } - @Override - public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) { - if (!AppPreferencesImpl.PREF__THEME.equals(key)) { - return; - } - + private void onThemeSettingsChanged() { if(paused) { themeChangePending = true; - return; + } else { + recreate(); } - recreate(); } /** diff --git a/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index ae8474d5eb39..e51d028e5fb8 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -692,13 +692,13 @@ private void setupGeneralCategory(int accentColor) { loadStoragePath(); - SwitchPreference themePref = (SwitchPreference) findPreference(AppPreferencesImpl.PREF__THEME); - - themePref.setSummary(preferences.isDarkThemeEnabled() ? - getString(R.string.prefs_value_theme_dark) : getString(R.string.prefs_value_theme_light)); + SwitchPreference themePref = (SwitchPreference) findPreference("dark_theme_enabled"); + boolean darkThemeEnabled = preferences.isDarkThemeEnabled(); + int summaryResId = darkThemeEnabled ? R.string.prefs_value_theme_dark : R.string.prefs_value_theme_light; + themePref.setSummary(summaryResId); themePref.setOnPreferenceChangeListener((preference, newValue) -> { - MainApp.setAppTheme((Boolean) newValue); - + boolean enabled = (Boolean)newValue; + MainApp.setAppTheme(enabled); return true; }); } diff --git a/src/main/java/com/owncloud/android/ui/activity/ThemedPreferenceActivity.java b/src/main/java/com/owncloud/android/ui/activity/ThemedPreferenceActivity.java index 1acc839a4f25..058a1bed8a4d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ThemedPreferenceActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ThemedPreferenceActivity.java @@ -20,17 +20,16 @@ package com.owncloud.android.ui.activity; -import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceActivity; +import com.nextcloud.client.preferences.AppPreferences; + import javax.inject.Inject; import androidx.annotation.Nullable; -public class ThemedPreferenceActivity - extends PreferenceActivity - implements SharedPreferences.OnSharedPreferenceChangeListener { +public class ThemedPreferenceActivity extends PreferenceActivity { /** * Tracks whether the activity should be recreate()'d after a theme change @@ -38,18 +37,29 @@ public class ThemedPreferenceActivity private boolean themeChangePending; private boolean paused; - @Inject SharedPreferences sharedPreferences; + @Inject AppPreferences preferences; + + private AppPreferences.Listener onThemeChangedListener = new AppPreferences.Listener() { + @Override + public void onDarkThemeEnabledChanged(boolean enabled) { + if(paused) { + themeChangePending = true; + return; + } + recreate(); + } + }; @Override protected void onPostCreate(@Nullable Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); - sharedPreferences.registerOnSharedPreferenceChangeListener(this); + preferences.addListener(onThemeChangedListener); } @Override protected void onDestroy() { super.onDestroy(); - sharedPreferences.unregisterOnSharedPreferenceChangeListener(this); + preferences.removeListener(onThemeChangedListener); } @Override @@ -67,14 +77,4 @@ protected void onResume() { recreate(); } } - - @Override - public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) { - if(paused) { - themeChangePending = true; - return; - } - - recreate(); - } } diff --git a/src/main/res/xml/preferences.xml b/src/main/res/xml/preferences.xml index 4dae1b4f4be8..67e479c50c71 100644 --- a/src/main/res/xml/preferences.xml +++ b/src/main/res/xml/preferences.xml @@ -27,8 +27,9 @@ android:title="@string/prefs_storage_path" android:key="storage_path"/> diff --git a/src/test/java/com/nextcloud/client/preferences/TestAppPreferences.java b/src/test/java/com/nextcloud/client/preferences/TestAppPreferences.java new file mode 100644 index 000000000000..9e7fef260255 --- /dev/null +++ b/src/test/java/com/nextcloud/client/preferences/TestAppPreferences.java @@ -0,0 +1,156 @@ +package com.nextcloud.client.preferences; + +import android.content.Context; +import android.content.SharedPreferences; + +import com.nextcloud.client.account.CurrentAccountProvider; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; +import org.mockito.InOrder; +import org.mockito.Mock; +import static org.mockito.Mockito.*; + +import org.mockito.MockitoAnnotations; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + TestAppPreferences.Preferences.class, + TestAppPreferences.ListenerRegistery.class +}) +public class TestAppPreferences { + + public static class ListenerRegistery { + private static final SharedPreferences NOT_USED_NULL = null; + + @Mock + private AppPreferences.Listener listener1; + + @Mock + private AppPreferences.Listener listener2; + + @Mock + private AppPreferences.Listener listener3; + + @Mock + private AppPreferences.Listener listener4; + + @Mock + AppPreferences appPreferences; + + private AppPreferencesImpl.ListenerRegistry registry; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(appPreferences.isDarkThemeEnabled()).thenReturn(true); + registry = new AppPreferencesImpl.ListenerRegistry(appPreferences); + } + + @Test + public void canRemoveListenersFromCallback() { + + // GIVEN + // registery has few listeners + // one listener will try to remove itself and other listener + registry.add(listener1); + registry.add(listener2); + registry.add(listener3); + registry.add(listener4); + + doAnswer((i) -> { + registry.remove(listener2); + registry.remove(listener3); + return null; + }).when(listener2).onDarkThemeEnabledChanged(anyBoolean()); + + // WHEN + // callback is called twice + registry.onSharedPreferenceChanged(NOT_USED_NULL, AppPreferencesImpl.PREF__DARK_THEME_ENABLED); + registry.onSharedPreferenceChanged(NOT_USED_NULL, AppPreferencesImpl.PREF__DARK_THEME_ENABLED); + + // THEN + // no ConcurrentModificationException + // 1st time, all listeners (including removed) are called + // 2nd time removed callbacks are not called + verify(listener1, times(2)).onDarkThemeEnabledChanged(anyBoolean()); + verify(listener2).onDarkThemeEnabledChanged(anyBoolean()); + verify(listener3).onDarkThemeEnabledChanged(anyBoolean()); + verify(listener4, times(2)).onDarkThemeEnabledChanged(anyBoolean()); + } + + @Test + public void nullsAreNotAddedToRegistry() { + // GIVEN + // registry has no listeners + // attempt to add null listener was made + registry.add(null); + + // WHEN + // callback is called + registry.onSharedPreferenceChanged(NOT_USED_NULL, AppPreferencesImpl.PREF__DARK_THEME_ENABLED); + + // THEN + // nothing happens + // null was not added to registry + } + + @Test + public void nullsAreNotRemovedFromRegistry() { + // GIVEN + // registry has no listeners + + // WHEN + // attempt to remove null listener was made + registry.remove(null); + + // THEN + // null is ignored + } + } + + public static class Preferences { + @Mock + private Context testContext; + + @Mock + private SharedPreferences sharedPreferences; + + @Mock + private SharedPreferences.Editor editor; + + @Mock + private CurrentAccountProvider accountProvider; + + private AppPreferencesImpl appPreferences; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(editor.remove(anyString())).thenReturn(editor); + when(sharedPreferences.edit()).thenReturn(editor); + appPreferences = new AppPreferencesImpl(testContext, sharedPreferences, accountProvider); + } + + @Test + public void removeLegacyPreferences() { + appPreferences.removeLegacyPreferences(); + InOrder inOrder = inOrder(editor); + inOrder.verify(editor).remove("instant_uploading"); + inOrder.verify(editor).remove("instant_video_uploading"); + inOrder.verify(editor).remove("instant_upload_path"); + inOrder.verify(editor).remove("instant_upload_path_use_subfolders"); + inOrder.verify(editor).remove("instant_upload_on_wifi"); + inOrder.verify(editor).remove("instant_upload_on_charging"); + inOrder.verify(editor).remove("instant_video_upload_path"); + inOrder.verify(editor).remove("instant_video_upload_path_use_subfolders"); + inOrder.verify(editor).remove("instant_video_upload_on_wifi"); + inOrder.verify(editor).remove("instant_video_uploading"); + inOrder.verify(editor).remove("instant_video_upload_on_charging"); + inOrder.verify(editor).remove("prefs_instant_behaviour"); + inOrder.verify(editor).apply(); + } + } +} diff --git a/src/test/java/com/nextcloud/client/preferences/TestPreferenceManager.java b/src/test/java/com/nextcloud/client/preferences/TestPreferenceManager.java deleted file mode 100644 index 64bdca3e5e4c..000000000000 --- a/src/test/java/com/nextcloud/client/preferences/TestPreferenceManager.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.nextcloud.client.preferences; - -import android.content.Context; -import android.content.SharedPreferences; - -import com.nextcloud.client.account.CurrentAccountProvider; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.InOrder; -import org.mockito.Mock; -import static org.mockito.Mockito.*; -import org.mockito.junit.MockitoJUnitRunner; - -@RunWith(MockitoJUnitRunner.class) -public class TestPreferenceManager { - - @Mock - private Context testContext; - - @Mock - private SharedPreferences sharedPreferences; - - @Mock - private SharedPreferences.Editor editor; - - @Mock - private CurrentAccountProvider accountProvider; - - private AppPreferencesImpl appPreferences; - - @Before - public void setUp() { - when(editor.remove(anyString())).thenReturn(editor); - when(sharedPreferences.edit()).thenReturn(editor); - appPreferences = new AppPreferencesImpl(testContext, sharedPreferences, accountProvider); - } - - @Test - public void removeLegacyPreferences() { - appPreferences.removeLegacyPreferences(); - InOrder inOrder = inOrder(editor); - inOrder.verify(editor).remove("instant_uploading"); - inOrder.verify(editor).remove("instant_video_uploading"); - inOrder.verify(editor).remove("instant_upload_path"); - inOrder.verify(editor).remove("instant_upload_path_use_subfolders"); - inOrder.verify(editor).remove("instant_upload_on_wifi"); - inOrder.verify(editor).remove("instant_upload_on_charging"); - inOrder.verify(editor).remove("instant_video_upload_path"); - inOrder.verify(editor).remove("instant_video_upload_path_use_subfolders"); - inOrder.verify(editor).remove("instant_video_upload_on_wifi"); - inOrder.verify(editor).remove("instant_video_uploading"); - inOrder.verify(editor).remove("instant_video_upload_on_charging"); - inOrder.verify(editor).remove("prefs_instant_behaviour"); - inOrder.verify(editor).apply(); - } -} From 25aaf4b907c7a1b1448653a96c7a5d3cb3be19c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kilian=20Pe=CC=81risset?= Date: Tue, 5 Nov 2019 14:20:35 +0100 Subject: [PATCH 06/69] Applied ability to set as wallpaper from properties of a picture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kilian Périsset --- .../com/owncloud/android/ui/fragment/FileDetailFragment.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index c9391fb2fed4..c33b437d315c 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -468,6 +468,10 @@ private boolean optionsItemSelected(MenuItem item) { containerActivity.getFileOperationsHelper().syncFile(getFile()); return true; } + case R.id.action_set_as_wallpaper: { + containerActivity.getFileOperationsHelper().setPictureAs(getFile(), getView()); + return true; + } case R.id.action_encrypted: { // TODO implement or remove return true; From 7f4db3fdd0708fad386069cc068ebd211aa89d27 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Sun, 3 Nov 2019 00:48:23 +0100 Subject: [PATCH 07/69] Make synced folder init/enable date persistent Signed-off-by: Alice Gaudon --- .../android/datamodel/SyncedFolder.java | 50 ++++++++---- .../datamodel/SyncedFolderDisplayItem.java | 11 ++- .../datamodel/SyncedFolderProvider.java | 12 ++- .../com/owncloud/android/db/ProviderMeta.java | 3 +- .../android/jobs/AccountRemovalJob.java | 7 +- .../providers/FileContentProvider.java | 30 ++++++++ .../ui/activity/SyncedFoldersActivity.java | 34 +++----- .../ui/adapter/SyncedFolderAdapter.java | 9 ++- .../android/utils/FilesSyncHelper.java | 77 ++++++------------- .../activity/SyncedFoldersActivityTest.java | 1 + 10 files changed, 127 insertions(+), 107 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 24aaa1f3f785..91b42b813469 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -30,23 +30,23 @@ /** * Synced folder entity containing all information per synced folder. */ -@Getter -@Setter @AllArgsConstructor public class SyncedFolder implements Serializable, Cloneable { public static final long UNPERSISTED_ID = Long.MIN_VALUE; + public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1; private static final long serialVersionUID = -793476118299906429L; - private long id = UNPERSISTED_ID; - private String localPath; - private String remotePath; - private Boolean wifiOnly; - private Boolean chargingOnly; - private Boolean subfolderByDate; - private String account; - private Integer uploadAction; - private boolean enabled; - private MediaFolderType type; + @Getter @Setter private long id; + @Getter @Setter private String localPath; + @Getter @Setter private String remotePath; + @Getter @Setter private Boolean wifiOnly; + @Getter @Setter private Boolean chargingOnly; + @Getter @Setter private Boolean subfolderByDate; + @Getter @Setter private String account; + @Getter @Setter private Integer uploadAction; + @Getter private boolean enabled; + @Getter private long enabledTimestampMs; + @Getter @Setter private MediaFolderType type; /** * constructor for new, to be persisted entity. @@ -59,11 +59,25 @@ public class SyncedFolder implements Serializable, Cloneable { * @param account the account owning the synced folder * @param uploadAction the action to be done after the upload * @param enabled flag if synced folder config is active + * @param timestampMs the current timestamp in milliseconds * @param type the type of the folder */ public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - MediaFolderType type) { + long timestampMs, MediaFolderType type) { + this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, + enabled, timestampMs, type); + } + + /** + * constructor for wrapping existing folders. + * + * @param id id + */ + protected SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, + Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, + long timestampMs, MediaFolderType type) { + this.id = id; this.localPath = localPath; this.remotePath = remotePath; this.wifiOnly = wifiOnly; @@ -71,10 +85,18 @@ public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boole this.subfolderByDate = subfolderByDate; this.account = account; this.uploadAction = uploadAction; - this.enabled = enabled; + this.setEnabled(enabled, timestampMs); this.type = type; } + /** + * @param timestampMs the current timestamp in milliseconds + */ + public void setEnabled(boolean enabled, long timestampMs) { + this.enabled = enabled; + this.enabledTimestampMs = enabled ? timestampMs : EMPTY_ENABLED_TIMESTAMP_MS; + } + public Object clone() { try { return super.clone(); diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index cac3df7a143b..7420b1889295 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -56,9 +56,11 @@ public class SyncedFolderDisplayItem extends SyncedFolder { */ public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - List filePaths, String folderName, long numberOfFiles, MediaFolderType type) + long timestampMs, List filePaths, String folderName, long numberOfFiles, + MediaFolderType type) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, type); + super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, + timestampMs, type); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; @@ -66,8 +68,9 @@ public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boo public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - String folderName, MediaFolderType type) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, type); + long timestampMs, String folderName, MediaFolderType type) { + super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, + timestampMs, type); this.folderName = folderName; } } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 07712a9e3493..b7dd75d967f6 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -27,6 +27,7 @@ import android.database.Cursor; import android.net.Uri; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.owncloud.android.db.ProviderMeta; @@ -37,6 +38,8 @@ import java.util.List; import java.util.Observable; +import javax.inject.Inject; + import androidx.annotation.NonNull; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -50,6 +53,8 @@ public class SyncedFolderProvider extends Observable { private ContentResolver mContentResolver; private AppPreferences preferences; + @Inject protected Clock clock; + /** * constructor. * @@ -162,7 +167,7 @@ public int updateSyncedFolderEnabled(long id, Boolean enabled) { // read sync folder object and update SyncedFolder syncedFolder = createSyncedFolderFromCursor(cursor); - syncedFolder.setEnabled(enabled); + syncedFolder.setEnabled(enabled, clock.getCurrentTime()); // update sync folder object in db result = updateSyncFolder(syncedFolder); @@ -347,11 +352,13 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION)); Boolean enabled = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1; + long enabledTimestampMs = cursor.getLong(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS)); MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE))); syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, - accountName, uploadAction, enabled, type); + accountName, uploadAction, enabled, enabledTimestampMs, type); } return syncedFolder; } @@ -370,6 +377,7 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.getWifiOnly()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.getChargingOnly()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction()); diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 093d36b9f2d8..8bbe982b15d2 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -31,7 +31,7 @@ */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 49; + public static final int DB_VERSION = 50; private ProviderMeta() { // No instance @@ -220,6 +220,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String SYNCED_FOLDER_WIFI_ONLY = "wifi_only"; public static final String SYNCED_FOLDER_CHARGING_ONLY = "charging_only"; public static final String SYNCED_FOLDER_ENABLED = "enabled"; + public static final String SYNCED_FOLDER_ENABLED_TIMESTAMP_MS = "enabled_timestamp_ms"; public static final String SYNCED_FOLDER_TYPE = "type"; public static final String SYNCED_FOLDER_SUBFOLDER_BY_DATE = "subfolder_by_date"; public static final String SYNCED_FOLDER_ACCOUNT = "account"; diff --git a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java index b87d3e99de5b..9065d9a0b673 100644 --- a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java +++ b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java @@ -57,7 +57,6 @@ import com.owncloud.android.ui.events.AccountRemovedEvent; import com.owncloud.android.utils.EncryptionUtils; import com.owncloud.android.utils.FileStorageUtils; -import com.owncloud.android.utils.FilesSyncHelper; import com.owncloud.android.utils.PushUtils; import org.greenrobot.eventbus.EventBus; @@ -129,7 +128,7 @@ protected Result onRunJob(@NotNull Params params) { arbitraryDataProvider.deleteKeyForAccount(account.name, PENDING_FOR_REMOVAL); // remove synced folders set for account - remoceSyncedFolders(context, account, arbitraryDataProvider); + remoceSyncedFolders(context, account); // delete all uploads for account uploadsStorageManager.removeAccountUploads(account); @@ -174,7 +173,7 @@ private void unregisterPushNotifications(Context context, Account account, Arbit } } - private void remoceSyncedFolders(Context context, Account account, ArbitraryDataProvider arbitraryDataProvider) { + private void remoceSyncedFolders(Context context, Account account) { SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(context.getContentResolver(), AppPreferencesImpl.fromContext(context)); List syncedFolders = syncedFolderProvider.getSyncedFolders(); @@ -183,8 +182,6 @@ private void remoceSyncedFolders(Context context, Account account, ArbitraryData for (SyncedFolder syncedFolder : syncedFolders) { if (syncedFolder.getAccount().equals(account.name)) { - arbitraryDataProvider.deleteKeyForAccount(FilesSyncHelper.GLOBAL, - FilesSyncHelper.SYNCEDFOLDERINITIATED + syncedFolder.getId()); syncedFolderIds.add(syncedFolder.getId()); } } diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index bcfe381e53ec..b82789ce98cd 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -42,9 +42,11 @@ import android.os.Binder; import android.text.TextUtils; +import com.nextcloud.client.core.Clock; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.SyncedFolder; import com.owncloud.android.db.ProviderMeta; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -58,6 +60,8 @@ import java.util.HashMap; import java.util.Locale; +import javax.inject.Inject; + import androidx.annotation.NonNull; /** @@ -91,6 +95,7 @@ public class FileContentProvider extends ContentProvider { public static final int ARBITRARY_DATA_TABLE_INTRODUCTION_VERSION = 20; public static final int MINIMUM_PATH_SEGMENTS_SIZE = 1; + @Inject protected Clock clock; private DataBaseHelper mDbHelper; private Context mContext; private UriMatcher mUriMatcher; @@ -822,6 +827,7 @@ private void createSyncedFoldersTable(SQLiteDatabase db) { + ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY + " INTEGER, " // wifi_only + ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY + " INTEGER, " // charging only + ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, " // enabled + + ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS + " INTEGER, " // enable date + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " TEXT, " // account + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER, " // upload action @@ -2013,6 +2019,30 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } + + if (oldVersion < 50 && newVersion >= 50) { + Log_OC.i(SQL, "Entering in the #50 add persistent enable date to synced_folders table"); + db.beginTransaction(); + try { + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS + " INTEGER "); + + db.execSQL("UPDATE " + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + " SET " + + ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS + " = CASE " + + " WHEN enabled = 0 THEN " + SyncedFolder.EMPTY_ENABLED_TIMESTAMP_MS + " " + + " ELSE " + clock.getCurrentTime() + + " END "); + + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } } @Override diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 78ae76286888..07ec55dc45fc 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -41,13 +41,13 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; -import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaProvider; @@ -113,6 +113,7 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA private int type; @Inject AppPreferences preferences; @Inject PowerManagementService powerManagementService; + @Inject Clock clock; @Override protected void onCreate(Bundle savedInstanceState) { @@ -223,7 +224,7 @@ private void setupContent() { final int gridWidth = getResources().getInteger(R.integer.media_grid_width); boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light); - mAdapter = new SyncedFolderAdapter(this, gridWidth, this, lightVersion); + mAdapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion); mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences); final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); @@ -386,6 +387,7 @@ private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull Sy syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), + clock.getCurrentTime(), filePaths, localFolder.getName(), files.length, @@ -411,6 +413,7 @@ private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedF syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), + clock.getCurrentTime(), mediaFolder.filePaths, mediaFolder.folderName, mediaFolder.numberOfFiles, @@ -432,9 +435,10 @@ private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull Media true, false, false, - getAccount().name, + getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, + clock.getCurrentTime(), mediaFolder.filePaths, mediaFolder.folderName, mediaFolder.numberOfFiles, @@ -519,7 +523,7 @@ public boolean onOptionsItemSelected(MenuItem item) { SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( SyncedFolder.UNPERSISTED_ID, null, null, true, false, false, getAccount().name, - FileUploader.LOCAL_BEHAVIOUR_FORGET, false, null, MediaFolderType.CUSTOM); + FileUploader.LOCAL_BEHAVIOUR_FORGET, false, clock.getCurrentTime(), null, MediaFolderType.CUSTOM); onSyncFolderSettingsClick(0, emptyCustomFolder); } @@ -548,9 +552,6 @@ public void showFiles(boolean onDeviceOnly) { @Override public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem) { - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). - getContentResolver()); - if (syncedFolderDisplayItem.getId() > UNPERSISTED_ID) { mSyncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(), syncedFolderDisplayItem.isEnabled()); @@ -565,9 +566,6 @@ public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedF FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem); showBatteryOptimizationInfo(); - } else { - String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + syncedFolderDisplayItem.getId(); - arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } @@ -600,9 +598,6 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). - getContentResolver()); - // custom folders newly created aren't in the list already, // so triggering a refresh if (MediaFolderType.CUSTOM == syncedFolder.getType() && syncedFolder.getId() == UNPERSISTED_ID) { @@ -610,15 +605,12 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(), - new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType()); + clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType()); long storedId = mSyncedFolderProvider.storeSyncedFolder(newCustomFolder); if (storedId != -1) { newCustomFolder.setId(storedId); if (newCustomFolder.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(newCustomFolder); - } else { - String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + newCustomFolder.getId(); - arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } mAdapter.addSyncFolderItem(newCustomFolder); @@ -635,9 +627,6 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { item.setId(storedId); if (item.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); - } else { - String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); - arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } } else { @@ -645,9 +634,6 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { mSyncedFolderProvider.updateSyncFolder(item); if (item.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); - } else { - String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); - arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); } } @@ -699,7 +685,7 @@ private SyncedFolderDisplayItem updateSyncedFolderItem(SyncedFolderDisplayItem i item.setChargingOnly(chargingOnly); item.setSubfolderByDate(subfolderByDate); item.setUploadAction(uploadAction); - item.setEnabled(enabled); + item.setEnabled(enabled, clock.getCurrentTime()); return item; } diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index 8a8176e54ef4..7588b323da7f 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -33,6 +33,7 @@ import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter; import com.afollestad.sectionedrecyclerview.SectionedViewHolder; +import com.nextcloud.client.core.Clock; import com.owncloud.android.R; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolderDisplayItem; @@ -54,14 +55,16 @@ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter { private final Context mContext; + private final Clock clock; private final int mGridWidth; private final int mGridTotal; private final ClickListener mListener; private final List mSyncFolderItems; private final boolean mLight; - public SyncedFolderAdapter(Context context, int gridWidth, ClickListener listener, boolean light) { + public SyncedFolderAdapter(Context context, Clock clock, int gridWidth, ClickListener listener, boolean light) { mContext = context; + this.clock = clock; mGridWidth = gridWidth; mGridTotal = gridWidth * 2; mListener = listener; @@ -148,7 +151,7 @@ public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int s holder.syncStatusButton.setVisibility(View.VISIBLE); holder.syncStatusButton.setTag(section); holder.syncStatusButton.setOnClickListener(v -> { - mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled()); + mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled(), clock.getCurrentTime()); setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); }); @@ -157,7 +160,7 @@ public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int s holder.syncStatusButton.setVisibility(View.VISIBLE); holder.syncStatusButton.setTag(section); holder.syncStatusButton.setOnClickListener(v -> { - mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled()); + mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled(), clock.getCurrentTime()); setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); }); diff --git a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 9116a3d57067..351abe46b298 100644 --- a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -30,7 +30,6 @@ import android.net.Uri; import android.os.Build; import android.provider.MediaStore; -import android.text.TextUtils; import android.util.Log; import com.evernote.android.job.JobManager; @@ -41,7 +40,6 @@ import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; -import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FilesystemDataProvider; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.SyncedFolder; @@ -73,7 +71,6 @@ public final class FilesSyncHelper { public static final String TAG = "FileSyncHelper"; public static final String GLOBAL = "global"; - public static final String SYNCEDFOLDERINITIATED = "syncedFolderIntitiated_"; public static final int ContentSyncJobId = 315; @@ -84,59 +81,34 @@ private FilesSyncHelper() { public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver); - Long currentTime = System.currentTimeMillis(); - double currentTimeInSeconds = currentTime / 1000.0; - String currentTimeString = Long.toString((long) currentTimeInSeconds); + final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); - String syncedFolderInitiatedKey = SYNCEDFOLDERINITIATED + syncedFolder.getId(); - boolean dryRun = TextUtils.isEmpty(arbitraryDataProvider.getValue - (GLOBAL, syncedFolderInitiatedKey)); - - if (MediaFolderType.IMAGE == syncedFolder.getType()) { - if (dryRun) { - arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, syncedFolderInitiatedKey, - currentTimeString); - } else { - FilesSyncHelper.insertContentIntoDB(android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI - , syncedFolder); + if (syncedFolder.isEnabled() && enabledTimestampMs >= 0) { + MediaFolderType mediaType = syncedFolder.getType(); + if (mediaType == MediaFolderType.IMAGE) { + FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI + , syncedFolder); FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, - syncedFolder); - } - - } else if (MediaFolderType.VIDEO == syncedFolder.getType()) { - - if (dryRun) { - arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, syncedFolderInitiatedKey, - currentTimeString); - } else { - FilesSyncHelper.insertContentIntoDB(android.provider.MediaStore.Video.Media.INTERNAL_CONTENT_URI, - syncedFolder); + syncedFolder); + } else if (mediaType == MediaFolderType.VIDEO) { + FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.INTERNAL_CONTENT_URI, + syncedFolder); FilesSyncHelper.insertContentIntoDB(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, - syncedFolder); - } - - } else { - try { - if (dryRun) { - arbitraryDataProvider.storeOrUpdateKeyValue(GLOBAL, syncedFolderInitiatedKey, - currentTimeString); - } else { + syncedFolder); + } else { + try { FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); Path path = Paths.get(syncedFolder.getLocalPath()); - String dateInitiated = arbitraryDataProvider.getValue(GLOBAL, - syncedFolderInitiatedKey); - Files.walkFileTree(path, new SimpleFileVisitor() { @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { - File file = path.toFile(); - if (attrs.lastModifiedTime().toMillis() >= Long.parseLong(dateInitiated) * 1000) { + if (attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) { filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), - attrs.lastModifiedTime().toMillis(), file.isDirectory(), syncedFolder); + attrs.lastModifiedTime().toMillis(), + file.isDirectory(), syncedFolder); } return FileVisitResult.CONTINUE; @@ -147,11 +119,9 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) { return FileVisitResult.CONTINUE; } }); - + } catch (IOException e) { + Log.e(TAG, "Something went wrong while indexing files for auto upload " + e.getLocalizedMessage()); } - - } catch (IOException e) { - Log.e(TAG, "Something went wrong while indexing files for auto upload " + e.getLocalizedMessage()); } } } @@ -172,7 +142,6 @@ public static void insertAllDBEntries(AppPreferences preferences, boolean skipCu private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver); Cursor cursor; int column_index_data; @@ -191,11 +160,10 @@ private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) { } path = path + "%"; - String syncedFolderInitiatedKey = SYNCEDFOLDERINITIATED + syncedFolder.getId(); - String dateInitiated = arbitraryDataProvider.getValue(GLOBAL, syncedFolderInitiatedKey); + long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); cursor = context.getContentResolver().query(uri, projection, MediaStore.MediaColumns.DATA + " LIKE ?", - new String[]{path}, null); + new String[]{path}, null); if (cursor != null) { column_index_data = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATA); @@ -203,9 +171,10 @@ private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) { while (cursor.moveToNext()) { contentPath = cursor.getString(column_index_data); isFolder = new File(contentPath).isDirectory(); - if (cursor.getLong(column_index_date_modified) >= Long.parseLong(dateInitiated)) { + if (cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) { filesystemDataProvider.storeOrUpdateFileValue(contentPath, - cursor.getLong(column_index_date_modified), isFolder, syncedFolder); + cursor.getLong(column_index_date_modified), isFolder, + syncedFolder); } } cursor.close(); diff --git a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index b29b06c6bc95..2861b3e3c058 100644 --- a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -177,6 +177,7 @@ private SyncedFolderDisplayItem create(String folderName, Boolean enabled) { "test@nextcloud.com", 1, enabled, + System.currentTimeMillis(), new ArrayList(), folderName, 2, From c2ea4de8020eb11506922b56b4b7c79e5a348b31 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 6 Nov 2019 01:44:41 +0100 Subject: [PATCH 08/69] FileContentProvider: add dagger injection Signed-off-by: Alice Gaudon --- src/main/java/com/nextcloud/client/di/ComponentsModule.java | 2 ++ .../com/owncloud/android/providers/FileContentProvider.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/com/nextcloud/client/di/ComponentsModule.java b/src/main/java/com/nextcloud/client/di/ComponentsModule.java index a8eadc36411a..558ef0afa9a5 100644 --- a/src/main/java/com/nextcloud/client/di/ComponentsModule.java +++ b/src/main/java/com/nextcloud/client/di/ComponentsModule.java @@ -32,6 +32,7 @@ import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.jobs.NotificationJob; import com.owncloud.android.providers.DiskLruImageCacheFileProvider; +import com.owncloud.android.providers.FileContentProvider; import com.owncloud.android.providers.UsersAndGroupsSearchProvider; import com.owncloud.android.services.AccountManagerService; import com.owncloud.android.services.OperationsService; @@ -148,6 +149,7 @@ abstract class ComponentsModule { @ContributesAndroidInjector abstract BootupBroadcastReceiver bootupBroadcastReceiver(); @ContributesAndroidInjector abstract NotificationJob.NotificationReceiver notificationJobBroadcastReceiver(); + @ContributesAndroidInjector abstract FileContentProvider fileContentProvider(); @ContributesAndroidInjector abstract UsersAndGroupsSearchProvider usersAndGroupsSearchProvider(); @ContributesAndroidInjector abstract DiskLruImageCacheFileProvider diskLruImageCacheFileProvider(); diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index b82789ce98cd..78824925e414 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -63,6 +63,7 @@ import javax.inject.Inject; import androidx.annotation.NonNull; +import dagger.android.AndroidInjection; /** * The ContentProvider for the ownCloud App. @@ -419,6 +420,7 @@ private void updateFilesTableAccordingToShareInsertion(SQLiteDatabase db, Conten @Override public boolean onCreate() { + AndroidInjection.inject(this); mDbHelper = new DataBaseHelper(getContext()); mContext = getContext(); From 2e13212752c3a94b8c06195f179d81db35f75c00 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 6 Nov 2019 01:50:40 +0100 Subject: [PATCH 09/69] SyncedFolderProvider: pass Clock dependency via constructor instead of dagger Signed-off-by: Alice Gaudon --- .../client/jobs/BackgroundJobFactory.kt | 9 +++-- .../java/com/owncloud/android/MainApp.java | 37 +++++++++++-------- .../datamodel/SyncedFolderProvider.java | 12 +++--- .../files/BootupBroadcastReceiver.java | 5 ++- .../android/jobs/AccountRemovalJob.java | 16 +++++--- .../owncloud/android/jobs/FilesSyncJob.java | 21 ++++++----- .../jobs/MediaFoldersDetectionJob.java | 12 ++++-- .../owncloud/android/jobs/NCJobCreator.java | 13 +++++-- .../ui/activity/SyncedFoldersActivity.java | 2 +- .../android/utils/FilesSyncHelper.java | 6 +-- .../client/jobs/BackgroundJobFactoryTest.kt | 5 +++ 11 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt b/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt index 3ddd9971144b..38a6a57c818d 100644 --- a/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt +++ b/src/main/java/com/nextcloud/client/jobs/BackgroundJobFactory.kt @@ -26,6 +26,7 @@ import androidx.annotation.RequiresApi import androidx.work.ListenableWorker import androidx.work.WorkerFactory import androidx.work.WorkerParameters +import com.nextcloud.client.core.Clock import com.nextcloud.client.device.DeviceInfo import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.preferences.AppPreferences @@ -40,6 +41,7 @@ import javax.inject.Provider class BackgroundJobFactory @Inject constructor( private val preferences: AppPreferences, private val contentResolver: ContentResolver, + private val clock: Clock, private val powerManagerService: PowerManagementService, private val backgroundJobManager: Provider, private val deviceInfo: DeviceInfo @@ -58,13 +60,14 @@ class BackgroundJobFactory @Inject constructor( } return when (workerClass) { - ContentObserverWork::class -> createContentObserverJob(context, workerParameters) + ContentObserverWork::class -> createContentObserverJob(context, workerParameters, clock) else -> null // falls back to default factory } } - private fun createContentObserverJob(context: Context, workerParameters: WorkerParameters): ListenableWorker? { - val folderResolver = SyncedFolderProvider(contentResolver, preferences) + private fun createContentObserverJob(context: Context, workerParameters: WorkerParameters, clock: Clock): + ListenableWorker? { + val folderResolver = SyncedFolderProvider(contentResolver, preferences, clock) @RequiresApi(Build.VERSION_CODES.N) if (deviceInfo.apiLevel >= Build.VERSION_CODES.N) { return ContentObserverWork( diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 05c90f3cae86..3b7f088efed5 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -45,6 +45,7 @@ import com.evernote.android.job.JobRequest; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.appinfo.AppInfo; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.di.ActivityInjector; import com.nextcloud.client.di.DaggerAppComponent; @@ -161,6 +162,9 @@ public class MainApp extends MultiDexApplication implements HasAndroidInjector { @Inject BackgroundJobManager backgroundJobManager; + @Inject + Clock clock; + private PassCodeManager passCodeManager; @SuppressWarnings("unused") @@ -268,7 +272,8 @@ public void onCreate() { preferences, uploadsStorageManager, connectivityService, - powerManagementService + powerManagementService, + clock ) ); @@ -304,7 +309,8 @@ public void onCreate() { accountManager, connectivityService, powerManagementService, - backgroundJobManager); + backgroundJobManager, + clock); initContactsBackup(accountManager); notificationChannels(); @@ -462,23 +468,24 @@ public static void initSyncOperations( final UserAccountManager accountManager, final ConnectivityService connectivityService, final PowerManagementService powerManagementService, - final BackgroundJobManager jobManager + final BackgroundJobManager jobManager, + final Clock clock ) { updateToAutoUpload(); - cleanOldEntries(); - updateAutoUploadEntries(); + cleanOldEntries(clock); + updateAutoUploadEntries(clock); if (getAppContext() != null) { if (PermissionUtil.checkSelfPermission(getAppContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE)) { - splitOutAutoUploadEntries(); + splitOutAutoUploadEntries(clock); } else { AppPreferences preferences = AppPreferencesImpl.fromContext(getAppContext()); preferences.setAutoUploadSplitEntriesEnabled(true); } } - initiateExistingAutoUploadEntries(); + initiateExistingAutoUploadEntries(clock); FilesSyncHelper.scheduleFilesSyncIfNeeded(mContext, jobManager); FilesSyncHelper.restartJobsIfNeeded( @@ -685,18 +692,18 @@ private static void updateToAutoUpload() { } } - private static void updateAutoUploadEntries() { + private static void updateAutoUploadEntries(Clock clock) { // updates entries to reflect their true paths Context context = getAppContext(); AppPreferences preferences = AppPreferencesImpl.fromContext(context); if (!preferences.isAutoUploadPathsUpdateEnabled()) { SyncedFolderProvider syncedFolderProvider = - new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences); + new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences, clock); syncedFolderProvider.updateAutoUploadPaths(mContext); } } - private static void splitOutAutoUploadEntries() { + private static void splitOutAutoUploadEntries(Clock clock) { Context context = getAppContext(); AppPreferences preferences = AppPreferencesImpl.fromContext(context); if (!preferences.isAutoUploadSplitEntriesEnabled()) { @@ -705,7 +712,7 @@ private static void splitOutAutoUploadEntries() { Log_OC.i(TAG, "Migrate synced_folders records for image/video split"); ContentResolver contentResolver = context.getContentResolver(); - SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences); + SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock); final List imageMediaFolders = MediaProvider.getImageFolders(contentResolver, 1, null, true); final List videoMediaFolders = MediaProvider.getVideoFolders(contentResolver, 1, null, true); @@ -751,12 +758,12 @@ private static void splitOutAutoUploadEntries() { } } - private static void initiateExistingAutoUploadEntries() { + private static void initiateExistingAutoUploadEntries(Clock clock) { new Thread(() -> { AppPreferences preferences = AppPreferencesImpl.fromContext(getAppContext()); if (!preferences.isAutoUploadInitialized()) { SyncedFolderProvider syncedFolderProvider = - new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences); + new SyncedFolderProvider(MainApp.getAppContext().getContentResolver(), preferences, clock); for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { if (syncedFolder.isEnabled()) { @@ -770,7 +777,7 @@ private static void initiateExistingAutoUploadEntries() { }).start(); } - private static void cleanOldEntries() { + private static void cleanOldEntries(Clock clock) { // previous versions of application created broken entries in the SyncedFolderProvider // database, and this cleans all that and leaves 1 (newest) entry per synced folder @@ -779,7 +786,7 @@ private static void cleanOldEntries() { if (!preferences.isLegacyClean()) { SyncedFolderProvider syncedFolderProvider = - new SyncedFolderProvider(context.getContentResolver(), preferences); + new SyncedFolderProvider(context.getContentResolver(), preferences, clock); List syncedFolderList = syncedFolderProvider.getSyncedFolders(); Map, Long> syncedFolders = new HashMap<>(); diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index b7dd75d967f6..8a66a4058a1b 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -38,8 +38,6 @@ import java.util.List; import java.util.Observable; -import javax.inject.Inject; - import androidx.annotation.NonNull; import static com.owncloud.android.datamodel.OCFile.PATH_SEPARATOR; @@ -50,22 +48,22 @@ public class SyncedFolderProvider extends Observable { static private final String TAG = SyncedFolderProvider.class.getSimpleName(); - private ContentResolver mContentResolver; - private AppPreferences preferences; - - @Inject protected Clock clock; + private final ContentResolver mContentResolver; + private final AppPreferences preferences; + private final Clock clock; /** * constructor. * * @param contentResolver the ContentResolver to work with. */ - public SyncedFolderProvider(ContentResolver contentResolver, AppPreferences preferences) { + public SyncedFolderProvider(ContentResolver contentResolver, AppPreferences preferences, Clock clock) { if (contentResolver == null) { throw new IllegalArgumentException("Cannot create an instance with a NULL contentResolver"); } mContentResolver = contentResolver; this.preferences = preferences; + this.clock = clock; } /** diff --git a/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java b/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java index 77f1535f0725..311d45240a72 100644 --- a/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java +++ b/src/main/java/com/owncloud/android/files/BootupBroadcastReceiver.java @@ -28,6 +28,7 @@ import android.content.Intent; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.network.ConnectivityService; @@ -53,6 +54,7 @@ public class BootupBroadcastReceiver extends BroadcastReceiver { @Inject ConnectivityService connectivityService; @Inject PowerManagementService powerManagementService; @Inject BackgroundJobManager backgroundJobManager; + @Inject Clock clock; /** * Receives broadcast intent reporting that the system was just boot up. @@ -69,7 +71,8 @@ public void onReceive(Context context, Intent intent) { accountManager, connectivityService, powerManagementService, - backgroundJobManager); + backgroundJobManager, + clock); MainApp.initContactsBackup(accountManager); } else { Log_OC.d(TAG, "Getting wrong intent: " + intent.getAction()); diff --git a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java index 9065d9a0b673..a63793d12908 100644 --- a/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java +++ b/src/main/java/com/owncloud/android/jobs/AccountRemovalJob.java @@ -38,6 +38,7 @@ import com.evernote.android.job.util.support.PersistableBundleCompat; import com.google.gson.Gson; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -80,12 +81,14 @@ public class AccountRemovalJob extends Job implements AccountManagerCallback syncedFolders = syncedFolderProvider.getSyncedFolders(); List syncedFolderIds = new ArrayList<>(); diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 0a254580a00d..5c7a9771aac1 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -34,6 +34,7 @@ import com.evernote.android.job.Job; import com.evernote.android.job.util.support.PersistableBundleCompat; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; @@ -76,22 +77,25 @@ public class FilesSyncJob extends Job { public static final String OVERRIDE_POWER_SAVING = "overridePowerSaving"; private static final String WAKELOCK_TAG_SEPARATION = ":"; - private UserAccountManager userAccountManager; - private AppPreferences preferences; - private UploadsStorageManager uploadsStorageManager; - private ConnectivityService connectivityService; - private PowerManagementService powerManagementService; + private final UserAccountManager userAccountManager; + private final AppPreferences preferences; + private final UploadsStorageManager uploadsStorageManager; + private final ConnectivityService connectivityService; + private final PowerManagementService powerManagementService; + private final Clock clock; FilesSyncJob(final UserAccountManager userAccountManager, final AppPreferences preferences, final UploadsStorageManager uploadsStorageManager, final ConnectivityService connectivityService, - final PowerManagementService powerManagementService) { + final PowerManagementService powerManagementService, + final Clock clock) { this.userAccountManager = userAccountManager; this.preferences = preferences; this.uploadsStorageManager = uploadsStorageManager; this.connectivityService = connectivityService; this.powerManagementService = powerManagementService; + this.clock = clock; } @NonNull @@ -126,13 +130,12 @@ protected Result onRunJob(@NonNull Params params) { userAccountManager, connectivityService, powerManagementService); - FilesSyncHelper.insertAllDBEntries(preferences, skipCustom); + FilesSyncHelper.insertAllDBEntries(preferences, clock, skipCustom); // Create all the providers we'll need final ContentResolver contentResolver = context.getContentResolver(); final FilesystemDataProvider filesystemDataProvider = new FilesystemDataProvider(contentResolver); - SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, - preferences); + SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock); Locale currentLocale = context.getResources().getConfiguration().locale; SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale); diff --git a/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java b/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java index 43e073608898..e421b9365a7d 100644 --- a/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java +++ b/src/main/java/com/owncloud/android/jobs/MediaFoldersDetectionJob.java @@ -39,6 +39,7 @@ import com.evernote.android.job.Job; import com.google.gson.Gson; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.owncloud.android.R; @@ -74,11 +75,13 @@ public class MediaFoldersDetectionJob extends Job { private static final String DISABLE_DETECTION_CLICK = "DISABLE_DETECTION_CLICK"; - private UserAccountManager userAccountManager; - private Random randomId = new Random(); + private final UserAccountManager userAccountManager; + private final Clock clock; + private final Random randomId = new Random(); - MediaFoldersDetectionJob(UserAccountManager accountManager) { + MediaFoldersDetectionJob(UserAccountManager accountManager, Clock clock) { this.userAccountManager = accountManager; + this.clock = clock; } @NonNull @@ -88,7 +91,8 @@ protected Result onRunJob(@NonNull Params params) { ContentResolver contentResolver = context.getContentResolver(); ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(contentResolver); SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, - AppPreferencesImpl.fromContext(context)); + AppPreferencesImpl.fromContext(context), + clock); Gson gson = new Gson(); String arbitraryDataString; MediaFoldersModel mediaFoldersModel; diff --git a/src/main/java/com/owncloud/android/jobs/NCJobCreator.java b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java index c0894a59e14b..010ae266897a 100644 --- a/src/main/java/com/owncloud/android/jobs/NCJobCreator.java +++ b/src/main/java/com/owncloud/android/jobs/NCJobCreator.java @@ -29,6 +29,7 @@ import com.evernote.android.job.Job; import com.evernote.android.job.JobCreator; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; @@ -48,6 +49,7 @@ public class NCJobCreator implements JobCreator { private final UploadsStorageManager uploadsStorageManager; private final ConnectivityService connectivityService; private final PowerManagementService powerManagementService; + private final Clock clock; public NCJobCreator( Context context, @@ -55,7 +57,8 @@ public NCJobCreator( AppPreferences preferences, UploadsStorageManager uploadsStorageManager, ConnectivityService connectivityServices, - PowerManagementService powerManagementService + PowerManagementService powerManagementService, + Clock clock ) { this.context = context; this.accountManager = accountManager; @@ -63,6 +66,7 @@ public NCJobCreator( this.uploadsStorageManager = uploadsStorageManager; this.connectivityService = connectivityServices; this.powerManagementService = powerManagementService; + this.clock = clock; } @Override @@ -73,19 +77,20 @@ public Job create(@NonNull String tag) { case ContactsImportJob.TAG: return new ContactsImportJob(); case AccountRemovalJob.TAG: - return new AccountRemovalJob(uploadsStorageManager, accountManager); + return new AccountRemovalJob(uploadsStorageManager, accountManager, clock); case FilesSyncJob.TAG: return new FilesSyncJob(accountManager, preferences, uploadsStorageManager, connectivityService, - powerManagementService); + powerManagementService, + clock); case OfflineSyncJob.TAG: return new OfflineSyncJob(accountManager, connectivityService, powerManagementService); case NotificationJob.TAG: return new NotificationJob(context, accountManager); case MediaFoldersDetectionJob.TAG: - return new MediaFoldersDetectionJob(accountManager); + return new MediaFoldersDetectionJob(accountManager, clock); default: return null; } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 07ec55dc45fc..16724bf6646b 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -225,7 +225,7 @@ private void setupContent() { final int gridWidth = getResources().getInteger(R.integer.media_grid_width); boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light); mAdapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion); - mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences); + mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock); final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); mAdapter.setLayoutManager(lm); diff --git a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 351abe46b298..a729eebf9a36 100644 --- a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -35,6 +35,7 @@ import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.jobs.BackgroundJobManager; import com.nextcloud.client.network.ConnectivityService; @@ -126,11 +127,10 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) { } } - public static void insertAllDBEntries(AppPreferences preferences, boolean skipCustom) { + public static void insertAllDBEntries(AppPreferences preferences, Clock clock, boolean skipCustom) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); - SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, - preferences); + SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock); for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { if (syncedFolder.isEnabled() && (MediaFolderType.CUSTOM != syncedFolder.getType() || !skipCustom)) { diff --git a/src/test/java/com/nextcloud/client/jobs/BackgroundJobFactoryTest.kt b/src/test/java/com/nextcloud/client/jobs/BackgroundJobFactoryTest.kt index 826d5a8621f9..23a5f88eed97 100644 --- a/src/test/java/com/nextcloud/client/jobs/BackgroundJobFactoryTest.kt +++ b/src/test/java/com/nextcloud/client/jobs/BackgroundJobFactoryTest.kt @@ -24,6 +24,7 @@ import android.content.ContentResolver import android.content.Context import android.os.Build import androidx.work.WorkerParameters +import com.nextcloud.client.core.Clock import com.nextcloud.client.device.DeviceInfo import com.nextcloud.client.device.PowerManagementService import com.nextcloud.client.preferences.AppPreferences @@ -59,6 +60,9 @@ class BackgroundJobFactoryTest { @Mock private lateinit var deviceInfo: DeviceInfo + @Mock + private lateinit var clock: Clock + private lateinit var factory: BackgroundJobFactory @Before @@ -67,6 +71,7 @@ class BackgroundJobFactoryTest { factory = BackgroundJobFactory( preferences, contentResolver, + clock, powerManagementService, Provider { backgroundJobManager }, deviceInfo From b59938d96075cf7edb381566e030c845f789c861 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Wed, 6 Nov 2019 03:39:59 +0100 Subject: [PATCH 10/69] daily dev 20191106 9e15f5ea3 Merge pull request #4799 from nextcloud/dependabot/gradle/com.android.tools.build-gradle-3.5.2 d208681fe Merge pull request #4796 from nextcloud/datepicker 7a8aee3e2 [tx-robot] updated from transifex a3ae32129 Bump gradle from 3.5.1 to 3.5.2 b0563b931 Theming: Fix date picker button text colour 207f8947c [tx-robot] updated from transifex 1ec09848b [tx-robot] updated from transifex 513151491 [tx-robot] updated from transifex 38a83bb36 daily dev 20191102 --- build.gradle | 4 ++-- .../metadata/android/en-US/changelogs/20191106.txt | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191106.txt diff --git a/build.gradle b/build.gradle index 799c4e5f2e9e..822da00ca7ee 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191102 - versionName "20191102" + versionCode 20191106 + versionName "20191106" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191106.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191106.txt new file mode 100644 index 000000000000..a5edb780817c --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191106.txt @@ -0,0 +1,9 @@ +9e15f5ea3 Merge pull request #4799 from nextcloud/dependabot/gradle/com.android.tools.build-gradle-3.5.2 +d208681fe Merge pull request #4796 from nextcloud/datepicker +7a8aee3e2 [tx-robot] updated from transifex +a3ae32129 Bump gradle from 3.5.1 to 3.5.2 +b0563b931 Theming: Fix date picker button text colour +207f8947c [tx-robot] updated from transifex +1ec09848b [tx-robot] updated from transifex +513151491 [tx-robot] updated from transifex +38a83bb36 daily dev 20191102 From 039adc0a1856ffd5a0d6956fb89e75e8dfe4e80c Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 6 Nov 2019 03:10:30 +0000 Subject: [PATCH 11/69] [tx-robot] updated from transifex --- src/main/res/values-cs-rCZ/strings.xml | 18 ++++++++++++++++++ src/main/res/values-es/strings.xml | 4 ++++ src/main/res/values-ro/strings.xml | 8 ++++++++ 3 files changed, 30 insertions(+) diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index fedff6986286..05e78b464af2 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -78,6 +78,7 @@ /AutoUpload Vytvořte nové nastavení vlastní složky Vytvořte vlastní složku + Zakázat kontrolu úspory energie Avatar Zavřít Vypnout @@ -167,6 +168,8 @@ Zkopírovat odkaz Kopírování/přesouvání do šifrované složky není v tuto chvíli podporováno. Zkopírovat do… + Nepodařilo se stáhnout celý obrázek + Nepodařilo se získat URL Nepodařilo se vytvořit složku Vytváření souboru ze šablony… Vytvořit nový dokument @@ -246,6 +249,8 @@ Kritická chyba: operace nelze provést Chyba při přidávání komentáře k souboru %1$s zhavarovalo + Report + Nahlásit problém? (vyžaduje Github účet) Chyba při získávání souboru Chyba při získávání šablon Chyba při spouštění kamery @@ -259,7 +264,9 @@ Přidat do oblíbených Oblíbené Není k dispozici aplikace pro posílání emailů! + soubor Smazat + Při načítání aktivit u souboru došlo k chybě Nepodařilo se načíst podrobnosti. Soubor Ponechat @@ -327,6 +334,7 @@ Zabezpečená spolupráce & výměna souborů Snadno použitelný webový emailový klient, kalendáře & kontakty Sdílení obrazovky, online schůzky & webové konference + složka Složka už existuje Vytvořit Ikona složky @@ -367,7 +375,9 @@ Obnovit Prohledat záznamy událostí Posílat záznamy událostí e-mailem + Logy: %1$d kB, dotaz nalezl %2$d / %3$d v %4$d ms Načítání… + Logy: %1$d kB, žádný filtr Záznamy událostí Server je v režimu údržby Vyčistit data @@ -411,6 +421,7 @@ Nové oznámení Byla vytvořena nová verze Není k dispozici aplikace pro práci s odkazy + Povolen je pouze jeden účet Není k dispozice aplikace pro práci s PDF Poslat Poznámku se nepodařilo odeslat @@ -474,6 +485,7 @@ 12:23:45 Toto je zástupný text 2012/05/18 12:23 odp. + Zákaz kontroly úspory energie může způsobit nahrávání souborů i v případě nízkého stavu baterie! smazáno ponechán v původní složce přesunut do složky aplikace @@ -516,6 +528,9 @@ Popis umístění úložiště Místní složka Vzdálená složka + Motiv + Tmavý + Světlý Náhled obrázku K náhledu není žádný místní soubor Obrázek se nedaří zobrazit @@ -527,6 +542,7 @@ Vyzkoušejte %1$s ve svém telefonu! Rád bych vás pozval(a) k používání %1$s ve vašem telefonu.\nKe stažení zde: %2$s %1$s nebo %2$s + Nepodařilo se najít soubor! Odstranění se nezdařilo Oznámení se nepodařilo odebrat. Odebrat @@ -574,6 +590,8 @@ Sdílet %1$s Vytvořit odkaz %1$s (skupina) + Sdílet vnitřní odkaz + Bude fungovat pouze pro uživatele s přístupem do tohoto %1$s %1$s (v %2$s) Je třeba zadat heslo Při pokusu o sdílení tohoto souboru či složky nastala chyba diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 9cd9138b514f..3f5ac4b44872 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -265,6 +265,7 @@ Añadir a favoritos Favorito ¡No hay ninguna app disponible para mandar correos! + archivo Eliminar Error al recuperar las actividades del archivo Fallo al cargar los detalles @@ -334,6 +335,7 @@ Colaboración segura e intercambio de archivos Correo web, calendario y contactos fáciles de usar Compartir pantalla, reuniones online y conferencias web + carpeta La carpeta ya existe Crear Icono de carpeta @@ -589,6 +591,8 @@ Compartir %1$s Obtener enlace %1$s (grupo) + Compartir enlace interno + Sólo funciona para usuarios con acceso a esto %1$s %1$s ( en %2$s ) Debes introducir una contraseña Ha ocurrido un error al tratar de compartir este archivo o carpeta diff --git a/src/main/res/values-ro/strings.xml b/src/main/res/values-ro/strings.xml index 4eb68946e546..308ab89263e8 100644 --- a/src/main/res/values-ro/strings.xml +++ b/src/main/res/values-ro/strings.xml @@ -219,9 +219,17 @@ Introduceti parola pentru decriptare cheie personală. Directorul nu este gol. Se generează chei noi… + Criptarea end-to-end este dezactivată pe server. + Criptarea funcționează începând cu versiunea KitKat(4.4) sau mai mare. + Te rog salvează parola de criptare formată din cele 12 cuvinte Parolă… + Setează criptarea + Nu s-au putut salva cheile de criptare, te rog încearcă din nou. + Eroare la decriptare. Parola greșită? + Te rog sa introduci un nume de fișier %1$s nu a putut fi copiat in dosarul local %2$s Eroare critică: nu se pot executa operațiunile + Raport Înapoi Adăugați la favorite Favorite From 142c79bc03068b480ee476903b6dccc683db3334 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Thu, 7 Nov 2019 03:36:57 +0100 Subject: [PATCH 12/69] daily dev 20191107 890789f0a Merge pull request #4797 from nextcloud/ezaquarii/replace-shared-preferences-with-app-preferences-in-base-activity 180bdee72 Merge pull request #4801 from Infomaniak/feature/resolve-set-as-wallpaper-issue 039adc0a1 [tx-robot] updated from transifex b59938d96 daily dev 20191106 --- build.gradle | 4 ++-- .../fastlane/metadata/android/en-US/changelogs/20191107.txt | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191107.txt diff --git a/build.gradle b/build.gradle index 822da00ca7ee..fe59ba96fbe9 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191106 - versionName "20191106" + versionCode 20191107 + versionName "20191107" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191107.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191107.txt new file mode 100644 index 000000000000..cb4d3dd2244f --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191107.txt @@ -0,0 +1,4 @@ +890789f0a Merge pull request #4797 from nextcloud/ezaquarii/replace-shared-preferences-with-app-preferences-in-base-activity +180bdee72 Merge pull request #4801 from Infomaniak/feature/resolve-set-as-wallpaper-issue +039adc0a1 [tx-robot] updated from transifex +b59938d96 daily dev 20191106 From 403fc875628cfddfb82033bb5120e715bdf961e4 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 7 Nov 2019 03:08:34 +0000 Subject: [PATCH 13/69] [tx-robot] updated from transifex --- src/main/res/values-pl/strings.xml | 6 +++--- src/main/res/values-zh-rCN/strings.xml | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index dd3c6a2e7823..a288cbabfa21 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -612,9 +612,9 @@ Zatrzymaj udostępnianie %1$s (zdalny) %1$s (konwersacja) - Nazwa, ID chmury federalnej lub adres e-mail… + Nazwa, ID chmury federacyjnej lub adres e-mail… Notatka dla odbiorcy - Pozwól na edycję + Zezwalaj na edycję Ustaw datę wygaśnięcia Ukryj pobieranie Schowaj listę plików @@ -725,7 +725,7 @@ Wystąpił błąd podczas anulowania udostępniania tego pliku lub katalogu. Nie można wyłączyć udostępniania. Sprawdź, czy plik istnieje aby anulować udostępnianie tego pliku - Usuwanie udostępnienia nie powiodło się + Zatrzymywanie udostępnienia nie powiodło się Dostęp przez niezaufaną domenę. Więcej informacji można znaleźć w dokumentacji. Wystąpił błąd podczas próby aktualizacji udostępniania Nie można zaktualizować. Sprawdź, czy plik istnieje. diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index 213c25099318..0409f29ece67 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -527,6 +527,9 @@ 存储路径 本地文件夹 远端文件夹 + 主题 + 深色 + 浅色 图片预览 没有可预览的本地文件 无法显示图像 From ff646aa3d090ec5a1f7bc25b14f154a61760888c Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Fri, 8 Nov 2019 03:38:19 +0100 Subject: [PATCH 14/69] daily dev 20191108 403fc8756 [tx-robot] updated from transifex 142c79bc0 daily dev 20191107 --- build.gradle | 4 ++-- .../fastlane/metadata/android/en-US/changelogs/20191108.txt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191108.txt diff --git a/build.gradle b/build.gradle index fe59ba96fbe9..f24056cb32ec 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191107 - versionName "20191107" + versionCode 20191108 + versionName "20191108" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191108.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191108.txt new file mode 100644 index 000000000000..ae871c08acc7 --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191108.txt @@ -0,0 +1,2 @@ +403fc8756 [tx-robot] updated from transifex +142c79bc0 daily dev 20191107 From 4690042b0657a9f825e5e776f848e056d0f07c1b Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Sun, 10 Nov 2019 03:14:29 +0000 Subject: [PATCH 15/69] [tx-robot] updated from transifex --- src/main/res/values-cs-rCZ/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index 05e78b464af2..ac1bfb5ee643 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -86,6 +86,7 @@ Nedaří se spustit nastavení akumulátoru přímo. Upravte ručně v nastavení. Optimalizace pro nižší vybíjení akumulátoru Došlo k problému s načtením certifikátu. + Seznam změn ve vývojářské verzi Zatržítko Zvolte místní složku… Zvolte složku na protějšku… From 86027dd09137df21fa598a1d305bc1e5060836aa Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2019 02:14:20 +0000 Subject: [PATCH 16/69] Bump annotations from 17.0.0 to 18.0.0 Bumps [annotations](https://github.com/JetBrains/java-annotations) from 17.0.0 to 18.0.0. - [Release notes](https://github.com/JetBrains/java-annotations/releases) - [Commits](https://github.com/JetBrains/java-annotations/compare/17.0.0...18.0.0) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f24056cb32ec..7f2d650a71d7 100644 --- a/build.gradle +++ b/build.gradle @@ -301,7 +301,7 @@ dependencies { implementation 'com.caverock:androidsvg:1.4' implementation 'androidx.annotation:annotation:1.1.0' implementation 'com.google.code.gson:gson:2.8.6' - implementation 'org.jetbrains:annotations:17.0.0' + implementation 'org.jetbrains:annotations:18.0.0' implementation 'com.github.cotechde.hwsecurity:hwsecurity-fido:2.5.1' From df6d847f29a04e01922d38c6d85ad8ba3e773ba5 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 12 Nov 2019 03:11:59 +0000 Subject: [PATCH 17/69] [tx-robot] updated from transifex --- src/main/res/values-ca/strings.xml | 8 ++++++++ src/main/res/values-cs-rCZ/strings.xml | 10 +++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index 38cb29811f88..344e3f0058de 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -265,6 +265,7 @@ Afegix a preferits Preferits No hi ha cap aplicació disponible per enviar correus electrònics! + arxiu Suprimeix Hi ha hagut un error mentre es recuperaven les activitats per al fitxer No s\'han pogut carregar els detalls @@ -334,6 +335,7 @@ Intercanvi de fitxers & amb coŀlaboració segura Correu web, agenda i contactes fàcils de fer servir Compartició de pantalles, reunions en línia i conferències web + carpeta La carpeta ja existeix Crea Icona de la carpeta @@ -527,6 +529,9 @@ Ruta d\'emmagatzematge Carpeta local Carpeta remota + Tema + Fosc + Clar Visualització prèvia d\'imatge No hi ha cap fitxer local per fer una vista prèvia No es pot mostrar la imatge @@ -538,6 +543,7 @@ Proveu %1$s al vostre dispositiu! Us vull convidar a fer servir %1$s al vostre dispositiu.\nDescarregueu-lo d\'aquí: %2$s %1$s o %2$s + No s\'ha pogut trobar l\'arxiu! No s\'ha pogut completar l\'eliminació Ha fallat la supressió de la notificació. Suprimeix @@ -585,6 +591,8 @@ Comparteix %1$s Obtén l\'enllaç %1$s (grup) + Compartir l\'enllaç intern + Només funciona amb els usuaris que poden accedir a %1$s %1$s (a %2$s) Heu d\'escriure una contrasenya S\'ha produït un error mentre s\'intentava compartir aquest fitxer o carpeta diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index ac1bfb5ee643..090d2f40022b 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -264,7 +264,7 @@ Vrátit se ke starému způsobu přihlašování Přidat do oblíbených Oblíbené - Není k dispozici aplikace pro posílání emailů! + Není k dispozici aplikace pro posílání e-mailů! soubor Smazat Při načítání aktivit u souboru došlo k chybě @@ -333,7 +333,7 @@ Toto je funkce Nextcloud, přejděte na něj. Mějte svá data zabezpečená a pod svou správou Zabezpečená spolupráce & výměna souborů - Snadno použitelný webový emailový klient, kalendáře & kontakty + Snadno použitelný webový e-mailový klient, kalendáře & kontakty Sdílení obrazovky, online schůzky & webové konference složka Složka už existuje @@ -370,7 +370,7 @@ Soubor nenalezen na souborovém systému Nejsou zde další složky. Záznamy událostí v aplikaci %1$s pro Android - Nebyla nalezena aplikace k odesílání záznamů událostí. Nainstalujte emailového klienta. + Nebyla nalezena aplikace k odesílání záznamů událostí. Nainstalujte e-mailového klienta. Přihlásit Smazat záznamy událostí Obnovit @@ -612,7 +612,7 @@ Zrušit sdílení %1$s (vzdálený) %1$s (konverzace) - Jméno, sdružené cloud ID, nebo emailová adresa… + Jméno, sdružené cloud ID, nebo e-mailová adresa… Poznámka pro příjemce Povolit úpravy Nastavit datum skončení platnosti @@ -799,7 +799,7 @@ Čeká se na Wi-Fi Uživatel Adresa - Email + E-mail Telefonní číslo Twitter Webové stránky From fb615ef2796ee5a2e538d3c6ccd479dc6fae8db2 Mon Sep 17 00:00:00 2001 From: Chris Narkiewicz Date: Mon, 21 Oct 2019 15:27:43 +0100 Subject: [PATCH 18/69] Remove out-of-band setAccount/onAccountSet calls Activity account is set in FilesActivity.onAccount(), but related components are updated in BaseActivity.onStart(). 1. Initialize account in BaseActivity.onCreate() 2. Update storage manager and capabilities immediately on account set, not waiting for onStart() lifecycle. 3. inline storage manager and capabilities update in setAccount() 4. Remove setAccount()/onAccountSet() calls that sets default account, as this duplicates BaseActivity.onCreate() Signed-off-by: Chris Narkiewicz --- .../client/onboarding/FirstRunActivity.java | 6 ++- .../android/ui/activity/BaseActivity.java | 44 +++++-------------- .../android/ui/activity/DrawerActivity.java | 2 +- .../ui/activity/FileDisplayActivity.java | 3 +- .../ui/activity/ManageAccountsActivity.java | 5 +-- .../ui/activity/NotificationsActivity.java | 2 +- .../ReceiveExternalFilesActivity.java | 4 +- .../ui/activity/SyncedFoldersActivity.java | 2 +- .../android/ui/activity/UserInfoActivity.java | 3 -- 9 files changed, 22 insertions(+), 49 deletions(-) diff --git a/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java b/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java index eb978cda1bc1..8790115ebe73 100644 --- a/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java +++ b/src/main/java/com/nextcloud/client/onboarding/FirstRunActivity.java @@ -217,17 +217,19 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { return; } - setAccount(account); userAccountManager.setCurrentOwnCloudAccount(account.name); - onAccountSet(); Intent i = new Intent(this, FileDisplayActivity.class); i.setAction(FileDisplayActivity.RESTART); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(i); + + finish(); } } + + public static FeatureItem[] getFirstRun() { return new FeatureItem[]{ new FeatureItem(R.drawable.logo, R.string.first_run_1_text, R.string.empty, true, false), diff --git a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java index e65b10316091..92cda745b2cd 100644 --- a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java @@ -9,6 +9,7 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; +import android.os.PersistableBundle; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; @@ -96,9 +97,10 @@ protected void onResume() { } } - @Override - protected void onPostResume() { - super.onPostResume(); + public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { + super.onCreate(savedInstanceState, persistentState); + Account account = accountManager.getCurrentAccount(); + setAccount(account, false); } @Override @@ -152,6 +154,11 @@ protected void setAccount(Account account, boolean savedAccount) { } else { swapToDefaultAccount(); } + + if(currentAccount != null) { + storageManager = new FileDataStorageManager(currentAccount, getContentResolver()); + capabilities = storageManager.getCapability(currentAccount.name); + } } /** @@ -188,26 +195,6 @@ protected void createAccount(boolean mandatoryCreation) { new Handler()); } - /** - * Called when the ownCloud {@link Account} associated to the Activity was just updated. - * - * Child classes must grant that state depending on the {@link Account} is updated. - */ - @Deprecated - protected void onAccountSet() { - if (getAccount() != null) { - storageManager = new FileDataStorageManager(getAccount(), getContentResolver()); - capabilities = storageManager.getCapability(currentAccount.name); - } else { - Log_OC.e(TAG, "onAccountChanged was called with NULL account associated!"); - } - } - - @Deprecated - protected void setAccount(Account account) { - currentAccount = account; - } - /** * Getter for the capabilities of the server where the current OC account lives. * @@ -228,16 +215,7 @@ public OCCapability getCapabilities() { public Account getAccount() { return currentAccount; } - - @Override - protected void onStart() { - super.onStart(); - - if(currentAccount != null) { - onAccountSet(); - } - } - + public FileDataStorageManager getStorageManager() { return storageManager; } diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 9670feb8c55a..18e368137ae6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -1293,7 +1293,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // current account has changed if (data.getBooleanExtra(ManageAccountsActivity.KEY_CURRENT_ACCOUNT_CHANGED, false)) { - setAccount(accountManager.getCurrentAccount()); + setAccount(accountManager.getCurrentAccount(), false); updateAccountList(); restart(); } else { diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index fe872a7b84d0..ec5fb347298f 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -2621,7 +2621,8 @@ private void handleOpenFileViaIntent(Intent intent) { return; } - setAccount(newAccount); + setAccount(newAccount, false); + updateAccountList(); } String fileId = String.valueOf(intent.getStringExtra(KEY_FILE_ID)); diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 7656482a636b..4108c000735e 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -126,15 +126,12 @@ protected void onCreate(Bundle savedInstanceState) { Account[] accountList = AccountManager.get(this).getAccountsByType(MainApp.getAccountType(this)); originalAccounts = DisplayUtils.toAccountNameSet(Arrays.asList(accountList)); - Account currentAccount = getUserAccountManager().getCurrentAccount(); + Account currentAccount = getAccount(); if (currentAccount != null) { originalCurrentAccount = currentAccount.name; } - setAccount(currentAccount); - onAccountSet(); - arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver()); multipleAccountsSupported = getResources().getBoolean(R.bool.multiaccount_support); diff --git a/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java b/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java index 1fbe351e3e62..79ada93f41c2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java @@ -127,7 +127,7 @@ protected void onCreate(Bundle savedInstanceState) { if (account != null && (currentAccount == null || !account.equalsIgnoreCase(currentAccount.name))) { accountManager.setCurrentOwnCloudAccount(account); - setAccount(getUserAccountManager().getCurrentAccount()); + setAccount(getUserAccountManager().getCurrentAccount(), false); currentAccount = getAccount(); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index fc89c91582e3..90f5f8090fcd 100755 --- a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -208,8 +208,6 @@ protected void setAccount(Account account, boolean savedAccount) { Log_OC.i(TAG, "No ownCloud account is available"); DialogNoAccount dialog = new DialogNoAccount(); dialog.show(getSupportFragmentManager(), null); - } else if (!savedAccount) { - setAccount(accounts[0]); } if (!somethingToUpload()) { @@ -680,7 +678,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { // there is no need for checking for is there more then one // account at this point // since account setup can set only one account at time - setAccount(accounts[0]); + setAccount(accounts[0], false); populateDirectoryList(); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 78ae76286888..a71264e372be 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -132,7 +132,7 @@ protected void onCreate(Bundle savedInstanceState) { if (account != null && currentAccount != null && !account.equalsIgnoreCase(currentAccount.name)) { accountManager.setCurrentOwnCloudAccount(account); - setAccount(getUserAccountManager().getCurrentAccount()); + setAccount(getUserAccountManager().getCurrentAccount(), false); } path = getIntent().getStringExtra(MediaFoldersDetectionJob.KEY_MEDIA_FOLDER_PATH); diff --git a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index 61523ddb9378..60b5ce39a70b 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -135,9 +135,6 @@ public void onCreate(Bundle savedInstanceState) { setContentView(R.layout.user_info_layout); unbinder = ButterKnife.bind(this); - setAccount(getUserAccountManager().getCurrentAccount()); - onAccountSet(); - boolean useBackgroundImage = URLUtil.isValidUrl( getStorageManager().getCapability(account.name).getServerBackground()); From d30b574ff6a4bff2ea921a4a556b4f30a8db2b65 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 12 Nov 2019 10:53:05 +0100 Subject: [PATCH 19/69] Add changelog for 3.9.0 Signed-off-by: tobiasKaminsky --- CHANGELOG.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e0cd7fbbac0..a40d7cf05e7e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,16 @@ +3.9.0 (November, 12, 2019) + +- preview Markdown with syntax highlighting @AndyScherzinger +- improved DavX5 integration @bitfireAT +- AutoUpload: allow files to upload into subfolder +- new media player service @ezaquarii +- Remote wipe integration +- Print from within Collabora +- enhanced SingleSignOn +- outdated server warning set to NC14 + +For a full list, please see https://github.com/nextcloud/android/milestone/38 + ## 3.8.1 (October, 11, 2019) - upload images into subfolder, if source folder also has subfolder From e43ef8e0625e3fa0a8a857f08644167646544bd1 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Wed, 13 Nov 2019 03:37:07 +0100 Subject: [PATCH 20/69] daily dev 20191113 f245b72de Merge pull request #4823 from nextcloud/changelog-master-stable-3.9.0 d30b574ff Add changelog for 3.9.0 df6d847f2 [tx-robot] updated from transifex 4343c3d67 Merge pull request #4814 from nextcloud/dependabot/gradle/org.jetbrains-annotations-18.0.0 86027dd09 Bump annotations from 17.0.0 to 18.0.0 4690042b0 [tx-robot] updated from transifex ff646aa3d daily dev 20191108 --- build.gradle | 4 ++-- .../metadata/android/en-US/changelogs/20191113.txt | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191113.txt diff --git a/build.gradle b/build.gradle index 7f2d650a71d7..f5feed893642 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191108 - versionName "20191108" + versionCode 20191113 + versionName "20191113" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191113.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191113.txt new file mode 100644 index 000000000000..27d66f662495 --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191113.txt @@ -0,0 +1,7 @@ +f245b72de Merge pull request #4823 from nextcloud/changelog-master-stable-3.9.0 +d30b574ff Add changelog for 3.9.0 +df6d847f2 [tx-robot] updated from transifex +4343c3d67 Merge pull request #4814 from nextcloud/dependabot/gradle/org.jetbrains-annotations-18.0.0 +86027dd09 Bump annotations from 17.0.0 to 18.0.0 +4690042b0 [tx-robot] updated from transifex +ff646aa3d daily dev 20191108 From f38900d989cd173679cdcb82d529302d28f60aec Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 13 Nov 2019 03:10:02 +0000 Subject: [PATCH 21/69] [tx-robot] updated from transifex --- src/main/res/values-nl/strings.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index b73ba5d8884c..4b3f0250bdb2 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -265,6 +265,7 @@ Toevoegen aan favorieten Favoriet Geen app beschikbaar voor versturen mail! + bestand Verwijderen Fout bij ophalen activiteiten van bestand Kon details niet laden @@ -334,6 +335,7 @@ Beveiligde samenwerking & bestand uitwisseling Eenvoudige webmail, agenda & contacten Schermdelen, online afspraken & web conferenties + map Map bestaat al Aanmaken Map pictogram @@ -589,6 +591,8 @@ Deel %1$s Link ophalen %1$s (groep) + Deel interne link + Dit werkt enkel voor gebruikers met toegang tot %1$s %1$s ( op %2$s ) Je moet een wachtwoord opgeven Er trad een fout op bij je poging dit bestand of deze map te delen From 693f408276d575e6994187c1efd716721e2c42fb Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sun, 3 Nov 2019 15:16:39 +0100 Subject: [PATCH 22/69] Improve menu file name pattern Signed-off-by: Andy Scherzinger --- CONTRIBUTING.md | 20 ++++++++ .../client/errorhandling/ShowErrorActivity.kt | 2 +- .../etm/pages/EtmPreferencesFragment.kt | 2 +- .../client/logger/ui/LogsActivity.kt | 2 +- .../ui/activity/FileDisplayActivity.java | 2 +- .../ui/activity/FolderPickerActivity.java | 6 +-- .../ui/activity/NotificationsActivity.java | 2 +- .../ReceiveExternalFilesActivity.java | 2 +- .../ui/activity/SyncedFoldersActivity.java | 2 +- .../ui/activity/UploadFilesActivity.java | 2 +- .../ui/activity/UploadListActivity.java | 2 +- .../android/ui/activity/UserInfoActivity.java | 2 +- .../android/ui/adapter/UserListAdapter.java | 2 +- .../ui/fragment/FileDetailFragment.java | 2 +- .../fragment/FileDetailSharingFragment.java | 2 +- .../ui/fragment/OCFileListFragment.java | 4 +- .../contactsbackup/ContactListFragment.java | 2 +- .../ui/preview/PreviewImageFragment.java | 2 +- .../ui/preview/PreviewMediaFragment.java | 2 +- .../ui/preview/PreviewTextFragment.java | 2 +- .../android/ui/trashbin/TrashbinActivity.java | 4 +- src/main/res/layout/drawer.xml | 2 +- ...ain_menu.xml => activity_file_display.xml} | 0 src/main/res/menu/activity_folder_picker.xml | 46 +++++++++++++++++++ .../menu/{logs_menu.xml => activity_logs.xml} | 0 ...ns_menu.xml => activity_notifications.xml} | 3 +- ...ml => activity_receive_external_files.xml} | 0 ...error_show.xml => activity_show_error.xml} | 0 ...s_menu.xml => activity_synced_folders.xml} | 0 ...options_menu.xml => activity_trashbin.xml} | 0 ...s_picker.xml => activity_upload_files.xml} | 0 ...list_menu.xml => activity_upload_list.xml} | 0 ...r_info_menu.xml => activity_user_info.xml} | 0 ...ist_menu.xml => fragment_contact_list.xml} | 0 ...ences.xml => fragment_etm_preferences.xml} | 0 ...ions_menu.xml => fragment_file_detail.xml} | 0 ... => fragment_file_detail_sharing_link.xml} | 0 .../{file_actions_menu.xml => item_file.xml} | 0 ...bin_actions_menu.xml => item_trashbin.xml} | 0 ...enu.xml => item_user_sharing_settings.xml} | 0 ...er_menu.xml => partial_drawer_entries.xml} | 0 41 files changed, 91 insertions(+), 28 deletions(-) rename src/main/res/menu/{main_menu.xml => activity_file_display.xml} (100%) create mode 100644 src/main/res/menu/activity_folder_picker.xml rename src/main/res/menu/{logs_menu.xml => activity_logs.xml} (100%) rename src/main/res/menu/{notifications_actions_menu.xml => activity_notifications.xml} (96%) rename src/main/res/menu/{receive_file_menu.xml => activity_receive_external_files.xml} (100%) rename src/main/res/menu/{activity_error_show.xml => activity_show_error.xml} (100%) rename src/main/res/menu/{synced_folders_menu.xml => activity_synced_folders.xml} (100%) rename src/main/res/menu/{trashbin_options_menu.xml => activity_trashbin.xml} (100%) rename src/main/res/menu/{upload_files_picker.xml => activity_upload_files.xml} (100%) rename src/main/res/menu/{upload_list_menu.xml => activity_upload_list.xml} (100%) rename src/main/res/menu/{user_info_menu.xml => activity_user_info.xml} (100%) rename src/main/res/menu/{contactlist_menu.xml => fragment_contact_list.xml} (100%) rename src/main/res/menu/{etm_preferences.xml => fragment_etm_preferences.xml} (100%) rename src/main/res/menu/{file_details_actions_menu.xml => fragment_file_detail.xml} (100%) rename src/main/res/menu/{file_detail_sharing_link_menu.xml => fragment_file_detail_sharing_link.xml} (100%) rename src/main/res/menu/{file_actions_menu.xml => item_file.xml} (100%) rename src/main/res/menu/{trashbin_actions_menu.xml => item_trashbin.xml} (100%) rename src/main/res/menu/{file_detail_sharing_menu.xml => item_user_sharing_settings.xml} (100%) rename src/main/res/menu/{drawer_menu.xml => partial_drawer_entries.xml} (100%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a57f5b423d30..45c48f796137 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -23,6 +23,8 @@ 1. Backport pull request 1. Pull requests that also need changes on library 1. Adding new files + 1. File naming + 1. Menu files 1. Translations 1. Releases 1. Types @@ -236,6 +238,24 @@ Source code of app: --> ``` +## File naming + +The file naming patterns are inspired and based on [Ribot's Android Project And Code Guidelines](https://github.com/ribot/android-guidelines/blob/c1d8c9c904eb31bf01fe24aadb963b74281fe79a/project_and_code_guidelines.md). + +### Menu files + +Similar to layout files, menu files should match the name of the component. For example, if we are defining a menu file that is going to be used in the `UserProfileActivity`, then the name of the file should be `activity_user_profile.xml`. Same pattern applies for menus used in adapter view items, dialogs, etc. + +| Component | Class Name | Menu Name | +| ---------------- | ---------------------- | ----------------------------- | +| Activity | `UserProfileActivity` | `activity_user_profile.xml` | +| Fragment | `SignUpFragment` | `fragment_sign_up.xml` | +| Dialog | `ChangePasswordDialog` | `dialog_change_password.xml` | +| AdapterView item | --- | `item_person.xml` | +| Partial layout | --- | `partial_stats_bar.xml` | + +A good practice is to not include the word `menu` as part of the name because these files are already located in the `menu` directory. In case a component uses several menus in different places (via popup menus) then the resource name would be extended. For example, if the user profile activity has two popup menus for configuring the users settings and one for the handling group assignments then the file names for the menus would be: `activity_user_profile_user_settings.xml` and `activity_user_profile_group_assignments.xml`. + ## Translations We manage translations via [Transifex](https://www.transifex.com/nextcloud/nextcloud/android/). So just request joining the translation team for Android on the site and start translating. All translations will then be automatically pushed to this repository, there is no need for any pull request for translations. diff --git a/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt b/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt index f8f4b5405324..d115e298c34d 100644 --- a/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt +++ b/src/main/java/com/nextcloud/client/errorhandling/ShowErrorActivity.kt @@ -69,7 +69,7 @@ class ShowErrorActivity : AppCompatActivity() { } override fun onCreateOptionsMenu(menu: Menu?): Boolean { - menuInflater.inflate(R.menu.activity_error_show, menu) + menuInflater.inflate(R.menu.activity_show_error, menu) return super.onCreateOptionsMenu(menu) } diff --git a/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt b/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt index 68866d1c1d3b..a14506feffbf 100644 --- a/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt +++ b/src/main/java/com/nextcloud/client/etm/pages/EtmPreferencesFragment.kt @@ -51,7 +51,7 @@ class EtmPreferencesFragment : EtmBaseFragment() { override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { super.onCreateOptionsMenu(menu, inflater) - inflater.inflate(R.menu.etm_preferences, menu) + inflater.inflate(R.menu.fragment_etm_preferences, menu) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt b/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt index 7d3342bc2a4f..4c791716f9a4 100644 --- a/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt +++ b/src/main/java/com/nextcloud/client/logger/ui/LogsActivity.kt @@ -84,7 +84,7 @@ class LogsActivity : ToolbarActivity() { } override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.logs_menu, menu) + menuInflater.inflate(R.menu.activity_logs, menu) (menu.findItem(R.id.action_search).actionView as SearchView).apply { setOnQueryTextListener(searchBoxListener) diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index ec5fb347298f..f083396db1a3 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -765,7 +765,7 @@ public boolean onPrepareOptionsMenu(Menu menu) { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.main_menu, menu); + inflater.inflate(R.menu.activity_file_display, menu); menu.findItem(R.id.action_create_dir).setVisible(false); menu.findItem(R.id.action_select_all).setVisible(false); diff --git a/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java b/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java index 211fddb9b817..72953d250ae2 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FolderPickerActivity.java @@ -293,11 +293,7 @@ protected void onPause() { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.main_menu, menu); - menu.findItem(R.id.action_switch_view).setVisible(false); - menu.findItem(R.id.action_sync_account).setVisible(false); - menu.findItem(R.id.action_select_all).setVisible(false); - // menu.findItem(R.id.action_sort).setVisible(false); + inflater.inflate(R.menu.activity_folder_picker, menu); return true; } diff --git a/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java b/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java index 79ada93f41c2..46792c86afbc 100644 --- a/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java @@ -303,7 +303,7 @@ private void hideRefreshLayoutLoader() { } public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.notifications_actions_menu, menu); + getMenuInflater().inflate(R.menu.activity_notifications, menu); return true; } diff --git a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index 90f5f8090fcd..06b62f7b90c1 100755 --- a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -1016,7 +1016,7 @@ private boolean isHaveMultipleAccount() { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.receive_file_menu, menu); + inflater.inflate(R.menu.activity_receive_external_files, menu); if (!isHaveMultipleAccount()) { MenuItem switchAccountMenu = menu.findItem(R.id.action_switch_account); diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index a71264e372be..0bf20965b426 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -175,7 +175,7 @@ protected void onCreate(Bundle savedInstanceState) { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.synced_folders_menu, menu); + inflater.inflate(R.menu.activity_synced_folders, menu); if (powerManagementService.isPowerSavingExclusionAvailable()) { MenuItem item = menu.findItem(R.id.action_disable_power_save_check); diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java index c65f12731c74..4a46e4e5c7ca 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadFilesActivity.java @@ -254,7 +254,7 @@ public static void startUploadActivityForResult(Activity activity, Account accou @Override public boolean onCreateOptionsMenu(Menu menu) { mOptionsMenu = menu; - getMenuInflater().inflate(R.menu.upload_files_picker, menu); + getMenuInflater().inflate(R.menu.activity_upload_files, menu); if(!mLocalFolderPickerMode) { MenuItem selectAll = menu.findItem(R.id.action_select_all); diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 5bb867f9b867..606cc7db24c7 100755 --- a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -269,7 +269,7 @@ protected void onPause() { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.upload_list_menu, menu); + inflater.inflate(R.menu.activity_upload_list, menu); return true; } diff --git a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java index 60b5ce39a70b..db5e18e2e782 100644 --- a/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java @@ -156,7 +156,7 @@ public void onCreate(Bundle savedInstanceState) { @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.user_info_menu, menu); + inflater.inflate(R.menu.activity_user_info, menu); return true; } diff --git a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java index 2ac47fb9ca8c..9bebf2244313 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UserListAdapter.java @@ -183,7 +183,7 @@ private void onOverflowIconClicked(View view, AppCompatCheckBox allowEditsCheckB context.getTheme().applyStyle(R.style.FallbackThemingTheme, true); } PopupMenu popup = new PopupMenu(context, view); - popup.inflate(R.menu.file_detail_sharing_menu); + popup.inflate(R.menu.item_user_sharing_settings); prepareOptionsMenu(popup.getMenu(), share); diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java index c33b437d315c..6b7943c46963 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -300,7 +300,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat private void onOverflowIconClicked(View view) { PopupMenu popup = new PopupMenu(getActivity(), view); - popup.inflate(R.menu.file_details_actions_menu); + popup.inflate(R.menu.fragment_file_detail); prepareOptionsMenu(popup.getMenu()); popup.setOnMenuItemClickListener(this::optionsItemSelected); diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java index 69139ec13694..35be700d8c05 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailSharingFragment.java @@ -409,7 +409,7 @@ public void showLinkOverflowMenu() { } PopupMenu popup = new PopupMenu(context, overflowMenuShareLink); - popup.inflate(R.menu.file_detail_sharing_link_menu); + popup.inflate(R.menu.fragment_file_detail_sharing_link); prepareOptionsMenu(popup.getMenu()); popup.setOnMenuItemClickListener(this::optionsItemSelected); popup.show(); diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index ab26974836d6..3919351a31ef 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -467,7 +467,7 @@ public void showActivityDetailView(OCFile file) { @Override public void onOverflowIconClicked(OCFile file, View view) { PopupMenu popup = new PopupMenu(getActivity(), view); - popup.inflate(R.menu.file_actions_menu); + popup.inflate(R.menu.item_file); Account currentAccount = ((FileActivity) getActivity()).getAccount(); FileMenuFilter mf = new FileMenuFilter(mAdapter.getFiles().size(), Collections.singleton(file), @@ -587,7 +587,7 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) { mActiveActionMode = mode; MenuInflater inflater = getActivity().getMenuInflater(); - inflater.inflate(R.menu.file_actions_menu, menu); + inflater.inflate(R.menu.item_file, menu); mode.invalidate(); //set gray color diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java index 11c089471673..2f6597e8378f 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java @@ -166,7 +166,7 @@ public static ContactListFragment newInstance(OCFile file, Account account) { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.contactlist_menu, menu); + inflater.inflate(R.menu.fragment_contact_list, menu); } @Override diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java index 385850b8591f..ac094285d20c 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageFragment.java @@ -315,7 +315,7 @@ public void onStop() { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.file_actions_menu, menu); + inflater.inflate(R.menu.item_file, menu); } /** diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java index ca91c8869eb4..30f6698412e6 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewMediaFragment.java @@ -334,7 +334,7 @@ private void stopAudio() { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.file_actions_menu, menu); + inflater.inflate(R.menu.item_file, menu); } @Override diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java b/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java index 4eb15b2e0225..d4345bc9ac8f 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewTextFragment.java @@ -396,7 +396,7 @@ protected void onPostExecute(final StringWriter stringWriter) { @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); - inflater.inflate(R.menu.file_actions_menu, menu); + inflater.inflate(R.menu.item_file, menu); MenuItem menuItem = menu.findItem(R.id.action_search); menuItem.setVisible(true); diff --git a/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java b/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java index 83200f40e2e6..a6aac5ac08d4 100644 --- a/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java +++ b/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java @@ -212,7 +212,7 @@ public void onDestroy() { @Override public void onOverflowIconClicked(TrashbinFile file, View view) { PopupMenu popup = new PopupMenu(this, view); - popup.inflate(R.menu.trashbin_actions_menu); + popup.inflate(R.menu.item_trashbin); popup.setOnMenuItemClickListener(item -> { trashbinPresenter.removeTrashbinFile(file); @@ -243,7 +243,7 @@ public void onRestoreIconClicked(TrashbinFile file, View view) { @Override public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.trashbin_options_menu, menu); + getMenuInflater().inflate(R.menu.activity_trashbin, menu); return true; } diff --git a/src/main/res/layout/drawer.xml b/src/main/res/layout/drawer.xml index 84f3773a362a..240f965d93c0 100644 --- a/src/main/res/layout/drawer.xml +++ b/src/main/res/layout/drawer.xml @@ -31,7 +31,7 @@ android:background="@color/bg_default" android:theme="@style/NavigationView_ItemTextAppearance" app:headerLayout="@layout/drawer_header" - app:menu="@menu/drawer_menu"> + app:menu="@menu/partial_drawer_entries"> + + + + + + + + diff --git a/src/main/res/menu/logs_menu.xml b/src/main/res/menu/activity_logs.xml similarity index 100% rename from src/main/res/menu/logs_menu.xml rename to src/main/res/menu/activity_logs.xml diff --git a/src/main/res/menu/notifications_actions_menu.xml b/src/main/res/menu/activity_notifications.xml similarity index 96% rename from src/main/res/menu/notifications_actions_menu.xml rename to src/main/res/menu/activity_notifications.xml index 97e8b0805b5d..5a4eaab72110 100644 --- a/src/main/res/menu/notifications_actions_menu.xml +++ b/src/main/res/menu/activity_notifications.xml @@ -1,4 +1,5 @@ - + true false From a00677cd6321f3120ecb7bac9785c3850c25f145 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Tue, 19 Nov 2019 03:37:08 +0100 Subject: [PATCH 34/69] daily dev 20191119 dbe12e6d2 Merge pull request #4789 from nextcloud/name_pattern_menus 77b6b178a Merge pull request #4841 from nextcloud/disableOldLoginMethod b561f6883 allow to switch off old login method b59d96ace [tx-robot] updated from transifex 9ae550731 [tx-robot] updated from transifex bfdaba8bd [tx-robot] updated from transifex c82c08059 daily dev 20191116 --- build.gradle | 4 ++-- .../metadata/android/en-US/changelogs/20191119.txt | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191119.txt diff --git a/build.gradle b/build.gradle index c3cc683423f0..a8e20289e85f 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191116 - versionName "20191116" + versionCode 20191119 + versionName "20191119" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191119.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191119.txt new file mode 100644 index 000000000000..32577837bd3f --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191119.txt @@ -0,0 +1,7 @@ +dbe12e6d2 Merge pull request #4789 from nextcloud/name_pattern_menus +77b6b178a Merge pull request #4841 from nextcloud/disableOldLoginMethod +b561f6883 allow to switch off old login method +b59d96ace [tx-robot] updated from transifex +9ae550731 [tx-robot] updated from transifex +bfdaba8bd [tx-robot] updated from transifex +c82c08059 daily dev 20191116 From f1a0ac5c26cd8a9847b595a6cfa9d6c2c263bdcc Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 19 Nov 2019 03:10:54 +0000 Subject: [PATCH 35/69] [tx-robot] updated from transifex --- src/main/res/values-sl/strings.xml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index c3979eccafe0..cab3b950f762 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -233,6 +233,9 @@ Vpišite geslo za odšifriranje zasebnega ključa. Mapa ni prazna. Poteka ustvarjanje novih ključev ... + Vseh 12 besed skupaj predstavlja izredno močno geslo, s katerim imate le vi možnost ogleda in uporabe šifriranih datotek. Geslo si zapišite in shranite nekje na varnem. + Šifriranje deluje le na različici KitKat (4.4) in višjih. + Zabeležite si vaših 12 besed gesla za šifriranje Geslo … Poteka pridobivanje ključev ... Shranjevanje ključev @@ -245,11 +248,14 @@ Napaka dodajanja opombe k datoteki Program %1$s se je sesul. Poročilo + Ali želite poslati poročilo na sledilnik (zahteva račun Github)? Pridobivanje datoteke je spodletelo Napaka pridobivanja predlog Napaka začenjanja kamere Nastavitve + Inženirski preizkusni način Dodaj ali pošlji + Podajanje datoteke na upravljalnik prenosov je spodletelo Tiskanje datoteke je spodletelo Nazaj Povrni na star način prijave @@ -320,6 +326,11 @@ Nedovoljeni znaki: / \\ < > : \" | ? * Ime datoteke vsebuje vsaj en neveljaven znak. Ime datoteke + To je zmožnost oblaka Nextcloud. Okolje je treba nadgraditi. + Hranite podatke varno in pod vašim nadzorom + Varno sodelovanje in izmenjava datotek + Enostavna uporaba spletne pošte, koledarja in stikov + Souporaba zaslona, spletni sestanki in konference mapa Mapa že obstaja Ustvari @@ -366,15 +377,19 @@ Dnevniki Strežnik je v načinu vzdrževanja Počisti podatke + Nastavitve, podatkovna zbirka, in potrdila strežnika na računu %1$s bodo trajno izbrisana. \n\nPrejete datoteke bodo ostale nedotaknjene.\n\nOpravilo je lahko dolgotrajno. Upravljanje s prostorom Predstavne datoteke ni mogoče prikazati v pretoku Predstavne datoteke ni mogoče prebrati. Predstavna datoteka ni pravilno kodirana. Poskus predvajanja je časovno potekel + Vgrajen predvajalnik predstavnih vsebin ne podpira podane vrsta datoteke Nepodprt predstavni kodek Gumb za hitro predvajanje naprej + Predvajalnik glasbe %1$s Gumb za predvajanje in premor Vrni nazaj + %1$s (predvajanje) najprej najnovejše najprej najstarejše po abecedi naraščajoče A–Ž @@ -388,8 +403,10 @@ Premakni v … Prišlo je do napake med čakanjem odziva strežnika. Opravila ni mogoče končati. Med povezovanjem s strežnikom je prišlo do napake. + Prišlo je do napake med čakanjem na strežnik. Opravila ni mogoče končati. Zahtevanega dejanja ni bilo mogoče končati. Strežnik ni dosegljiv Nova opomba … + Zaznana je nova mapa s predstavno vsebino: %1$s . slika video Novo obvestilo @@ -406,6 +423,7 @@ Opazovalnik datotek Pokaži napredek usklajevanja datotek in končana opravila Usklajevanje datotek + Pokaži obvestila za zaznane nove mape predstavnih vsebin in podobno Splošna obvestila Napredek glasbenega predvajalnika Predvajalnik predstavnih datotek @@ -447,6 +465,7 @@ Splošno Več Dnevno usklajevanje stikov + Pokaži obvestila iskanja predstavnih vsebin Obvesti o na novo zaznanih mapah s predstavno vsebino Splošno Javno dovoljenje GNU GPL, različice 2 Pomoč @@ -490,6 +509,7 @@ Preimenovanje ni mogoče. Ime je že uporabljeno. Osveževanje ni dovoljena Osveževanje ni dovoljeno + Slika prilagojene velikosti ni na voljo. Obnovi datoteko Obnovi izbrisano datoteko Poteka pridobivanje datoteke ... From 8879179cffe6a65a9d937f938fa92aacd57bb6c5 Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot Date: Tue, 19 Nov 2019 12:28:44 +0000 Subject: [PATCH 36/69] Drone: update FindBugs results to reflect reduced error/warning count [skip ci] Signed-off-by: nextcloud-android-bot --- scripts/analysis/findbugs-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 57214136334b..97964b3fde64 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -423 +422 \ No newline at end of file From 0223085a8dfaf413811dee91acd3023a861e5824 Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Tue, 19 Nov 2019 14:35:31 +0100 Subject: [PATCH 37/69] shorten text a bit Signed-off-by: tobiasKaminsky --- src/main/res/values/strings.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 3c1fbb8a5aa7..4aac77165312 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -710,12 +710,12 @@ Account not found! Protecting your data - self-hosted productivity platform + self-hosted productivity Browse and share all actions at your fingertips - Activity, shares, offline files + Activity, shares, … everything quickly accessible All your accounts @@ -724,8 +724,8 @@ Automatic upload for your photos & videos - Sync calendar & contacts - with DAVx5 (formerly DAVdroid) + Calendar & contacts + Sync with DAVx5 No personal info set Add name, picture and contact details on your profile page. From 26c5c5e015eefe5653a40cbf57aa8a16cf87a277 Mon Sep 17 00:00:00 2001 From: Unpublished Date: Mon, 18 Nov 2019 01:25:50 +0100 Subject: [PATCH 38/69] Use png for checker image to use it via bitmap Signed-off-by: Unpublished --- src/main/res/drawable-hdpi/checker_16_16.png | Bin 0 -> 149 bytes src/main/res/drawable-mdpi/checker_16_16.png | Bin 0 -> 81 bytes src/main/res/drawable-xhdpi/checker_16_16.png | Bin 0 -> 162 bytes .../res/drawable-xxhdpi/checker_16_16.png | Bin 0 -> 170 bytes .../res/drawable-xxxhdpi/checker_16_16.png | Bin 0 -> 176 bytes src/main/res/drawable/checker_16_16.xml | 18 ------------------ 6 files changed, 18 deletions(-) create mode 100644 src/main/res/drawable-hdpi/checker_16_16.png create mode 100644 src/main/res/drawable-mdpi/checker_16_16.png create mode 100644 src/main/res/drawable-xhdpi/checker_16_16.png create mode 100644 src/main/res/drawable-xxhdpi/checker_16_16.png create mode 100644 src/main/res/drawable-xxxhdpi/checker_16_16.png delete mode 100644 src/main/res/drawable/checker_16_16.xml diff --git a/src/main/res/drawable-hdpi/checker_16_16.png b/src/main/res/drawable-hdpi/checker_16_16.png new file mode 100644 index 0000000000000000000000000000000000000000..1448873071b4521781a2f6708bdc06eaada7b45a GIT binary patch literal 149 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;w=>VS)*Z=?jzkT}_NCLs@*RPS; zqJj&T0hO_r1o;L3{Q3F!$Jg)LF3l1L3MqNIIEGZjB^NLzopr0FJQ}Jpcdz literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xhdpi/checker_16_16.png b/src/main/res/drawable-xhdpi/checker_16_16.png new file mode 100644 index 0000000000000000000000000000000000000000..a700c752975f683510202a0849946d2e4c972a86 GIT binary patch literal 162 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv#Q>iW*VnIKzj^cKKMWwVAyR8D zPwWRO<|qmB3x50k=lgF@UwqeJVf_gxrs3)07*Y|J+`ts3$`Oz}gOgF%T46zD3#WtR m+tmsQsy+)+)kJ!97#a4@mslY$zV$p%4}+(xpUXO@geCyP2}WlC literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxhdpi/checker_16_16.png b/src/main/res/drawable-xxhdpi/checker_16_16.png new file mode 100644 index 0000000000000000000000000000000000000000..1cfdf0a9a50aa044896f4ec4127554a354818c73 GIT binary patch literal 170 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDB3?!H8JlO)I6a#!hT;INZ`~Uy{*RNj#Ngx1n z|NZ+1WB^6pym`njxg HN@xNAL*Ge_ literal 0 HcmV?d00001 diff --git a/src/main/res/drawable-xxxhdpi/checker_16_16.png b/src/main/res/drawable-xxxhdpi/checker_16_16.png new file mode 100644 index 0000000000000000000000000000000000000000..9c964435ce9671fd4c38d1028791968f7c8babea GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0J3?w7mbKU|e#Q>iW*LUyU{r~^}_3PI_5(t3Y zfB*gg89FVdQ&MBb@0D9p}egFUf literal 0 HcmV?d00001 diff --git a/src/main/res/drawable/checker_16_16.xml b/src/main/res/drawable/checker_16_16.xml deleted file mode 100644 index 685071dd2044..000000000000 --- a/src/main/res/drawable/checker_16_16.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - From 0977c9d2e1f12560655343e4aa014e73328f1b98 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Wed, 20 Nov 2019 03:37:09 +0100 Subject: [PATCH 39/69] daily dev 20191120 21ef00654 Merge pull request #4858 from nextcloud/changeScreenshot 0223085a8 shorten text a bit 8879179cf Drone: update FindBugs results to reflect reduced error/warning count [skip ci] 94502f66b Merge pull request #4787 from ArisuOngaku/auto-upload-start-date-persistence 21109375c Merge pull request #4839 from nextcloud/dependabot/gradle/markwonVersion-4.2.0 f1a0ac5c2 [tx-robot] updated from transifex a00677cd6 daily dev 20191119 --- build.gradle | 4 ++-- .../metadata/android/en-US/changelogs/20191120.txt | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191120.txt diff --git a/build.gradle b/build.gradle index 7d32ef99740a..8df2fd31ffb7 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191119 - versionName "20191119" + versionCode 20191120 + versionName "20191120" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191120.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191120.txt new file mode 100644 index 000000000000..0a30249a3a52 --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191120.txt @@ -0,0 +1,7 @@ +21ef00654 Merge pull request #4858 from nextcloud/changeScreenshot +0223085a8 shorten text a bit +8879179cf Drone: update FindBugs results to reflect reduced error/warning count [skip ci] +94502f66b Merge pull request #4787 from ArisuOngaku/auto-upload-start-date-persistence +21109375c Merge pull request #4839 from nextcloud/dependabot/gradle/markwonVersion-4.2.0 +f1a0ac5c2 [tx-robot] updated from transifex +a00677cd6 daily dev 20191119 From 16c220e7afa2e9ceea87e6be2eaebb61a6b6b2a0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2019 02:16:42 +0000 Subject: [PATCH 40/69] Bump exifinterface from 1.0.0 to 1.1.0 Bumps exifinterface from 1.0.0 to 1.1.0. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 8df2fd31ffb7..4a579645bca9 100644 --- a/build.gradle +++ b/build.gradle @@ -273,7 +273,7 @@ dependencies { implementation 'com.jakewharton:disklrucache:2.0.2' implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.cardview:cardview:1.0.0' - implementation 'androidx.exifinterface:exifinterface:1.0.0' + implementation 'androidx.exifinterface:exifinterface:1.1.0' implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0" implementation "androidx.work:work-runtime:2.2.0" implementation "androidx.work:work-runtime-ktx:2.2.0" From 6e5d7a89c1268fcb4d5f691b18f3f0a39283a597 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Thu, 21 Nov 2019 03:37:08 +0100 Subject: [PATCH 41/69] daily dev 20191121 b9ff6cecf Merge pull request #4843 from Unpublished/fix4842 0977c9d2e daily dev 20191120 --- build.gradle | 4 ++-- .../fastlane/metadata/android/en-US/changelogs/20191121.txt | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191121.txt diff --git a/build.gradle b/build.gradle index 8df2fd31ffb7..a6704f8d1e1e 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191120 - versionName "20191120" + versionCode 20191121 + versionName "20191121" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191121.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191121.txt new file mode 100644 index 000000000000..858f2fc0dde5 --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191121.txt @@ -0,0 +1,2 @@ +b9ff6cecf Merge pull request #4843 from Unpublished/fix4842 +0977c9d2e daily dev 20191120 From 609b99666fd19967dad1d6543f14a5561e1a2d91 Mon Sep 17 00:00:00 2001 From: Chris Narkiewicz Date: Sat, 16 Nov 2019 22:17:22 +0000 Subject: [PATCH 42/69] New user model New non-nullable user model abstracts away all complexities of Nextcloud user account and provides. - backported java.util.Optional - extended UserAccountManager with User getters - deprecated CurrentAccountProvider.getCurrentAccount() method - migrated connectivity service to new user model - migrated UploadsStorageManager to new user model - migrated OCFileListAdapter Signed-off-by: Chris Narkiewicz --- .../nextcloud/client/account/AnonymousUser.kt | 57 +++ .../account/CurrentAccountProvider.java | 18 +- .../client/account/RegisteredUser.kt | 46 +++ .../com/nextcloud/client/account/Server.kt | 30 ++ .../java/com/nextcloud/client/account/User.kt | 53 +++ .../client/account/UserAccountManager.java | 19 + .../account/UserAccountManagerImpl.java | 94 +++++ .../network/ConnectivityServiceImpl.java | 80 ++-- .../client/network/NetworkModule.java | 7 +- .../com/nextcloud/java/util/Optional.java | 366 ++++++++++++++++++ .../java/util/function/Predicate.java | 121 ++++++ .../com/nextcloud/java/util/package-info.java | 26 ++ .../datamodel/UploadsStorageManager.java | 131 +++---- .../android/ui/adapter/OCFileListAdapter.java | 55 +-- .../ui/fragment/OCFileListFragment.java | 4 +- .../client/network/ConnectivityServiceTest.kt | 51 ++- 16 files changed, 997 insertions(+), 161 deletions(-) create mode 100644 src/main/java/com/nextcloud/client/account/AnonymousUser.kt create mode 100644 src/main/java/com/nextcloud/client/account/RegisteredUser.kt create mode 100644 src/main/java/com/nextcloud/client/account/Server.kt create mode 100644 src/main/java/com/nextcloud/client/account/User.kt create mode 100644 src/main/java/com/nextcloud/java/util/Optional.java create mode 100644 src/main/java/com/nextcloud/java/util/function/Predicate.java create mode 100644 src/main/java/com/nextcloud/java/util/package-info.java diff --git a/src/main/java/com/nextcloud/client/account/AnonymousUser.kt b/src/main/java/com/nextcloud/client/account/AnonymousUser.kt new file mode 100644 index 000000000000..d949cf5bf393 --- /dev/null +++ b/src/main/java/com/nextcloud/client/account/AnonymousUser.kt @@ -0,0 +1,57 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2019 Chris Narkiewicz + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.nextcloud.client.account + +import android.accounts.Account +import android.content.Context +import android.net.Uri +import com.owncloud.android.MainApp +import com.owncloud.android.R +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.lib.common.OwnCloudBasicCredentials +import java.net.URI + +/** + * This object represents anonymous user, ie. user that did not log in the Nextcloud server. + * It serves as a semantically correct "empty value", allowing simplification of logic + * in various components requiring user data, such as DB queries. + */ +internal class AnonymousUser(private val accountType: String) : User { + + companion object { + @JvmStatic + fun fromContext(context: Context): AnonymousUser { + val type = context.getString(R.string.account_type) + return AnonymousUser(type) + } + } + + override val accountName: String = "anonymous" + override val server = Server(URI.create(""), MainApp.MINIMUM_SUPPORTED_SERVER_VERSION) + + override fun toPlatformAccount(): Account { + return Account(accountName, accountType) + } + + override fun toOwnCloudAccount(): OwnCloudAccount { + return OwnCloudAccount(Uri.EMPTY, OwnCloudBasicCredentials("", "")) + } +} diff --git a/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java b/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java index f37624933b4d..ab6484599a5f 100644 --- a/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java +++ b/src/main/java/com/nextcloud/client/account/CurrentAccountProvider.java @@ -2,19 +2,31 @@ import android.accounts.Account; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** - * This interface provides access to currently selected user Account. + * This interface provides access to currently selected user. + * * @see UserAccountManager */ -@FunctionalInterface public interface CurrentAccountProvider { /** - * Get currently active account. + * Get currently active account. * * @return Currently selected {@link Account} or first valid {@link Account} registered in OS or null, if not available at all. */ + @Deprecated @Nullable Account getCurrentAccount(); + + /** + * Get currently active user profile. If there is no actice user, anonymous user is returned. + * + * @return User profile. Profile is never null. + */ + @NonNull + default User getUser() { + return new AnonymousUser("dummy"); + } } diff --git a/src/main/java/com/nextcloud/client/account/RegisteredUser.kt b/src/main/java/com/nextcloud/client/account/RegisteredUser.kt new file mode 100644 index 000000000000..7a8a5f6314ab --- /dev/null +++ b/src/main/java/com/nextcloud/client/account/RegisteredUser.kt @@ -0,0 +1,46 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2019 Chris Narkiewicz + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.nextcloud.client.account + +import android.accounts.Account +import com.owncloud.android.lib.common.OwnCloudAccount + +/** + * This class represents normal user logged into the Nextcloud server. + */ +internal class RegisteredUser( + private val account: Account, + private val ownCloudAccount: OwnCloudAccount, + override val server: Server +) : User { + + override val accountName: String get() { + return account.name + } + + override fun toPlatformAccount(): Account { + return account + } + + override fun toOwnCloudAccount(): OwnCloudAccount { + return ownCloudAccount + } +} diff --git a/src/main/java/com/nextcloud/client/account/Server.kt b/src/main/java/com/nextcloud/client/account/Server.kt new file mode 100644 index 000000000000..0a44dfb4ecbd --- /dev/null +++ b/src/main/java/com/nextcloud/client/account/Server.kt @@ -0,0 +1,30 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2019 Chris Narkiewicz + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.nextcloud.client.account + +import com.owncloud.android.lib.resources.status.OwnCloudVersion +import java.net.URI + +/** + * This object provides all information necessary to interact + * with backend server. + */ +data class Server(val uri: URI, val version: OwnCloudVersion) diff --git a/src/main/java/com/nextcloud/client/account/User.kt b/src/main/java/com/nextcloud/client/account/User.kt new file mode 100644 index 000000000000..408797d07965 --- /dev/null +++ b/src/main/java/com/nextcloud/client/account/User.kt @@ -0,0 +1,53 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2019 Chris Narkiewicz + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.nextcloud.client.account + +import android.accounts.Account +import com.owncloud.android.lib.common.OwnCloudAccount + +interface User { + val accountName: String + val server: Server + + /** + * This is temporary helper method created to facilitate incremental refactoring. + * Code using legacy platform Account can be partially converted to instantiate User + * object and use account instance when required. + * + * This method calls will allow tracing code awaiting further refactoring. + * + * @return Account instance that is associated with this User object. + */ + @Deprecated("Temporary workaround") + fun toPlatformAccount(): Account + + /** + * This is temporary helper method created to facilitate incremental refactoring. + * Code using legacy ownCloud account can be partially converted to instantiate User + * object and use account instance when required. + * + * This method calls will allow tracing code awaiting further refactoring. + * + * @return OwnCloudAccount instance that is associated with this User object. + */ + @Deprecated("Temporary workaround") + fun toOwnCloudAccount(): OwnCloudAccount +} diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManager.java b/src/main/java/com/nextcloud/client/account/UserAccountManager.java index 5584541c49e2..0b217073e24c 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManager.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManager.java @@ -21,10 +21,13 @@ import android.accounts.Account; +import com.nextcloud.java.util.Optional; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.resources.status.OwnCloudVersion; +import java.util.List; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -51,6 +54,22 @@ public interface UserAccountManager extends CurrentAccountProvider { @NonNull Account[] getAccounts(); + /** + * Get configured nextcloud user accounts + * @return List of users or empty list, if users are not registered. + */ + @NonNull + List getAllUsers(); + + /** + * Get user with a specific account name. + * + * @param accountName Account name of the requested user + * @return User or empty optional if user does not exist. + */ + @NonNull + Optional getUser(CharSequence accountName); + /** * Check if Nextcloud account is registered in {@link android.accounts.AccountManager} * diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java b/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java index 575580f04956..4ea8246872eb 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManagerImpl.java @@ -27,6 +27,7 @@ import android.preference.PreferenceManager; import android.text.TextUtils; +import com.nextcloud.java.util.Optional; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -35,11 +36,18 @@ import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.UserInfo; +import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; +import org.jetbrains.annotations.NotNull; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + import javax.inject.Inject; import androidx.annotation.NonNull; @@ -80,6 +88,20 @@ public Account[] getAccounts() { return accountManager.getAccountsByType(getAccountType()); } + @Override + @NonNull + public List getAllUsers() { + Account[] accounts = getAccounts(); + List users = new ArrayList<>(accounts.length); + for (Account account : accounts) { + User user = createUserFromAccount(account); + if (user != null) { + users.add(user); + } + } + return users; + } + @Override public boolean exists(Account account) { Account[] nextcloudAccounts = getAccounts(); @@ -103,6 +125,7 @@ public boolean exists(Account account) { return false; } + @Override @Nullable public Account getCurrentAccount() { Account[] ocAccounts = getAccounts(); @@ -139,6 +162,76 @@ public Account getCurrentAccount() { return defaultAccount; } + /** + * Temporary solution to convert platform account to user instance. + * It takes null and returns null on error to ease error handling + * in legacy code. + * + * @param account Account instance + * @return User instance or null, if conversion failed + */ + @Nullable + private User createUserFromAccount(@Nullable Account account) { + if (account == null) { + return null; + } + + OwnCloudAccount ownCloudAccount = null; + try { + ownCloudAccount = new OwnCloudAccount(account, context); + } catch (AccountUtils.AccountNotFoundException ex) { + return null; + } + + /* + * Server version + */ + String serverVersionStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_VERSION); + OwnCloudVersion serverVersion; + if (serverVersionStr != null) { + serverVersion = new OwnCloudVersion(serverVersionStr); + } else { + serverVersion = MainApp.MINIMUM_SUPPORTED_SERVER_VERSION; + } + + /* + * Server address + */ + String serverAddressStr = accountManager.getUserData(account, AccountUtils.Constants.KEY_OC_BASE_URL); + if (serverAddressStr == null || serverAddressStr.isEmpty()) { + return AnonymousUser.fromContext(context); + } + URI serverUri = URI.create(serverAddressStr); // TODO: validate + + return new RegisteredUser( + account, + ownCloudAccount, + new Server(serverUri, serverVersion) + ); + } + + /** + * Get user. If user cannot be retrieved due to data error, anonymous user is returned instead. + * + * + * @return User instance + */ + @NotNull + @Override + public User getUser() { + Account account = getCurrentAccount(); + User user = createUserFromAccount(account); + return user != null ? user : AnonymousUser.fromContext(context); + } + + @Override + @NonNull + public Optional getUser(CharSequence accountName) { + Account account = getAccountByName(accountName.toString()); + User user = createUserFromAccount(account); + return Optional.of(user); + } + @Override @Nullable public OwnCloudAccount getCurrentOwnCloudAccount() { @@ -197,6 +290,7 @@ public boolean setCurrentOwnCloudAccount(int hashCode) { return result; } + @Deprecated @Override @NonNull public OwnCloudVersion getServerVersion(Account account) { diff --git a/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java b/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java index 9060b7922ad3..14f2a54cc4cf 100644 --- a/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java +++ b/src/main/java/com/nextcloud/client/network/ConnectivityServiceImpl.java @@ -20,14 +20,13 @@ package com.nextcloud.client.network; -import android.accounts.Account; import android.net.ConnectivityManager; import android.net.NetworkInfo; import com.evernote.android.job.JobRequest; +import com.nextcloud.client.account.Server; import com.nextcloud.client.account.UserAccountManager; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.utils.Log_OC; +import com.nextcloud.client.logger.Logger; import com.owncloud.android.lib.resources.status.OwnCloudVersion; import org.apache.commons.httpclient.HttpClient; @@ -44,10 +43,11 @@ class ConnectivityServiceImpl implements ConnectivityService { private final static String TAG = ConnectivityServiceImpl.class.getName(); - private ConnectivityManager connectivityManager; - private UserAccountManager accountManager; - private ClientFactory clientFactory; - private GetRequestBuilder requestBuilder; + private final ConnectivityManager connectivityManager; + private final UserAccountManager accountManager; + private final ClientFactory clientFactory; + private final GetRequestBuilder requestBuilder; + private final Logger logger; static class GetRequestBuilder implements Function1 { @Override @@ -59,55 +59,55 @@ public GetMethod invoke(String url) { ConnectivityServiceImpl(ConnectivityManager connectivityManager, UserAccountManager accountManager, ClientFactory clientFactory, - GetRequestBuilder requestBuilder) { + GetRequestBuilder requestBuilder, + Logger logger) { this.connectivityManager = connectivityManager; this.accountManager = accountManager; this.clientFactory = clientFactory; this.requestBuilder = requestBuilder; + this.logger = logger; } @Override public boolean isInternetWalled() { if (isOnlineWithWifi()) { try { - Account account = accountManager.getCurrentAccount(); - OwnCloudAccount ocAccount = accountManager.getCurrentOwnCloudAccount(); - if (account != null && ocAccount != null) { - OwnCloudVersion serverVersion = accountManager.getServerVersion(account); - - String url; - if (serverVersion.compareTo(OwnCloudVersion.nextcloud_13) > 0) { - url = ocAccount.getBaseUri() + "/index.php/204"; - } else { - url = ocAccount.getBaseUri() + "/status.php"; - } - - GetMethod get = requestBuilder.invoke(url); - HttpClient client = clientFactory.createPlainClient(); - - int status = client.executeMethod(get); + Server server = accountManager.getUser().getServer(); + String baseServerAddress = server.getUri().toString(); + if (baseServerAddress.isEmpty()) { + return true; + } + String url; + if (server.getVersion().compareTo(OwnCloudVersion.nextcloud_13) > 0) { + url = baseServerAddress + "/index.php/204"; + } else { + url = baseServerAddress + "/status.php"; + } - if (serverVersion.compareTo(OwnCloudVersion.nextcloud_13) > 0) { - return !(status == HttpStatus.SC_NO_CONTENT && - (get.getResponseContentLength() == -1 || get.getResponseContentLength() == 0)); - } else { - if (status == HttpStatus.SC_OK) { - try { - // try parsing json to verify response - // check if json contains maintenance and it should be false - - String json = get.getResponseBodyAsString(); - return new JSONObject(json).getBoolean("maintenance"); - } catch (Exception e) { - return true; - } - } else { + GetMethod get = requestBuilder.invoke(url); + HttpClient client = clientFactory.createPlainClient(); + + int status = client.executeMethod(get); + + if (server.getVersion().compareTo(OwnCloudVersion.nextcloud_13) > 0) { + return !(status == HttpStatus.SC_NO_CONTENT && + (get.getResponseContentLength() == -1 || get.getResponseContentLength() == 0)); + } else { + if (status == HttpStatus.SC_OK) { + try { + // try parsing json to verify response + // check if json contains maintenance and it should be false + String json = get.getResponseBodyAsString(); + return new JSONObject(json).getBoolean("maintenance"); + } catch (Exception e) { return true; } + } else { + return true; } } } catch (IOException e) { - Log_OC.e(TAG, "Error checking internet connection", e); + logger.e(TAG, "Error checking internet connection", e); } } else { return getActiveNetworkType() == JobRequest.NetworkType.ANY; diff --git a/src/main/java/com/nextcloud/client/network/NetworkModule.java b/src/main/java/com/nextcloud/client/network/NetworkModule.java index 61634af7a964..2a7edf8a7dd1 100644 --- a/src/main/java/com/nextcloud/client/network/NetworkModule.java +++ b/src/main/java/com/nextcloud/client/network/NetworkModule.java @@ -24,6 +24,7 @@ import android.net.ConnectivityManager; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.logger.Logger; import javax.inject.Singleton; @@ -36,11 +37,13 @@ public class NetworkModule { @Provides ConnectivityService connectivityService(ConnectivityManager connectivityManager, UserAccountManager accountManager, - ClientFactory clientFactory) { + ClientFactory clientFactory, + Logger logger) { return new ConnectivityServiceImpl(connectivityManager, accountManager, clientFactory, - new ConnectivityServiceImpl.GetRequestBuilder()); + new ConnectivityServiceImpl.GetRequestBuilder(), + logger); } @Provides diff --git a/src/main/java/com/nextcloud/java/util/Optional.java b/src/main/java/com/nextcloud/java/util/Optional.java new file mode 100644 index 000000000000..e3a31e5c2a84 --- /dev/null +++ b/src/main/java/com/nextcloud/java/util/Optional.java @@ -0,0 +1,366 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.nextcloud.java.util; + +import com.nextcloud.java.util.function.Predicate; + +import java.util.NoSuchElementException; + +import androidx.core.util.Consumer; +import androidx.core.util.ObjectsCompat; +import androidx.core.util.Supplier; +import kotlin.jvm.functions.Function1; + +/** + * This class is backported from Java 8 to be used on older Android API levels. + * It uses available interfaces from Kotlin and androidx. It is semantically + * identical with Java 8 API, allowing smooth migration when those APIs become + * available. + * + * A container object which may or may not contain a non-null value. + * If a value is present, {@code isPresent()} will return {@code true} and + * {@code get()} will return the value. + * + *

Additional methods that depend on the presence or absence of a contained + * value are provided, such as {@link #orElse(java.lang.Object) orElse()} + * (return a default value if value not present) and + * {@link #ifPresent(Consumer) ifPresent()} (execute a block + * of code if the value is present). + */ +public final class Optional { + /** + * Common instance for {@code empty()}. + */ + private static final Optional EMPTY = new Optional<>(); + + /** + * If non-null, the value; if null, indicates no value is present + */ + private final T value; + + /** + * Constructs an empty instance. + * + * @implNote Generally only one empty instance, {@link Optional#EMPTY}, + * should exist per VM. + */ + private Optional() { + this.value = null; + } + + /** + * Returns an empty {@code Optional} instance. No value is present for this + * Optional. + * + * @apiNote Though it may be tempting to do so, avoid testing if an object + * is empty by comparing with {@code ==} against instances returned by + * {@code Option.empty()}. There is no guarantee that it is a singleton. + * Instead, use {@link #isPresent()}. + * + * @param Type of the non-existent value + * @return an empty {@code Optional} + */ + public static Optional empty() { + @SuppressWarnings("unchecked") + Optional t = (Optional) EMPTY; + return t; + } + + /** + * Constructs an instance with the value present. + * + * @param value the non-null value to be present + * @throws NullPointerException if value is null + */ + private Optional(T value) { + if (value == null) { + throw new NullPointerException(); + } + this.value = value; + } + + /** + * Returns an {@code Optional} with the specified present non-null value. + * + * @param the class of the value + * @param value the value to be present, which must be non-null + * @return an {@code Optional} with the value present + * @throws NullPointerException if value is null + */ + public static Optional of(T value) { + return new Optional<>(value); + } + + /** + * Returns an {@code Optional} describing the specified value, if non-null, + * otherwise returns an empty {@code Optional}. + * + * @param the class of the value + * @param value the possibly-null value to describe + * @return an {@code Optional} with a present value if the specified value + * is non-null, otherwise an empty {@code Optional} + */ + public static Optional ofNullable(T value) { + return value == null ? empty() : of(value); + } + + /** + * If a value is present in this {@code Optional}, returns the value, + * otherwise throws {@code NoSuchElementException}. + * + * @return the non-null value held by this {@code Optional} + * @throws NoSuchElementException if there is no value present + * + * @see Optional#isPresent() + */ + public T get() { + if (value == null) { + throw new NoSuchElementException("No value present"); + } + return value; + } + + /** + * Return {@code true} if there is a value present, otherwise {@code false}. + * + * @return {@code true} if there is a value present, otherwise {@code false} + */ + public boolean isPresent() { + return value != null; + } + + /** + * If a value is present, invoke the specified consumer with the value, + * otherwise do nothing. + * + * @param consumer block to be executed if a value is present + * @throws NullPointerException if value is present and {@code consumer} is + * null + */ + public void ifPresent(Consumer consumer) { + if (value != null) { + consumer.accept(value); + } + } + + /** + * If a value is present, and the value matches the given predicate, + * return an {@code Optional} describing the value, otherwise return an + * empty {@code Optional}. + * + * @param predicate a predicate to apply to the value, if present + * @return an {@code Optional} describing the value of this {@code Optional} + * if a value is present and the value matches the given predicate, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the predicate is null + */ + public Optional filter(Predicate predicate) { + if (predicate == null) { + throw new NullPointerException(); + } + if (!isPresent()) { + return this; + } else { + return predicate.test(value) ? this : empty(); + } + } + + /** + * If a value is present, apply the provided mapping function to it, + * and if the result is non-null, return an {@code Optional} describing the + * result. Otherwise return an empty {@code Optional}. + * + * @apiNote This method supports post-processing on optional values, without + * the need to explicitly check for a return status. For example, the + * following code traverses a stream of file names, selects one that has + * not yet been processed, and then opens that file, returning an + * {@code Optional}: + * + *

{@code
+     *     Optional fis =
+     *         names.stream().filter(name -> !isProcessedYet(name))
+     *                       .findFirst()
+     *                       .map(name -> new FileInputStream(name));
+     * }
+ * + * Here, {@code findFirst} returns an {@code Optional}, and then + * {@code map} returns an {@code Optional} for the desired + * file if one exists. + * + * @param The type of the result of the mapping function + * @param mapper a mapping function to apply to the value, if present + * @return an {@code Optional} describing the result of applying a mapping + * function to the value of this {@code Optional}, if a value is present, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the mapping function is null + */ + public Optional map(Function1 mapper) { + if (mapper == null) { + throw new NullPointerException(); + } + if (!isPresent()) { + return empty(); + } else { + return Optional.ofNullable(mapper.invoke(value)); + } + } + + /** + * If a value is present, apply the provided {@code Optional}-bearing + * mapping function to it, return that result, otherwise return an empty + * {@code Optional}. This method is similar to {@link #map(Function1)}, + * but the provided mapper is one whose result is already an {@code Optional}, + * and if invoked, {@code flatMap} does not wrap it with an additional + * {@code Optional}. + * + * @param The type parameter to the {@code Optional} returned by + * @param mapper a mapping function to apply to the value, if present + * the mapping function + * @return the result of applying an {@code Optional}-bearing mapping + * function to the value of this {@code Optional}, if a value is present, + * otherwise an empty {@code Optional} + * @throws NullPointerException if the mapping function is null or returns + * a null result + */ + public Optional flatMap(Function1> mapper) { + if(mapper == null) { + throw new NullPointerException(); + } + if (!isPresent()) { + return empty(); + } else { + Optional u = mapper.invoke(value); + if (u == null) { + throw new NullPointerException(); + } + return u; + } + } + + /** + * Return the value if present, otherwise return {@code other}. + * + * @param other the value to be returned if there is no value present, may + * be null + * @return the value, if present, otherwise {@code other} + */ + public T orElse(T other) { + return value != null ? value : other; + } + + /** + * Return the value if present, otherwise invoke {@code other} and return + * the result of that invocation. + * + * @param other a {@code Supplier} whose result is returned if no value + * is present + * @return the value if present otherwise the result of {@code other.get()} + * @throws NullPointerException if value is not present and {@code other} is + * null + */ + public T orElseGet(Supplier other) { + return value != null ? value : other.get(); + } + + /** + * Return the contained value, if present, otherwise throw an exception + * to be created by the provided supplier. + * + * @apiNote A method reference to the exception constructor with an empty + * argument list can be used as the supplier. For example, + * {@code IllegalStateException::new} + * + * @param Type of the exception to be thrown + * @param exceptionSupplier The supplier which will return the exception to + * be thrown + * @return the present value + * @throws X if there is no value present + * @throws NullPointerException if no value is present and + * {@code exceptionSupplier} is null + */ + public T orElseThrow(Supplier exceptionSupplier) throws X { + if (value != null) { + return value; + } else { + throw exceptionSupplier.get(); + } + } + + /** + * Indicates whether some other object is "equal to" this Optional. The + * other object is considered equal if: + *
    + *
  • it is also an {@code Optional} and; + *
  • both instances have no value present or; + *
  • the present values are "equal to" each other via {@code equals()}. + *
+ * + * @param obj an object to be tested for equality + * @return {code true} if the other object is "equal to" this object + * otherwise {@code false} + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (!(obj instanceof Optional)) { + return false; + } + + Optional other = (Optional) obj; + return ObjectsCompat.equals(value, other.value); + } + + /** + * Returns the hash code value of the present value, if any, or 0 (zero) if + * no value is present. + * + * @return hash code value of the present value or 0 if no value is present + */ + @Override + public int hashCode() { + return ObjectsCompat.hashCode(value); + } + + /** + * Returns a non-empty string representation of this Optional suitable for + * debugging. The exact presentation format is unspecified and may vary + * between implementations and versions. + * + * @implSpec If a value is present the result must include its string + * representation in the result. Empty and present Optionals must be + * unambiguously differentiable. + * + * @return the string representation of this instance + */ + @Override + public String toString() { + return value != null + ? String.format("Optional[%s]", value) + : "Optional.empty"; + } +} diff --git a/src/main/java/com/nextcloud/java/util/function/Predicate.java b/src/main/java/com/nextcloud/java/util/function/Predicate.java new file mode 100644 index 000000000000..857ba3d3b150 --- /dev/null +++ b/src/main/java/com/nextcloud/java/util/function/Predicate.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.nextcloud.java.util.function; + +/** + * This class is backported from Java 8 to be used on older Android API levels. + * + * Represents a predicate (boolean-valued function) of one argument. + * + *

This is a functional interface + * whose functional method is {@link #test(Object)}. + * + * @param the type of the input to the predicate + */ +@FunctionalInterface +public interface Predicate { + + /** + * Evaluates this predicate on the given argument. + * + * @param t the input argument + * @return {@code true} if the input argument matches the predicate, + * otherwise {@code false} + */ + boolean test(T t); + + /** + * Returns a composed predicate that represents a short-circuiting logical + * AND of this predicate and another. When evaluating the composed + * predicate, if this predicate is {@code false}, then the {@code other} + * predicate is not evaluated. + * + *

Any exceptions thrown during evaluation of either predicate are relayed + * to the caller; if evaluation of this predicate throws an exception, the + * {@code other} predicate will not be evaluated. + * + * @param other a predicate that will be logically-ANDed with this + * predicate + * @return a composed predicate that represents the short-circuiting logical + * AND of this predicate and the {@code other} predicate + * @throws NullPointerException if other is null + */ + default Predicate and(Predicate other) { + if (other == null) { + throw new NullPointerException(); + } + return (t) -> test(t) && other.test(t); + } + + /** + * Returns a predicate that represents the logical negation of this + * predicate. + * + * @return a predicate that represents the logical negation of this + * predicate + */ + default Predicate negate() { + return (t) -> !test(t); + } + + /** + * Returns a composed predicate that represents a short-circuiting logical + * OR of this predicate and another. When evaluating the composed + * predicate, if this predicate is {@code true}, then the {@code other} + * predicate is not evaluated. + * + *

Any exceptions thrown during evaluation of either predicate are relayed + * to the caller; if evaluation of this predicate throws an exception, the + * {@code other} predicate will not be evaluated. + * + * @param other a predicate that will be logically-ORed with this + * predicate + * @return a composed predicate that represents the short-circuiting logical + * OR of this predicate and the {@code other} predicate + * @throws NullPointerException if other is null + */ + default Predicate or(Predicate other) { + if (other == null) { + throw new NullPointerException(); + } + return (t) -> test(t) || other.test(t); + } + + /** + * Returns a predicate that tests if two arguments are equal according + * to {@link androidx.core.util.ObjectsCompat#equals(Object, Object)}. + * + * @param the type of arguments to the predicate + * @param targetRef the object reference with which to compare for equality, + * which may be {@code null} + * @return a predicate that tests if two arguments are equal according + * to {@link androidx.core.util.ObjectsCompat#equals(Object, Object)} + */ + static Predicate isEqual(Object targetRef) { + return (null == targetRef) + ? object -> object == null + : object -> targetRef.equals(object); + } +} diff --git a/src/main/java/com/nextcloud/java/util/package-info.java b/src/main/java/com/nextcloud/java/util/package-info.java new file mode 100644 index 000000000000..fd71fbc2d3b7 --- /dev/null +++ b/src/main/java/com/nextcloud/java/util/package-info.java @@ -0,0 +1,26 @@ +/* + * Nextcloud Android client application + * + * @author Chris Narkiewicz + * Copyright (C) 2019 Chris Narkiewicz + * Copyright (C) 2019 Nextcloud GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * This is a compatibility package providing some backported Java 8 classes + * not available in some older Android runtimes. + */ +package com.nextcloud.java.util; diff --git a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index d31313f378ff..b8130bcdc4cf 100644 --- a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -30,6 +30,7 @@ import android.net.Uri; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; import com.owncloud.android.db.OCUpload; import com.owncloud.android.db.ProviderMeta.ProviderTableMeta; import com.owncloud.android.db.UploadResult; @@ -345,24 +346,19 @@ private OCUpload createOCUploadFromCursor(Cursor c) { } public OCUpload[] getCurrentAndPendingUploadsForCurrentAccount() { - Account account = currentAccountProvider.getCurrentAccount(); - - if (account != null) { - return getUploads( - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.LOCK_FAILED.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + - "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - " AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", - account.name); - } else { - return new OCUpload[0]; - } + User user = currentAccountProvider.getUser(); + + return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_IN_PROGRESS.value + + " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + + "==" + UploadResult.DELAYED_FOR_WIFI.getValue() + + " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + + "==" + UploadResult.LOCK_FAILED.getValue() + + " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + + "==" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + " OR " + ProviderTableMeta.UPLOADS_LAST_RESULT + + "==" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + " AND " + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + user.getAccountName()); } /** @@ -384,14 +380,10 @@ public OCUpload[] getFailedUploads() { } public OCUpload[] getFinishedUploadsForCurrentAccount() { - Account account = currentAccountProvider.getCurrentAccount(); + User user = currentAccountProvider.getUser(); - if (account != null) { - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", account.name); - } else { - return new OCUpload[0]; - } + return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", user.getAccountName()); } /** @@ -403,23 +395,19 @@ public OCUpload[] getFinishedUploads() { } public OCUpload[] getFailedButNotDelayedUploadsForCurrentAccount() { - Account account = currentAccountProvider.getCurrentAccount(); - - if (account != null) { - return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.LOCK_FAILED.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", - account.name); - } else { - return new OCUpload[0]; - } + User user = currentAccountProvider.getUser(); + + return getUploads(ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.LOCK_FAILED.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + user.getAccountName()); } /** @@ -446,50 +434,41 @@ private ContentResolver getDB() { } public long clearFailedButNotDelayedUploads() { - Account account = currentAccountProvider.getCurrentAccount(); - - long result = 0; - if (account != null) { - result = getDB().delete( - ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.LOCK_FAILED.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + - AND + ProviderTableMeta.UPLOADS_LAST_RESULT + - "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + - AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", - new String[]{account.name} - ); - } - + User user = currentAccountProvider.getUser(); + final long deleted = getDB().delete( + ProviderTableMeta.CONTENT_URI_UPLOADS, + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_FAILED.value + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.LOCK_FAILED.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.DELAYED_FOR_WIFI.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.DELAYED_FOR_CHARGING.getValue() + + AND + ProviderTableMeta.UPLOADS_LAST_RESULT + + "<>" + UploadResult.DELAYED_IN_POWER_SAVE_MODE.getValue() + + AND + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", + new String[]{user.getAccountName()} + ); Log_OC.d(TAG, "delete all failed uploads but those delayed for Wifi"); - if (result > 0) { + if (deleted > 0) { notifyObserversNow(); } - return result; + return deleted; } public long clearSuccessfulUploads() { - Account account = currentAccountProvider.getCurrentAccount(); - - long result = 0; - if (account != null) { - result = getDB().delete( - ProviderTableMeta.CONTENT_URI_UPLOADS, - ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + - ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{account.name} - ); - } + User user = currentAccountProvider.getUser(); + final long deleted = getDB().delete( + ProviderTableMeta.CONTENT_URI_UPLOADS, + ProviderTableMeta.UPLOADS_STATUS + "==" + UploadStatus.UPLOAD_SUCCEEDED.value + AND + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + "== ?", new String[]{user.getAccountName()} + ); Log_OC.d(TAG, "delete all successful uploads"); - if (result > 0) { + if (deleted > 0) { notifyObserversNow(); } - return result; + return deleted; } /** diff --git a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java index 597c1e66016a..ec947a8fb25b 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/OCFileListAdapter.java @@ -47,6 +47,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.target.BitmapImageViewTarget; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.R; @@ -117,7 +118,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter checkedFiles; private FileDataStorageManager mStorageManager; - private Account account; + private User user; private OCFileListFragmentInterface ocFileListFragmentInterface; private FilesFilter mFilesFilter; @@ -135,7 +136,7 @@ public class OCFileListAdapter extends RecyclerView.Adapter(); this.transferServiceGetter = transferServiceGetter; - if (account != null) { + if (this.user != null) { AccountManager platformAccountManager = AccountManager.get(mContext); - userId = platformAccountManager.getUserData(account, + userId = platformAccountManager.getUserData(this.user.toPlatformAccount(), com.owncloud.android.lib.common.accounts.AccountUtils.Constants.KEY_USER_ID); } else { userId = ""; @@ -399,7 +400,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi showFederatedShareAvatar(sharee.getUserId(), avatarRadius, resources, avatar); } else { avatar.setTag(sharee); - DisplayUtils.setAvatar(account, + DisplayUtils.setAvatar(user.toPlatformAccount(), sharee.getUserId(), sharee.getDisplayName(), this, @@ -460,17 +461,17 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi OperationsService.OperationsServiceBinder operationsServiceBinder = transferServiceGetter.getOperationsServiceBinder(); FileDownloader.FileDownloaderBinder fileDownloaderBinder = transferServiceGetter.getFileDownloaderBinder(); FileUploader.FileUploaderBinder fileUploaderBinder = transferServiceGetter.getFileUploaderBinder(); - if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(account, file)) { + if (operationsServiceBinder != null && operationsServiceBinder.isSynchronizing(user.toPlatformAccount(), file)) { //synchronizing gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing); gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE); - } else if (fileDownloaderBinder != null && fileDownloaderBinder.isDownloading(account, file)) { + } else if (fileDownloaderBinder != null && fileDownloaderBinder.isDownloading(user.toPlatformAccount(), file)) { // downloading gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing); gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE); - } else if (fileUploaderBinder != null && fileUploaderBinder.isUploading(account, file)) { + } else if (fileUploaderBinder != null && fileUploaderBinder.isUploading(user.toPlatformAccount(), file)) { //uploading gridViewHolder.localFileIndicator.setImageResource(R.drawable.ic_synchronizing); gridViewHolder.localFileIndicator.setVisibility(View.VISIBLE); @@ -575,13 +576,17 @@ private void setThumbnail(OCFile file, ImageView thumbnailView) { if (ThumbnailsCacheManager.cancelPotentialThumbnailWork(file, thumbnailView)) { try { final ThumbnailsCacheManager.ThumbnailGenerationTask task = - new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, mStorageManager, - account, asyncTasks); + new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, + mStorageManager, + user.toPlatformAccount(), + asyncTasks); if (thumbnail == null) { thumbnail = BitmapUtils.drawableToBitmap( - MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), - account, mContext)); + MimeTypeUtil.getFileTypeIcon(file.getMimeType(), + file.getFileName(), + user.toPlatformAccount(), + mContext)); } final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = new ThumbnailsCacheManager.AsyncThumbnailDrawable(mContext.getResources(), @@ -602,7 +607,7 @@ private void setThumbnail(OCFile file, ImageView thumbnailView) { } else { thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), - account, + user.toPlatformAccount(), mContext)); } } @@ -685,7 +690,7 @@ private void showShareIcon(OCFileListGridImageViewHolder gridViewHolder, OCFile sharedIconView.setImageResource(R.drawable.ic_unshared); sharedIconView.setContentDescription(mContext.getString(R.string.shared_icon_share)); } - if (accountManager.accountOwnsFile(file, account)) { + if (accountManager.accountOwnsFile(file, user.toPlatformAccount())) { sharedIconView.setOnClickListener(view -> ocFileListFragmentInterface.onShareIconClick(file)); } else { sharedIconView.setOnClickListener(view -> ocFileListFragmentInterface.showShareDetailView(file)); @@ -703,7 +708,7 @@ private void showShareIcon(OCFileListGridImageViewHolder gridViewHolder, OCFile * @param limitToMimeType show only files of this mimeType */ public void swapDirectory( - Account account, + User account, OCFile directory, FileDataStorageManager updatedStorageManager, boolean onlyOnDevice, String limitToMimeType @@ -712,8 +717,8 @@ public void swapDirectory( if (updatedStorageManager != null && !updatedStorageManager.equals(mStorageManager)) { mStorageManager = updatedStorageManager; - showShareAvatar = mStorageManager.getCapability(account.name).getVersion().isShareesOnDavSupported(); - this.account = account; + showShareAvatar = mStorageManager.getCapability(account.getAccountName()).getVersion().isShareesOnDavSupported(); + this.user = account; } if (mStorageManager != null) { mFiles = mStorageManager.getFolderContent(directory, onlyOnDevice); @@ -744,11 +749,11 @@ public void setData(List objects, ExtendedListFragment.SearchType search FileDataStorageManager storageManager, OCFile folder, boolean clear) { if (storageManager != null && mStorageManager == null) { mStorageManager = storageManager; - showShareAvatar = mStorageManager.getCapability(account.name).getVersion().isShareesOnDavSupported(); + showShareAvatar = mStorageManager.getCapability(user.getAccountName()).getVersion().isShareesOnDavSupported(); } if (mStorageManager == null) { - mStorageManager = new FileDataStorageManager(account, mContext.getContentResolver()); + mStorageManager = new FileDataStorageManager(user.toPlatformAccount(), mContext.getContentResolver()); } if (clear) { @@ -808,12 +813,12 @@ private void parseShares(List objects) { shares.add(ocShare); // get ocFile from Server to have an up-to-date copy - RemoteOperationResult result = new ReadFileRemoteOperation(ocShare.getPath()).execute(account, + RemoteOperationResult result = new ReadFileRemoteOperation(ocShare.getPath()).execute(user.toPlatformAccount(), mContext); if (result.isSuccess()) { OCFile file = FileStorageUtils.fillOCFile((RemoteFile) result.getData().get(0)); - FileStorageUtils.searchForLocalFileInDefaultPath(file, account); + FileStorageUtils.searchForLocalFileInDefaultPath(file, user.toPlatformAccount()); file = mStorageManager.saveFileWithParent(file, mContext); ShareType newShareType = ocShare.getShareType(); @@ -868,7 +873,7 @@ private void parseVirtuals(List objects, ExtendedListFragment.SearchType for (Object remoteFile : objects) { OCFile ocFile = FileStorageUtils.fillOCFile((RemoteFile) remoteFile); - FileStorageUtils.searchForLocalFileInDefaultPath(ocFile, account); + FileStorageUtils.searchForLocalFileInDefaultPath(ocFile, user.toPlatformAccount()); try { if (ExtendedListFragment.SearchType.PHOTO_SEARCH == searchType) { @@ -885,9 +890,9 @@ private void parseVirtuals(List objects, ExtendedListFragment.SearchType true, false, mStorageManager, - account, + user.toPlatformAccount(), mContext); - refreshFolderOperation.execute(account, mContext); + refreshFolderOperation.execute(user.toPlatformAccount(), mContext); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 3919351a31ef..4fc993e3003c 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -332,7 +332,7 @@ public void onActivityCreated(Bundle savedInstanceState) { mAdapter = new OCFileListAdapter( getActivity(), - accountManager.getCurrentAccount(), + accountManager.getUser(), preferences, accountManager, mContainerActivity, @@ -1180,7 +1180,7 @@ public void run() { } mAdapter.swapDirectory( - accountManager.getCurrentAccount(), + accountManager.getUser(), directory, storageManager, onlyOnDevice, diff --git a/src/test/java/com/nextcloud/client/network/ConnectivityServiceTest.kt b/src/test/java/com/nextcloud/client/network/ConnectivityServiceTest.kt index 23933fbfad53..e805d3555f62 100644 --- a/src/test/java/com/nextcloud/client/network/ConnectivityServiceTest.kt +++ b/src/test/java/com/nextcloud/client/network/ConnectivityServiceTest.kt @@ -19,16 +19,17 @@ */ package com.nextcloud.client.network -import android.accounts.Account import android.net.ConnectivityManager import android.net.NetworkInfo -import android.net.Uri +import com.nextcloud.client.account.Server +import com.nextcloud.client.account.User import com.nextcloud.client.account.UserAccountManager +import com.nextcloud.client.logger.Logger import com.nhaarman.mockitokotlin2.any import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.never import com.nhaarman.mockitokotlin2.verify import com.nhaarman.mockitokotlin2.whenever -import com.owncloud.android.lib.common.OwnCloudAccount import com.owncloud.android.lib.resources.status.OwnCloudVersion import org.apache.commons.httpclient.HttpClient import org.apache.commons.httpclient.HttpStatus @@ -42,6 +43,7 @@ import org.junit.runners.Suite import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.MockitoAnnotations +import java.net.URI @RunWith(Suite::class) @Suite.SuiteClasses( @@ -86,13 +88,14 @@ class ConnectivityServiceTest { lateinit var requestBuilder: ConnectivityServiceImpl.GetRequestBuilder @Mock - lateinit var platformAccount: Account + lateinit var logger: Logger - @Mock - lateinit var ownCloudAccount: OwnCloudAccount + val baseServerUri = URI.create(SERVER_BASE_URL) + val newServer = Server(baseServerUri, OwnCloudVersion.nextcloud_14) + val legacyServer = Server(baseServerUri, OwnCloudVersion.nextcloud_13) @Mock - lateinit var baseServerUri: Uri + lateinit var user: User lateinit var connectivityService: ConnectivityServiceImpl @@ -103,15 +106,15 @@ class ConnectivityServiceTest { platformConnectivityManager, accountManager, clientFactory, - requestBuilder + requestBuilder, + logger ) + whenever(platformConnectivityManager.activeNetworkInfo).thenReturn(networkInfo) whenever(requestBuilder.invoke(any())).thenReturn(getRequest) whenever(clientFactory.createPlainClient()).thenReturn(client) - whenever(accountManager.currentOwnCloudAccount).thenReturn(ownCloudAccount) - whenever(accountManager.currentAccount).thenReturn(platformAccount) - whenever(baseServerUri.toString()).thenReturn(SERVER_BASE_URL) - whenever(ownCloudAccount.baseUri).thenReturn(baseServerUri) + whenever(user.server).thenReturn(newServer) + whenever(accountManager.user).thenReturn(user) } } @@ -158,7 +161,7 @@ class ConnectivityServiceTest { fun setUp() { whenever(networkInfo.isConnectedOrConnecting).thenReturn(true) whenever(networkInfo.type).thenReturn(ConnectivityManager.TYPE_WIFI) - whenever(accountManager.getServerVersion(any())).thenReturn(OwnCloudVersion.nextcloud_13) + whenever(user.server).thenReturn(legacyServer) assertTrue("Precondition failed", connectivityService.isOnlineWithWifi) } @@ -207,6 +210,28 @@ class ConnectivityServiceTest { assertTrue("Precondition failed", connectivityService.isOnlineWithWifi) } + @Test + fun `check request is not sent when server uri is not set`() { + // GIVEN + // network connectivity is present + // user has no server URI (empty) + val serverWithoutUri = Server(URI(""), OwnCloudVersion.nextcloud_14) + whenever(user.server).thenReturn(serverWithoutUri) + + // WHEN + // connectivity is checked + val result = connectivityService.isInternetWalled + + // THEN + // connection is walled + // request is not sent + assertTrue("Server should not be accessible", result) + verify(requestBuilder, never()).invoke(any()) + verify(client, never()).executeMethod(any()) + verify(client, never()).executeMethod(any(), any()) + verify(client, never()).executeMethod(any(), any(), any()) + } + fun mockResponse(contentLength: Long = 0, status: Int = HttpStatus.SC_OK) { whenever(client.executeMethod(any())).thenReturn(status) whenever(getRequest.statusCode).thenReturn(status) From 6249a06a8dcd8e820a4ddd92027c27548b9c5068 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Sat, 23 Nov 2019 03:36:58 +0100 Subject: [PATCH 43/69] daily dev 20191123 c8eba1d5a Merge pull request #4835 from nextcloud/ezaquarii/new-user-model 609b99666 New user model 44220d73f Merge pull request #4867 from nextcloud/dependabot/gradle/androidx.exifinterface-exifinterface-1.1.0 6e5d7a89c daily dev 20191121 --- build.gradle | 4 ++-- .../fastlane/metadata/android/en-US/changelogs/20191123.txt | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191123.txt diff --git a/build.gradle b/build.gradle index edea4336c760..b3d3254d8b88 100644 --- a/build.gradle +++ b/build.gradle @@ -162,8 +162,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191121 - versionName "20191121" + versionCode 20191123 + versionName "20191123" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191123.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191123.txt new file mode 100644 index 000000000000..cd2e13307ba8 --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191123.txt @@ -0,0 +1,4 @@ +c8eba1d5a Merge pull request #4835 from nextcloud/ezaquarii/new-user-model +609b99666 New user model +44220d73f Merge pull request #4867 from nextcloud/dependabot/gradle/androidx.exifinterface-exifinterface-1.1.0 +6e5d7a89c daily dev 20191121 From 0691786fb80d11744b2946d21bfd1d4291324818 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2019 02:15:12 +0000 Subject: [PATCH 44/69] Bump flexbox from 1.1.1 to 2.0.0 Bumps [flexbox](https://github.com/google/flexbox-layout) from 1.1.1 to 2.0.0. - [Release notes](https://github.com/google/flexbox-layout/releases) - [Commits](https://github.com/google/flexbox-layout/compare/1.1.1...2.0.0) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b3d3254d8b88..cd30ea96c2e8 100644 --- a/build.gradle +++ b/build.gradle @@ -292,7 +292,7 @@ dependencies { implementation 'com.github.chrisbanes:PhotoView:2.3.0' implementation 'pl.droidsonroids.gif:android-gif-drawable:1.2.15' implementation 'com.github.tobiaskaminsky:qrcodescanner:0.1.2.2' // 'com.github.blikoon:QRCodeScanner:0.1.2' - implementation 'com.google.android:flexbox:1.1.1' + implementation 'com.google.android:flexbox:2.0.0' implementation 'org.parceler:parceler-api:1.1.12' kapt 'org.parceler:parceler:1.1.12' implementation('com.github.bumptech.glide:glide:3.8.0') { From 4f26261bf85cc51a23f255555616b5c61ef9ac75 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2019 02:15:52 +0000 Subject: [PATCH 45/69] Bump detekt-gradle-plugin from 1.1.1 to 1.2.0 Bumps detekt-gradle-plugin from 1.1.1 to 1.2.0. Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b3d3254d8b88..024c13970eb8 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ buildscript { } classpath 'gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.6' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.1.1" + classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.2.0" } } From a7eb7148fa0ceb42981366eb2ddcf0ff921e6a55 Mon Sep 17 00:00:00 2001 From: Chris Narkiewicz Date: Mon, 18 Nov 2019 19:08:54 +0000 Subject: [PATCH 46/69] Migrate simple cases of getCurrentAccount() to getUser() Migrate trivially convertible uses of getCurrentAccount() to new user model - getUser(). Signed-off-by: Chris Narkiewicz --- .../nextcloud/client/account/AnonymousUser.kt | 1 + .../client/account/RegisteredUser.kt | 1 + .../java/com/nextcloud/client/account/User.kt | 1 + .../client/account/UserAccountManager.java | 14 +++ .../com/nextcloud/client/di/AppModule.java | 5 +- .../client/network/ClientFactory.java | 22 ++++ .../client/network/ClientFactoryImpl.java | 13 ++ .../preferences/AppPreferencesImpl.java | 42 +++---- .../authentication/AuthenticatorActivity.java | 5 +- .../datamodel/FileDataStorageManager.java | 12 +- .../DiskLruImageCacheFileProvider.java | 5 +- .../UsersAndGroupsSearchProvider.java | 15 ++- .../ui/activities/ActivitiesActivity.java | 3 +- .../activities/ActivitiesServiceApiImpl.java | 8 +- .../data/files/FilesServiceApiImpl.java | 52 +++----- .../android/ui/activity/BaseActivity.java | 17 ++- .../android/ui/activity/DrawerActivity.java | 74 ++++++----- .../ui/activity/FileDisplayActivity.java | 18 ++- .../ui/activity/ManageAccountsActivity.java | 10 +- .../ui/activity/NotificationsActivity.java | 53 ++++---- .../ui/activity/RichDocumentsWebView.java | 8 +- .../android/ui/activity/SettingsActivity.java | 26 ++-- .../ui/activity/SyncedFoldersActivity.java | 23 ++-- .../ui/activity/UploadListActivity.java | 8 +- .../ui/adapter/ActivityListAdapter.java | 15 ++- .../android/ui/adapter/TemplateAdapter.java | 9 +- .../ui/adapter/TrashbinListAdapter.java | 12 +- .../ui/asynctasks/PhotoSearchTask.java | 10 +- .../dialog/ChooseTemplateDialogFragment.java | 15 ++- .../ui/fragment/ExtendedListFragment.java | 4 +- .../FileDetailActivitiesFragment.java | 30 ++--- .../ui/fragment/OCFileListFragment.java | 54 +++----- .../android/ui/fragment/PhotoFragment.java | 2 +- .../contactsbackup/ContactListFragment.java | 10 +- .../ui/helpers/FileOperationsHelper.java | 13 +- .../ui/preview/PreviewImageActivity.java | 5 +- .../ui/trashbin/RemoteTrashbinRepository.java | 115 +++++++++++------- .../android/ui/trashbin/TrashbinActivity.java | 21 ++-- .../owncloud/android/utils/DisplayUtils.java | 8 +- .../utils/glide/CustomGlideStreamLoader.java | 7 +- .../utils/glide/CustomGlideUriLoader.java | 7 +- .../utils/glide/HttpStreamFetcher.java | 18 +-- src/main/res/values/strings.xml | 3 - 43 files changed, 445 insertions(+), 349 deletions(-) diff --git a/src/main/java/com/nextcloud/client/account/AnonymousUser.kt b/src/main/java/com/nextcloud/client/account/AnonymousUser.kt index d949cf5bf393..ec2a284e2cbc 100644 --- a/src/main/java/com/nextcloud/client/account/AnonymousUser.kt +++ b/src/main/java/com/nextcloud/client/account/AnonymousUser.kt @@ -46,6 +46,7 @@ internal class AnonymousUser(private val accountType: String) : User { override val accountName: String = "anonymous" override val server = Server(URI.create(""), MainApp.MINIMUM_SUPPORTED_SERVER_VERSION) + override val isAnonymous = true override fun toPlatformAccount(): Account { return Account(accountName, accountType) diff --git a/src/main/java/com/nextcloud/client/account/RegisteredUser.kt b/src/main/java/com/nextcloud/client/account/RegisteredUser.kt index 7a8a5f6314ab..4ba956552137 100644 --- a/src/main/java/com/nextcloud/client/account/RegisteredUser.kt +++ b/src/main/java/com/nextcloud/client/account/RegisteredUser.kt @@ -31,6 +31,7 @@ internal class RegisteredUser( private val ownCloudAccount: OwnCloudAccount, override val server: Server ) : User { + override val isAnonymous = false override val accountName: String get() { return account.name diff --git a/src/main/java/com/nextcloud/client/account/User.kt b/src/main/java/com/nextcloud/client/account/User.kt index 408797d07965..0500d8358a9a 100644 --- a/src/main/java/com/nextcloud/client/account/User.kt +++ b/src/main/java/com/nextcloud/client/account/User.kt @@ -26,6 +26,7 @@ import com.owncloud.android.lib.common.OwnCloudAccount interface User { val accountName: String val server: Server + val isAnonymous: Boolean /** * This is temporary helper method created to facilitate incremental refactoring. diff --git a/src/main/java/com/nextcloud/client/account/UserAccountManager.java b/src/main/java/com/nextcloud/client/account/UserAccountManager.java index 0b217073e24c..d4394423a564 100644 --- a/src/main/java/com/nextcloud/client/account/UserAccountManager.java +++ b/src/main/java/com/nextcloud/client/account/UserAccountManager.java @@ -97,10 +97,24 @@ public interface UserAccountManager extends CurrentAccountProvider { * @return Version of the OC server corresponding to account, according to the data saved * in the system AccountManager */ + @Deprecated @NonNull OwnCloudVersion getServerVersion(Account account); + @Deprecated boolean isSearchSupported(@Nullable Account account); + + /** + * Check if user's account supports media streaming. This is a property of server where user has his account. + * + * @deprecated Please use {@link OwnCloudVersion#isMediaStreamingSupported()} directly, + * obtainable from {@link User#getServer()} and {@link Server#getVersion()} + * + * @param account Account used to perform {@link android.accounts.AccountManager} lookup. + * + * @return true is server supports media streaming, false otherwise + */ + @Deprecated boolean isMediaStreamingSupported(@Nullable Account account); void resetOwnCloudAccount(); diff --git a/src/main/java/com/nextcloud/client/di/AppModule.java b/src/main/java/com/nextcloud/client/di/AppModule.java index f9199705240c..64c96123b448 100644 --- a/src/main/java/com/nextcloud/client/di/AppModule.java +++ b/src/main/java/com/nextcloud/client/di/AppModule.java @@ -41,6 +41,7 @@ import com.nextcloud.client.logger.Logger; import com.nextcloud.client.logger.LoggerImpl; import com.nextcloud.client.logger.LogsRepository; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.ui.activities.data.activities.ActivitiesRepository; @@ -106,8 +107,8 @@ ActivitiesRepository activitiesRepository(ActivitiesServiceApi api) { } @Provides - FilesRepository filesRepository(UserAccountManager accountManager) { - return new RemoteFilesRepository(new FilesServiceApiImpl(accountManager)); + FilesRepository filesRepository(UserAccountManager accountManager, ClientFactory clientFactory) { + return new RemoteFilesRepository(new FilesServiceApiImpl(accountManager, clientFactory)); } @Provides diff --git a/src/main/java/com/nextcloud/client/network/ClientFactory.java b/src/main/java/com/nextcloud/client/network/ClientFactory.java index affbb34c79bc..9f8694bab25f 100644 --- a/src/main/java/com/nextcloud/client/network/ClientFactory.java +++ b/src/main/java/com/nextcloud/client/network/ClientFactory.java @@ -26,6 +26,7 @@ import android.app.Activity; import android.net.Uri; +import com.nextcloud.client.account.User; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -35,10 +36,31 @@ public interface ClientFactory { + /** + * This exception wraps all possible errors thrown by trigger-happy + * OwnCloudClient constructor, making try-catch blocks manageable. + * + * This is a temporary refactoring measure, until a better + * error handling method can be procured. + */ + @Deprecated + class CreationException extends Exception { + + private static final long serialVersionUID = 0L; + + CreationException(Throwable t) { + super(t); + } + } + + OwnCloudClient create(User user) throws CreationException; + + @Deprecated OwnCloudClient create(Account account) throws OperationCanceledException, AuthenticatorException, IOException, AccountUtils.AccountNotFoundException; + @Deprecated OwnCloudClient create(Account account, Activity currentActivity) throws OperationCanceledException, AuthenticatorException, IOException, AccountUtils.AccountNotFoundException; diff --git a/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java b/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java index 467d98ebc0b6..ff88a8487f9b 100644 --- a/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java +++ b/src/main/java/com/nextcloud/client/network/ClientFactoryImpl.java @@ -27,6 +27,7 @@ import android.content.Context; import android.net.Uri; +import com.nextcloud.client.account.User; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; @@ -41,6 +42,18 @@ class ClientFactoryImpl implements ClientFactory { this.context = context; } + @Override + public OwnCloudClient create(User user) throws CreationException { + try { + return OwnCloudClientFactory.createOwnCloudClient(user.toPlatformAccount(), context); + } catch (OperationCanceledException| + AuthenticatorException| + IOException| + AccountUtils.AccountNotFoundException e) { + throw new CreationException(e); + } + } + @Override public OwnCloudClient create(Account account) throws OperationCanceledException, AuthenticatorException, IOException, diff --git a/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java b/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java index 1ac83f887386..60867fabf047 100644 --- a/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java +++ b/src/main/java/com/nextcloud/client/preferences/AppPreferencesImpl.java @@ -27,6 +27,7 @@ import android.content.SharedPreferences; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManagerImpl; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -282,7 +283,7 @@ public boolean isFingerprintUnlockEnabled() { @Override public String getFolderLayout(OCFile folder) { return getFolderPreference(context, - currentAccountProvider.getCurrentAccount(), + currentAccountProvider.getUser(), PREF__FOLDER_LAYOUT, folder, FOLDER_LAYOUT_LIST); @@ -291,7 +292,7 @@ public String getFolderLayout(OCFile folder) { @Override public void setFolderLayout(OCFile folder, String layout_name) { setFolderPreference(context, - currentAccountProvider.getCurrentAccount(), + currentAccountProvider.getUser(), PREF__FOLDER_LAYOUT, folder, layout_name); @@ -300,7 +301,7 @@ public void setFolderLayout(OCFile folder, String layout_name) { @Override public FileSortOrder getSortOrderByFolder(OCFile folder) { return FileSortOrder.sortOrders.get(getFolderPreference(context, - currentAccountProvider.getCurrentAccount(), + currentAccountProvider.getUser(), PREF__FOLDER_SORT_ORDER, folder, FileSortOrder.sort_a_to_z.name)); @@ -309,7 +310,7 @@ public FileSortOrder getSortOrderByFolder(OCFile folder) { @Override public void setSortOrder(OCFile folder, FileSortOrder sortOrder) { setFolderPreference(context, - currentAccountProvider.getCurrentAccount(), + currentAccountProvider.getUser(), PREF__FOLDER_SORT_ORDER, folder, sortOrder.name); @@ -322,28 +323,23 @@ public FileSortOrder getSortOrderByType(FileSortOrder.Type type) { @Override public FileSortOrder getSortOrderByType(FileSortOrder.Type type, FileSortOrder defaultOrder) { - Account account = currentAccountProvider.getCurrentAccount(); - if (account == null) { + User user = currentAccountProvider.getUser(); + if (user.isAnonymous()) { return defaultOrder; } ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver()); - String value = dataProvider.getValue(account.name, PREF__FOLDER_SORT_ORDER + "_" + type); + String value = dataProvider.getValue(user.getAccountName(), PREF__FOLDER_SORT_ORDER + "_" + type); return value.isEmpty() ? defaultOrder : FileSortOrder.sortOrders.get(value); } @Override public void setSortOrder(FileSortOrder.Type type, FileSortOrder sortOrder) { - Account account = currentAccountProvider.getCurrentAccount(); - - if (account == null) { - throw new IllegalArgumentException("Account may not be null!"); - } - + User user = currentAccountProvider.getUser(); ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver()); - dataProvider.storeOrUpdateKeyValue(account.name, PREF__FOLDER_SORT_ORDER + "_" + type, sortOrder.name); + dataProvider.storeOrUpdateKeyValue(user.getAccountName(), PREF__FOLDER_SORT_ORDER + "_" + type, sortOrder.name); } @Override @@ -576,22 +572,22 @@ public long getPhotoSearchTimestamp() { * @return Preference value */ private static String getFolderPreference(final Context context, - final Account account, + final User user, final String preferenceName, final OCFile folder, final String defaultValue) { - if (account == null) { + if (user.isAnonymous()) { return defaultValue; } ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver()); - FileDataStorageManager storageManager = new FileDataStorageManager(account, context.getContentResolver()); + FileDataStorageManager storageManager = new FileDataStorageManager(user.toPlatformAccount(), context.getContentResolver()); - String value = dataProvider.getValue(account.name, getKeyFromFolder(preferenceName, folder)); + String value = dataProvider.getValue(user.getAccountName(), getKeyFromFolder(preferenceName, folder)); OCFile prefFolder = folder; while (prefFolder != null && value.isEmpty()) { prefFolder = storageManager.getFileById(prefFolder.getParentId()); - value = dataProvider.getValue(account.name, getKeyFromFolder(preferenceName, prefFolder)); + value = dataProvider.getValue(user.getAccountName(), getKeyFromFolder(preferenceName, prefFolder)); } return value.isEmpty() ? defaultValue : value; } @@ -605,16 +601,12 @@ private static String getFolderPreference(final Context context, * @param value Preference value to set. */ private static void setFolderPreference(final Context context, - final Account account, + final User user, final String preferenceName, final OCFile folder, final String value) { - if (account == null) { - throw new IllegalArgumentException("Account may not be null!"); - } - ArbitraryDataProvider dataProvider = new ArbitraryDataProvider(context.getContentResolver()); - dataProvider.storeOrUpdateKeyValue(account.name, getKeyFromFolder(preferenceName, folder), value); + dataProvider.storeOrUpdateKeyValue(user.getAccountName(), getKeyFromFolder(preferenceName, folder), value); } private static String getKeyFromFolder(String preferenceName, OCFile folder) { diff --git a/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java b/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java index a83d1bb7a919..de9551e416b8 100644 --- a/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java +++ b/src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java @@ -92,6 +92,7 @@ import com.blikoon.qrcodescanner.QrCodeActivity; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.DeviceInfo; import com.nextcloud.client.di.Injectable; @@ -1735,8 +1736,8 @@ protected boolean createAccount(RemoteOperationResult authResult) { } /// add the new account as default in preferences, if there is none already - Account defaultAccount = accountManager.getCurrentAccount(); - if (defaultAccount == null) { + User defaultAccount = accountManager.getUser(); + if (defaultAccount.isAnonymous()) { SharedPreferences.Editor editor = PreferenceManager.getDefaultSharedPreferences(this).edit(); editor.putString("select_oc_account", accountName); editor.apply(); diff --git a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java index aca96b5ff3f4..28bd0e5411ba 100644 --- a/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/FileDataStorageManager.java @@ -1259,8 +1259,8 @@ private OCShare createShareInstance(Cursor c) { private void resetShareFlagsInAllFiles() { ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false); - cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false); + cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); + cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, ""); String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + "=?"; String[] whereArgs = new String[]{account.name}; @@ -1279,8 +1279,8 @@ private void resetShareFlagsInAllFiles() { private void resetShareFlagsInFolder(OCFile folder) { ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false); - cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false); + cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); + cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, ""); String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PARENT + "=?"; String[] whereArgs = new String[]{account.name, String.valueOf(folder.getFileId())}; @@ -1299,8 +1299,8 @@ private void resetShareFlagsInFolder(OCFile folder) { private void resetShareFlagInAFile(String filePath) { ContentValues cv = new ContentValues(); - cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, false); - cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, false); + cv.put(ProviderTableMeta.FILE_SHARED_VIA_LINK, Boolean.FALSE); + cv.put(ProviderTableMeta.FILE_SHARED_WITH_SHAREE, Boolean.FALSE); cv.put(ProviderTableMeta.FILE_PUBLIC_LINK, ""); String where = ProviderTableMeta.FILE_ACCOUNT_OWNER + AND + ProviderTableMeta.FILE_PATH + "=?"; String[] whereArgs = new String[]{account.name, filePath}; diff --git a/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java b/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java index 0c17aeb4df87..cf398a425cbf 100644 --- a/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java +++ b/src/main/java/com/owncloud/android/providers/DiskLruImageCacheFileProvider.java @@ -31,6 +31,7 @@ import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.owncloud.android.MainApp; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -61,8 +62,8 @@ public boolean onCreate() { } private OCFile getFile(Uri uri) { - Account account = accountManager.getCurrentAccount(); - FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(account, + User user = accountManager.getUser(); + FileDataStorageManager fileDataStorageManager = new FileDataStorageManager(user.toPlatformAccount(), MainApp.getAppContext().getContentResolver()); return fileDataStorageManager.getFileByPath(uri.getPath()); diff --git a/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java b/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java index acc79b695ade..0daa551ab90a 100644 --- a/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java +++ b/src/main/java/com/owncloud/android/providers/UsersAndGroupsSearchProvider.java @@ -34,6 +34,7 @@ import android.provider.BaseColumns; import android.widget.Toast; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -180,18 +181,14 @@ private Cursor searchForUsersOrGroups(Uri uri) { // need to trust on the AccountUtils to get the current account since the query in the client side is not // directly started by our code, but from SearchView implementation - Account account = accountManager.getCurrentAccount(); - - if (account == null) { - throw new IllegalArgumentException("Account may not be null!"); - } + User user = accountManager.getUser(); String userQuery = lastPathSegment.toLowerCase(Locale.ROOT); // request to the OC server about users and groups matching userQuery GetShareesRemoteOperation searchRequest = new GetShareesRemoteOperation(userQuery, REQUESTED_PAGE, RESULTS_PER_PAGE); - RemoteOperationResult result = searchRequest.execute(account, getContext()); + RemoteOperationResult result = searchRequest.execute(user.toPlatformAccount(), getContext()); List names = new ArrayList<>(); if (result.isSuccess()) { @@ -217,8 +214,10 @@ private Cursor searchForUsersOrGroups(Uri uri) { Uri remoteBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_REMOTE).build(); Uri emailBaseUri = new Uri.Builder().scheme(CONTENT).authority(DATA_EMAIL).build(); - FileDataStorageManager manager = new FileDataStorageManager(account, getContext().getContentResolver()); - boolean federatedShareAllowed = manager.getCapability(account.name).getFilesSharingFederationOutgoing() + FileDataStorageManager manager = new FileDataStorageManager(user.toPlatformAccount(), + getContext().getContentResolver()); + boolean federatedShareAllowed = manager.getCapability(user.getAccountName()) + .getFilesSharingFederationOutgoing() .isTrue(); try { diff --git a/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java b/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java index 7d06866c6ef9..4c36bce115b8 100644 --- a/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activities/ActivitiesActivity.java @@ -169,7 +169,8 @@ private void setupContent() { PorterDuff.Mode.SRC_IN); FileDataStorageManager storageManager = new FileDataStorageManager(getAccount(), getContentResolver()); - adapter = new ActivityListAdapter(this, getUserAccountManager(), this, storageManager, getCapabilities(), false); + adapter = new ActivityListAdapter(this, getUserAccountManager(), this, storageManager, + getCapabilities(), false); recyclerView.setAdapter(adapter); LinearLayoutManager layoutManager = new LinearLayoutManager(this); diff --git a/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java b/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java index 450fbcc9cc40..360c2458881e 100644 --- a/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java +++ b/src/main/java/com/owncloud/android/ui/activities/data/activities/ActivitiesServiceApiImpl.java @@ -28,6 +28,7 @@ import android.content.Context; import android.os.AsyncTask; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -58,8 +59,11 @@ public ActivitiesServiceApiImpl(UserAccountManager accountManager) { @Override public void getAllActivities(int lastGiven, ActivitiesServiceCallback> callback) { - Account account = accountManager.getCurrentAccount(); - GetActivityListTask getActivityListTask = new GetActivityListTask(account, accountManager, lastGiven, callback); + User user = accountManager.getUser(); + GetActivityListTask getActivityListTask = new GetActivityListTask(user.toPlatformAccount(), + accountManager, + lastGiven, + callback); getActivityListTask.execute(); } diff --git a/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java b/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java index 892d05a4cf25..8492a0751a4f 100644 --- a/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java +++ b/src/main/java/com/owncloud/android/ui/activities/data/files/FilesServiceApiImpl.java @@ -22,19 +22,16 @@ */ package com.owncloud.android.ui.activities.data.files; -import android.accounts.Account; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.content.Context; import android.os.AsyncTask; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -44,8 +41,6 @@ import com.owncloud.android.ui.activity.BaseActivity; import com.owncloud.android.utils.FileStorageUtils; -import java.io.IOException; - /** * Implementation of the Files service API that communicates with the NextCloud remote server. */ @@ -54,15 +49,18 @@ public class FilesServiceApiImpl implements FilesServiceApi { private static final String TAG = FilesServiceApiImpl.class.getSimpleName(); private UserAccountManager accountManager; + private ClientFactory clientFactory; - public FilesServiceApiImpl(UserAccountManager accountManager) { + public FilesServiceApiImpl(UserAccountManager accountManager, ClientFactory clientFactory) { this.accountManager = accountManager; + this.clientFactory = clientFactory; } @Override public void readRemoteFile(String fileUrl, BaseActivity activity, FilesServiceCallback callback) { ReadRemoteFileTask readRemoteFileTask = new ReadRemoteFileTask( accountManager, + clientFactory, fileUrl, activity, callback @@ -77,30 +75,29 @@ private static class ReadRemoteFileTask extends AsyncTask // TODO: Figure out a better way to do this than passing a BaseActivity reference. private final BaseActivity baseActivity; private final String fileUrl; - private final Account account; + private final User user; private final UserAccountManager accountManager; + private final ClientFactory clientFactory; private ReadRemoteFileTask(UserAccountManager accountManager, + ClientFactory clientFactory, String fileUrl, BaseActivity baseActivity, FilesServiceCallback callback) { this.callback = callback; this.baseActivity = baseActivity; this.fileUrl = fileUrl; - this.account = accountManager.getCurrentAccount(); + this.user = accountManager.getUser(); this.accountManager = accountManager; + this.clientFactory = clientFactory; } @Override protected Boolean doInBackground(Void... voids) { final Context context = MainApp.getAppContext(); - OwnCloudAccount ocAccount; - OwnCloudClient ownCloudClient; try { - ocAccount = new OwnCloudAccount(account, context); - ownCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - ownCloudClient.setOwnCloudVersion(accountManager.getServerVersion(account)); + OwnCloudClient ownCloudClient = clientFactory.create(user); + ownCloudClient.setOwnCloudVersion(user.getServer().getVersion()); // always update file as it could be an old state saved in database RemoteOperationResult resultRemoteFileOp = new ReadFileRemoteOperation(fileUrl).execute(ownCloudClient); @@ -111,28 +108,19 @@ protected Boolean doInBackground(Void... voids) { if (remoteOcFile.isFolder()) { // perform folder synchronization RemoteOperation synchFolderOp = new RefreshFolderOperation(remoteOcFile, - System.currentTimeMillis(), - false, - true, - baseActivity.getStorageManager(), - baseActivity.getAccount(), - context); + System.currentTimeMillis(), + false, + true, + baseActivity.getStorageManager(), + baseActivity.getAccount(), + context); synchFolderOp.execute(ownCloudClient); } } return true; - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { + } catch (ClientFactory.CreationException e) { Log_OC.e(TAG, "Account not found", e); errorMessage = baseActivity.getString(R.string.account_not_found); - } catch (IOException e) { - Log_OC.e(TAG, "IO error", e); - errorMessage = baseActivity.getString(R.string.io_error); - } catch (OperationCanceledException e) { - Log_OC.e(TAG, "Operation has been canceled", e); - errorMessage = baseActivity.getString(R.string.operation_canceled); - } catch (AuthenticatorException e) { - Log_OC.e(TAG, "Authentication Exception", e); - errorMessage = baseActivity.getString(R.string.authentication_exception); } return false; diff --git a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java index 92cda745b2cd..327bb103873d 100644 --- a/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/BaseActivity.java @@ -6,17 +6,16 @@ import android.accounts.AccountManagerFuture; import android.accounts.OperationCanceledException; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; import android.os.PersistableBundle; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; -import com.nextcloud.client.preferences.AppPreferencesImpl; +import com.nextcloud.java.util.Optional; import com.owncloud.android.MainApp; -import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.lib.common.utils.Log_OC; @@ -161,6 +160,10 @@ protected void setAccount(Account account, boolean savedAccount) { } } + protected void setUser(User user) { + setAccount(user.toPlatformAccount(), false); + } + /** * Tries to swap the current ownCloud {@link Account} for other valid and existing. * @@ -215,6 +218,14 @@ public OCCapability getCapabilities() { public Account getAccount() { return currentAccount; } + + public Optional getUser() { + if (currentAccount != null) { + return accountManager.getUser(currentAccount.name); + } else { + return Optional.empty(); + } + } public FileDataStorageManager getStorageManager() { return storageManager; diff --git a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java index 18e368137ae6..5984380b1f6a 100644 --- a/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/DrawerActivity.java @@ -56,8 +56,10 @@ import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.SimpleTarget; import com.google.android.material.navigation.NavigationView; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.onboarding.FirstRunActivity; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; @@ -217,6 +219,9 @@ public abstract class DrawerActivity extends ToolbarActivity @Inject AppPreferences preferences; + @Inject + ClientFactory clientFactory; + /** * Initializes the drawer, its content and highlights the menu item with the given id. * This method needs to be called after the content view has been set. @@ -357,21 +362,19 @@ public void run() { navigationView.getMenu().setGroupVisible(R.id.drawer_menu_accounts, false); } - Account account = accountManager.getCurrentAccount(); + User account = accountManager.getUser(); filterDrawerMenu(navigationView.getMenu(), account); } - private void filterDrawerMenu(Menu menu, Account account) { - OCCapability capability = null; - if (account != null) { - FileDataStorageManager storageManager = new FileDataStorageManager(account, getContentResolver()); - capability = storageManager.getCapability(account.name); - } + private void filterDrawerMenu(final Menu menu, @NonNull final User user) { + FileDataStorageManager storageManager = new FileDataStorageManager(user.toPlatformAccount(), + getContentResolver()); + OCCapability capability = storageManager.getCapability(user.getAccountName()); - boolean hasSearchSupport = accountManager.getServerVersion(account).isSearchSupported(); + boolean hasSearchSupport = user.getServer().getVersion().isSearchSupported(); - DrawerMenuUtil.filterSearchMenuItems(menu, account, getResources(), hasSearchSupport); - DrawerMenuUtil.filterTrashbinMenuItem(menu, account, capability, accountManager); + DrawerMenuUtil.filterSearchMenuItems(menu, user.toPlatformAccount(), getResources(), hasSearchSupport); + DrawerMenuUtil.filterTrashbinMenuItem(menu, user.toPlatformAccount(), capability, accountManager); DrawerMenuUtil.filterActivityMenuItem(menu, capability); DrawerMenuUtil.setupHomeMenuItem(menu, getResources()); @@ -685,7 +688,7 @@ public void updateAccountList() { if (mNavigationView != null && mDrawerLayout != null) { if (persistingAccounts.size() > 0) { repopulateAccountList(persistingAccounts); - setAccountInDrawer(accountManager.getCurrentAccount()); + setAccountInDrawer(accountManager.getUser()); populateDrawerOwnCloudAccounts(); // activate second/end account avatar @@ -791,30 +794,25 @@ protected void updateActionBarTitleAndHomeButton(OCFile chosenFile) { * sets the given account name in the drawer in case the drawer is available. The account name is shortened * beginning from the @-sign in the username. * - * @param account the account to be set in the drawer + * @param user the account to be set in the drawer */ - protected void setAccountInDrawer(Account account) { - if (mDrawerLayout != null && account != null) { + protected void setAccountInDrawer(User user) { + if (mDrawerLayout != null && user != null) { TextView username = (TextView) findNavigationViewChildById(R.id.drawer_username); TextView usernameFull = (TextView) findNavigationViewChildById(R.id.drawer_username_full); - usernameFull.setText(DisplayUtils.convertIdn(account.name.substring(account.name.lastIndexOf('@') + 1), + String name = user.getAccountName(); + usernameFull.setText(DisplayUtils.convertIdn(name.substring(name.lastIndexOf('@') + 1), false)); usernameFull.setTextColor(ThemeUtils.fontColor(this)); - try { - OwnCloudAccount oca = new OwnCloudAccount(account, this); - username.setText(oca.getDisplayName()); - username.setTextColor(ThemeUtils.fontColor(this)); - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { - Log_OC.w(TAG, "Couldn't read display name of account fallback to account name"); - username.setText(UserAccountManager.getUsername(account)); - } + username.setText(user.toOwnCloudAccount().getDisplayName()); + username.setTextColor(ThemeUtils.fontColor(this)); View currentAccountView = findNavigationViewChildById(R.id.drawer_current_account); - currentAccountView.setTag(account.name); + currentAccountView.setTag(name); - DisplayUtils.setAvatar(account, this, mCurrentAccountAvatarRadiusDimension, getResources(), + DisplayUtils.setAvatar(user.toPlatformAccount(), this, mCurrentAccountAvatarRadiusDimension, getResources(), currentAccountView, this); // check and show quota info if available @@ -958,6 +956,7 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { }; DisplayUtils.downloadIcon(getUserAccountManager(), + clientFactory, this, firstQuota.iconUrl, target, @@ -1024,14 +1023,14 @@ private void getAndDisplayUserQuota() { // set user space information Thread t = new Thread(new Runnable() { public void run() { - final Account currentAccount = accountManager.getCurrentAccount(); + final User user = accountManager.getUser(); - if (currentAccount == null) { + if (user.isAnonymous()) { return; } final Context context = MainApp.getAppContext(); - RemoteOperationResult result = new GetUserInfoRemoteOperation().execute(currentAccount, context); + RemoteOperationResult result = new GetUserInfoRemoteOperation().execute(user.toPlatformAccount(), context); if (result.isSuccess() && result.getData() != null) { final UserInfo userInfo = (UserInfo) result.getData().get(0); @@ -1101,6 +1100,7 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { }; DisplayUtils.downloadIcon(getUserAccountManager(), + clientFactory, this, link.iconUrl, target, @@ -1375,12 +1375,11 @@ private void populateDrawerOwnCloudAccounts() { } } - Account currentAccount = accountManager.getCurrentAccount(); - - mAvatars[0] = currentAccount; + User user = accountManager.getUser(); + mAvatars[0] = user.toPlatformAccount(); int j = 0; for (int i = 1; i <= 2 && i < persistingAccounts.size() && j < persistingAccounts.size(); j++) { - if (!currentAccount.equals(persistingAccounts.get(j))) { + if (!user.equals(persistingAccounts.get(j))) { mAvatars[i] = persistingAccounts.get(j); i++; } @@ -1462,11 +1461,10 @@ && getStorageManager() != null) { getCapabilities.execute(getStorageManager(), getBaseContext()); } - Account account = accountManager.getCurrentAccount(); - - if (account != null && getStorageManager() != null && - getStorageManager().getCapability(account.name) != null && - getStorageManager().getCapability(account.name).getExternalLinks().isTrue()) { + User user = accountManager.getUser(); + String name = user.getAccountName(); + if (getStorageManager() != null && getStorageManager().getCapability(name) != null && + getStorageManager().getCapability(name).getExternalLinks().isTrue()) { int count = arbitraryDataProvider.getIntegerValue(FilesSyncHelper.GLOBAL, FileActivity.APP_OPENED_COUNT); @@ -1481,7 +1479,7 @@ && getStorageManager() != null) { Log_OC.d("ExternalLinks", "update via api"); RemoteOperation getExternalLinksOperation = new ExternalLinksOperation(); - RemoteOperationResult result = getExternalLinksOperation.execute(account, this); + RemoteOperationResult result = getExternalLinksOperation.execute(user.toPlatformAccount(), this); if (result.isSuccess() && result.getData() != null) { externalLinksProvider.deleteAllExternalLinks(); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index f083396db1a3..84df7e4fd4db 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -54,11 +54,13 @@ import android.view.ViewTreeObserver; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.User; import com.nextcloud.client.appinfo.AppInfo; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.media.PlayerServiceConnection; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.java.util.Optional; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -2545,8 +2547,8 @@ public void onMessageEvent(TokenPushEvent event) { @Override public void onStart() { super.onStart(); - final Account account = getAccount(); - if (account != null) { + Optional optionalUser = getUser(); + if (optionalUser.isPresent()) { /// Check whether the 'main' OCFile handled by the Activity is contained in the // current Account OCFile file = getFile(); @@ -2572,10 +2574,12 @@ public void onStart() { } setFile(file); - setAccountInDrawer(account); + User user = optionalUser.get(); + setAccountInDrawer(user); setupDrawer(); - final boolean accountChanged = !account.equals(mLastDisplayedAccount); + final String lastDisplayedAccountName = mLastDisplayedAccount != null ? mLastDisplayedAccount.name : null; + final boolean accountChanged = !user.getAccountName().equals(lastDisplayedAccountName); if (accountChanged) { Log_OC.d(TAG, "Initializing Fragments in onAccountChanged.."); initFragmentsWithFile(); @@ -2587,7 +2591,11 @@ public void onStart() { updateActionBarTitleAndHomeButton(file.isFolder() ? null : file); } } - mLastDisplayedAccount = account; + if (optionalUser.isPresent()) { + mLastDisplayedAccount = optionalUser.get().toPlatformAccount(); + } else { + mLastDisplayedAccount = null; + } EventBus.getDefault().post(new TokenPushEvent()); checkForNewDevVersionNecessary(findViewById(R.id.root_layout), getApplicationContext()); diff --git a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 4108c000735e..531f448900e7 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -38,6 +38,7 @@ import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.onboarding.FirstRunActivity; import com.owncloud.android.MainApp; @@ -198,11 +199,11 @@ private boolean hasAccountListChanged() { * @return true if account list has changed, false if not */ private boolean hasCurrentAccountChanged() { - Account account = getUserAccountManager().getCurrentAccount(); - if (account == null) { + User account = getUserAccountManager().getUser(); + if (account.isAnonymous()) { return true; } else { - return !account.name.equals(originalCurrentAccount); + return !account.getAccountName().equals(originalCurrentAccount); } } @@ -320,7 +321,8 @@ public void run(AccountManagerFuture future) { } } - if (getUserAccountManager().getCurrentAccount() == null) { + User user = getUserAccountManager().getUser(); + if (user.isAnonymous()) { String accountName = ""; Account[] accounts = AccountManager.get(this).getAccountsByType(MainApp.getAccountType(this)); if (accounts.length != 0) { diff --git a/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java b/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java index 46792c86afbc..261d12497aee 100644 --- a/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/NotificationsActivity.java @@ -24,9 +24,6 @@ package com.owncloud.android.ui.activity; -import android.accounts.Account; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.content.Intent; import android.graphics.PorterDuff; import android.os.Bundle; @@ -39,13 +36,14 @@ import android.widget.TextView; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.network.ClientFactory; +import com.nextcloud.java.util.Optional; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.jobs.NotificationJob; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -58,9 +56,10 @@ import com.owncloud.android.utils.PushUtils; import com.owncloud.android.utils.ThemeUtils; -import java.io.IOException; import java.util.List; +import javax.inject.Inject; + import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; @@ -109,7 +108,9 @@ public class NotificationsActivity extends FileActivity implements Notifications private NotificationListAdapter adapter; private Snackbar snackbar; private OwnCloudClient client; - private Account currentAccount; + private Optional optionalUser; + + @Inject ClientFactory clientFactory; @Override protected void onCreate(Bundle savedInstanceState) { @@ -119,16 +120,18 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.notifications_layout); unbinder = ButterKnife.bind(this); - currentAccount = getAccount(); + optionalUser = getUser(); // use account from intent (opened via android notification can have a different account than current one) if (getIntent() != null && getIntent().getExtras() != null) { - String account = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT); - - if (account != null && (currentAccount == null || !account.equalsIgnoreCase(currentAccount.name))) { - accountManager.setCurrentOwnCloudAccount(account); - setAccount(getUserAccountManager().getCurrentAccount(), false); - currentAccount = getAccount(); + String accountName = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT); + if(accountName != null && optionalUser.isPresent()) { + User user = optionalUser.get(); + if (user.getAccountName().equalsIgnoreCase(accountName)) { + accountManager.setCurrentOwnCloudAccount(accountName); + setUser(getUserAccountManager().getUser()); + optionalUser = getUser(); + } } } @@ -142,7 +145,7 @@ protected void onCreate(Bundle savedInstanceState) { setupDrawer(R.id.nav_notifications); ThemeUtils.setColoredTitle(getSupportActionBar(), getString(R.string.drawer_item_notifications), this); - if (currentAccount == null) { + if (!optionalUser.isPresent()) { // show error runOnUiThread(() -> setEmptyContent(noResultsHeadline, getString(R.string.account_not_found))); return; @@ -156,7 +159,6 @@ protected void onCreate(Bundle savedInstanceState) { swipeEmptyListRefreshLayout.setOnRefreshListener(() -> { setLoadingMessage(); fetchAndSetData(); - }); setupPushWarning(); @@ -175,16 +177,16 @@ private void setupPushWarning() { snackbar = Snackbar.make(emptyContentContainer, R.string.push_notifications_not_implemented, Snackbar.LENGTH_INDEFINITE); } else { - ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver()); - - boolean usesOldLogin = arbitraryDataProvider.getBooleanValue(currentAccount.name, + final ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver()); + final String accountName = optionalUser.isPresent() ? optionalUser.get().getAccountName() : ""; + final boolean usesOldLogin = arbitraryDataProvider.getBooleanValue(accountName, UserAccountManager.ACCOUNT_USES_STANDARD_PASSWORD); if (usesOldLogin) { snackbar = Snackbar.make(emptyContentContainer, R.string.push_notifications_old_login, Snackbar.LENGTH_INDEFINITE); } else { - String pushValue = arbitraryDataProvider.getValue(currentAccount.name, PushUtils.KEY_PUSH); + String pushValue = arbitraryDataProvider.getValue(accountName, PushUtils.KEY_PUSH); if (pushValue == null || pushValue.isEmpty()) { snackbar = Snackbar.make(emptyContentContainer, R.string.push_notifications_temp_error, @@ -250,13 +252,12 @@ private void populateList(List notifications) { private void fetchAndSetData() { Thread t = new Thread(() -> { - if (client == null) { + if (client == null && optionalUser.isPresent()) { try { - OwnCloudAccount ocAccount = new OwnCloudAccount(currentAccount, this); - client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, this); - client.setOwnCloudVersion(accountManager.getServerVersion(currentAccount)); - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException | - IOException | OperationCanceledException | AuthenticatorException e) { + User user = optionalUser.get(); + client = clientFactory.create(user); + client.setOwnCloudVersion(user.getServer().getVersion()); + } catch (ClientFactory.CreationException e) { Log_OC.e(TAG, "Error initializing client", e); } } diff --git a/src/main/java/com/owncloud/android/ui/activity/RichDocumentsWebView.java b/src/main/java/com/owncloud/android/ui/activity/RichDocumentsWebView.java index 9f0188569b3c..e72182bcd93b 100644 --- a/src/main/java/com/owncloud/android/ui/activity/RichDocumentsWebView.java +++ b/src/main/java/com/owncloud/android/ui/activity/RichDocumentsWebView.java @@ -49,6 +49,7 @@ import com.bumptech.glide.Glide; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; @@ -155,7 +156,8 @@ protected void onCreate(Bundle savedInstanceState) { break; } - Glide.with(this).using(new CustomGlideStreamLoader(currentAccountProvider)).load(template.getThumbnailLink()) + Glide.with(this).using(new CustomGlideStreamLoader(currentAccountProvider, clientFactory)) + .load(template.getThumbnailLink()) .placeholder(placeholder) .error(placeholder) .into(thumbnail); @@ -314,9 +316,9 @@ private void handleRemoteFile(Intent data) { OCFile file = data.getParcelableExtra(FolderPickerActivity.EXTRA_FILES); new Thread(() -> { - Account account = currentAccountProvider.getCurrentAccount(); + User user = currentAccountProvider.getUser(); RichDocumentsCreateAssetOperation operation = new RichDocumentsCreateAssetOperation(file.getRemotePath()); - RemoteOperationResult result = operation.execute(account, this); + RemoteOperationResult result = operation.execute(user.toPlatformAccount(), this); if (result.isSuccess()) { String asset = (String) result.getSingleData(); diff --git a/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java b/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java index e51d028e5fb8..03563a730460 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SettingsActivity.java @@ -25,7 +25,6 @@ */ package com.owncloud.android.ui.activity; -import android.accounts.Account; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; @@ -52,10 +51,12 @@ import android.view.Window; import android.webkit.URLUtil; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.etm.EtmActivity; import com.nextcloud.client.logger.ui.LogsActivity; +import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.preferences.AppPreferences; import com.nextcloud.client.preferences.AppPreferencesImpl; import com.owncloud.android.BuildConfig; @@ -69,8 +70,6 @@ import com.owncloud.android.datastorage.StoragePoint; import com.owncloud.android.lib.common.ExternalLink; import com.owncloud.android.lib.common.ExternalLinkType; -import com.owncloud.android.lib.common.OwnCloudAccount; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.asynctasks.LoadingVersionNumberTask; import com.owncloud.android.utils.DeviceCredentialUtils; @@ -130,10 +129,11 @@ public class SettingsActivity extends ThemedPreferenceActivity private String storagePath; private String pendingLock; - private Account account; + private User user; @Inject ArbitraryDataProvider arbitraryDataProvider; @Inject AppPreferences preferences; @Inject UserAccountManager accountManager; + @Inject ClientFactory clientFactory; @SuppressWarnings("deprecation") @Override @@ -156,7 +156,7 @@ public void onCreate(Bundle savedInstanceState) { String appVersion = getAppVersion(); PreferenceScreen preferenceScreen = (PreferenceScreen) findPreference("preference_screen"); - account = accountManager.getCurrentAccount(); + user = accountManager.getUser(); // retrieve user's base uri setupBaseUri(); @@ -408,7 +408,7 @@ private void setupRecommendPreference(PreferenceCategory preferenceCategoryMore) } private void setupE2EMnemonicPreference(PreferenceCategory preferenceCategoryMore) { - String mnemonic = arbitraryDataProvider.getValue(account.name, EncryptionUtils.MNEMONIC); + String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC); Preference pMnemonic = findPreference("mnemonic"); if (pMnemonic != null) { @@ -603,11 +603,11 @@ private void setupAutoUploadCategory(int accentColor, PreferenceScreen preferenc final SwitchPreference pUploadOnWifiCheckbox = (SwitchPreference) findPreference("synced_folder_on_wifi"); pUploadOnWifiCheckbox.setChecked( - arbitraryDataProvider.getBooleanValue(account, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI)); + arbitraryDataProvider.getBooleanValue(user.toPlatformAccount(), SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI)); pUploadOnWifiCheckbox.setOnPreferenceClickListener(preference -> { - arbitraryDataProvider.storeOrUpdateKeyValue(account.name, SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI, - String.valueOf(pUploadOnWifiCheckbox.isChecked())); + arbitraryDataProvider.storeOrUpdateKeyValue(user.getAccountName(), SYNCED_FOLDER_LIGHT_UPLOAD_ON_WIFI, + String.valueOf(pUploadOnWifiCheckbox.isChecked())); return true; }); @@ -764,7 +764,7 @@ private void launchDavDroidLogin() { davDroidLoginIntent.setData(Uri.parse(serverBaseUri.toString() + AuthenticatorActivity.WEB_LOGIN)); davDroidLoginIntent.putExtra("davPath", DAV_PATH); } - davDroidLoginIntent.putExtra("username", UserAccountManager.getUsername(account)); + davDroidLoginIntent.putExtra("username", UserAccountManager.getUsername(user.toPlatformAccount())); startActivityForResult(davDroidLoginIntent, ACTION_REQUEST_CODE_DAVDROID_SETUP); } else { @@ -789,9 +789,7 @@ private void setupBaseUri() { // retrieve and set user's base URI Thread t = new Thread(() -> { try { - OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext()); - serverBaseUri = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, - getApplicationContext()).getBaseUri(); + serverBaseUri = clientFactory.create(user).getBaseUri(); } catch (Exception e) { Log_OC.e(TAG, "Error retrieving user's base URI", e); } @@ -857,7 +855,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { RequestCredentialsActivity.KEY_CHECK_RESULT_TRUE) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(getContentResolver()); - String mnemonic = arbitraryDataProvider.getValue(account.name, EncryptionUtils.MNEMONIC); + String mnemonic = arbitraryDataProvider.getValue(user.getAccountName(), EncryptionUtils.MNEMONIC); int accentColor = ThemeUtils.primaryAccentColor(this); diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 85506e065250..972522d6336f 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -41,10 +41,12 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.nextcloud.client.account.User; import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; +import com.nextcloud.java.util.Optional; import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -125,15 +127,15 @@ protected void onCreate(Bundle savedInstanceState) { setContentView(R.layout.synced_folders_layout); - String account; - Account currentAccount; if (getIntent() != null && getIntent().getExtras() != null) { - account = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT); - currentAccount = getAccount(); - - if (account != null && currentAccount != null && !account.equalsIgnoreCase(currentAccount.name)) { - accountManager.setCurrentOwnCloudAccount(account); - setAccount(getUserAccountManager().getCurrentAccount(), false); + final String accountName = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT); + Optional optionalUser = getUser(); + if (optionalUser.isPresent() && accountName != null) { + User user = optionalUser.get(); + if (!accountName.equalsIgnoreCase(user.getAccountName())) { + accountManager.setCurrentOwnCloudAccount(accountName); + setUser(getUserAccountManager().getUser()); + } } path = getIntent().getStringExtra(MediaFoldersDetectionJob.KEY_MEDIA_FOLDER_PATH); @@ -254,10 +256,9 @@ private void load(final int perFolderMediaItemLimit, boolean force) { List syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders(); List currentAccountSyncedFoldersList = new ArrayList<>(); - Account currentAccount = getUserAccountManager().getCurrentAccount(); + User user = getUserAccountManager().getUser(); for (SyncedFolder syncedFolder : syncedFolderArrayList) { - if (currentAccount != null && syncedFolder.getAccount().equals(currentAccount.name)) { - + if (syncedFolder.getAccount().equals(user.getAccountName())) { // delete non-existing & disabled synced folders if (!new File(syncedFolder.getLocalPath()).exists() && !syncedFolder.isEnabled()) { mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 606cc7db24c7..2ca1520000e1 100755 --- a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -43,9 +43,11 @@ import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; import com.evernote.android.job.util.support.PersistableBundleCompat; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; +import com.nextcloud.java.util.Optional; import com.owncloud.android.R; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.files.services.FileUploader; @@ -230,9 +232,9 @@ private void refresh() { protected void onStart() { super.onStart(); ThemeUtils.setColoredTitle(getSupportActionBar(), R.string.uploads_view_title, this); - final Account account = getAccount(); - if (account != null) { - setAccountInDrawer(account); + final Optional optionalUser = getUser(); + if (optionalUser.isPresent()) { + setAccountInDrawer(optionalUser.get()); } } diff --git a/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java index 5a5275ced0ed..018dd33fb24d 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/ActivityListAdapter.java @@ -51,6 +51,7 @@ import com.bumptech.glide.load.resource.file.FileToStreamDecoder; import com.caverock.androidsvg.SVG; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; @@ -94,6 +95,7 @@ public class ActivityListAdapter extends RecyclerView.Adapter values; @@ -246,8 +248,11 @@ private ImageView createThumbnailNew(PreviewObject previewObject) { if (MimeTypeUtil.isImageOrVideo(previewObject.getMimeType())) { int placeholder = R.drawable.file; - Glide.with(context).using(new CustomGlideStreamLoader(currentAccountProvider)).load(previewObject.getSource()). - placeholder(placeholder).error(placeholder).into(imageView); + Glide.with(context).using(new CustomGlideStreamLoader(currentAccountProvider, clientFactory)) + .load(previewObject.getSource()) + .placeholder(placeholder) + .error(placeholder) + .into(imageView); } else { if (MimeTypeUtil.isFolder(previewObject.getMimeType())) { imageView.setImageDrawable( @@ -297,8 +302,10 @@ private void setBitmap(OCFile file, ImageView fileIcon, boolean isDetailView) { String uri = client.getBaseUri() + "/index.php/apps/files/api/v1/thumbnail/" + px + "/" + px + Uri.encode(file.getRemotePath(), "/"); - Glide.with(context).using(new CustomGlideStreamLoader(currentAccountProvider)).load(uri).placeholder(placeholder) - .error(placeholder).into(fileIcon); // using custom fetcher + Glide.with(context).using(new CustomGlideStreamLoader(currentAccountProvider, clientFactory)) + .load(uri).placeholder(placeholder) + .error(placeholder) + .into(fileIcon); // using custom fetcher } else { if (isDetailView) { diff --git a/src/main/java/com/owncloud/android/ui/adapter/TemplateAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/TemplateAdapter.java index 40a42bf5a067..6769d5b53033 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/TemplateAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/TemplateAdapter.java @@ -33,6 +33,7 @@ import com.bumptech.glide.Glide; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.R; import com.owncloud.android.datamodel.Template; import com.owncloud.android.ui.dialog.ChooseTemplateDialogFragment; @@ -56,17 +57,20 @@ public class TemplateAdapter extends RecyclerView.Adapter files; private final Context context; - private final Account account; + private final User user; private final FileDataStorageManager storageManager; private final AppPreferences preferences; @@ -76,11 +76,11 @@ public TrashbinListAdapter( FileDataStorageManager storageManager, AppPreferences preferences, Context context, - Account account + User user ) { this.files = new ArrayList<>(); this.trashbinActivityInterface = trashbinActivityInterface; - this.account = account; + this.user = user; this.storageManager = storageManager; this.preferences = preferences; this.context = context; @@ -237,7 +237,7 @@ private void setThumbnail(TrashbinFile file, ImageView thumbnailView) { try { final ThumbnailsCacheManager.ThumbnailGenerationTask task = new ThumbnailsCacheManager.ThumbnailGenerationTask(thumbnailView, storageManager, - account, asyncTasks); + user.toPlatformAccount(), asyncTasks); final ThumbnailsCacheManager.AsyncThumbnailDrawable asyncDrawable = new ThumbnailsCacheManager.AsyncThumbnailDrawable(context.getResources(), @@ -257,7 +257,7 @@ private void setThumbnail(TrashbinFile file, ImageView thumbnailView) { } } else { thumbnailView.setImageDrawable(MimeTypeUtil.getFileTypeIcon(file.getMimeType(), file.getFileName(), - account, context)); + user.toPlatformAccount(), context)); } } } diff --git a/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java b/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java index a300ac0ca975..e388d96380d8 100644 --- a/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java +++ b/src/main/java/com/owncloud/android/ui/asynctasks/PhotoSearchTask.java @@ -21,9 +21,9 @@ package com.owncloud.android.ui.asynctasks; -import android.accounts.Account; import android.os.AsyncTask; +import com.nextcloud.client.account.User; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; @@ -38,18 +38,18 @@ public class PhotoSearchTask extends AsyncTask { private int columnCount; - private Account account; + private User user; private WeakReference photoFragmentWeakReference; private SearchRemoteOperation searchRemoteOperation; private FileDataStorageManager storageManager; public PhotoSearchTask(int columnsCount, PhotoFragment photoFragment, - Account account, + User user, SearchRemoteOperation searchRemoteOperation, FileDataStorageManager storageManager) { this.columnCount = columnsCount; - this.account = account; + this.user = user; this.photoFragmentWeakReference = new WeakReference<>(photoFragment); this.searchRemoteOperation = searchRemoteOperation; this.storageManager = storageManager; @@ -88,7 +88,7 @@ protected RemoteOperationResult doInBackground(Void... voids) { searchRemoteOperation.setTimestamp(timestamp); if (photoFragment.getContext() != null) { - return searchRemoteOperation.execute(account, photoFragment.getContext()); + return searchRemoteOperation.execute(user.toPlatformAccount(), photoFragment.getContext()); } else { return new RemoteOperationResult(new IllegalStateException("No context available")); } diff --git a/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.java index 044932479085..90bffa6dc35f 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ChooseTemplateDialogFragment.java @@ -24,7 +24,6 @@ package com.owncloud.android.ui.dialog; -import android.accounts.Account; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Dialog; @@ -40,16 +39,16 @@ import android.widget.EditText; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.Template; import com.owncloud.android.files.CreateFileFromTemplateOperation; import com.owncloud.android.files.FetchTemplateOperation; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.ui.activity.ExternalSiteWebView; @@ -88,7 +87,8 @@ public class ChooseTemplateDialogFragment extends DialogFragment implements Dial private TemplateAdapter adapter; private OCFile parentFolder; private OwnCloudClient client; - @Inject CurrentAccountProvider currentAccount; + @Inject CurrentAccountProvider currentUser; + @Inject ClientFactory clientFactory; public enum Type { DOCUMENT, @@ -151,9 +151,8 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { fileName.getBackground().setColorFilter(accentColor, PorterDuff.Mode.SRC_ATOP); try { - Account account = currentAccount.getCurrentAccount(); - OwnCloudAccount ocAccount = new OwnCloudAccount(account, activity); - client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(ocAccount, getContext()); + User user = currentUser.getUser(); + client = clientFactory.create(user); new FetchTemplateTask(this, client).execute(type); } catch (Exception e) { @@ -162,7 +161,7 @@ public Dialog onCreateDialog(Bundle savedInstanceState) { listView.setHasFixedSize(true); listView.setLayoutManager(new GridLayoutManager(activity, 2)); - adapter = new TemplateAdapter(type, this, getContext(), currentAccount); + adapter = new TemplateAdapter(type, this, getContext(), currentUser, clientFactory); listView.setAdapter(adapter); // Build the dialog diff --git a/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java index a1032b363b56..165bc9d0e4db 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/ExtendedListFragment.java @@ -53,6 +53,7 @@ import android.widget.TextView; import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; @@ -304,7 +305,8 @@ private void performSearch(final String query, boolean isSubmit) { handler.postDelayed(new Runnable() { @Override public void run() { - if (accountManager.isSearchSupported(accountManager.getCurrentAccount())) { + User user = accountManager.getUser(); + if (user.getServer().getVersion().isSearchSupported()) { EventBus.getDefault().post(new SearchEvent(query, SearchRemoteOperation.SearchType.FILE_SEARCH, SearchEvent.UnsetType.NO_UNSET)); } else { diff --git a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java index 60886c3f7fd3..ce4d1e37c202 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/FileDetailActivitiesFragment.java @@ -24,9 +24,6 @@ package com.owncloud.android.ui.fragment; import android.accounts.Account; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; -import android.content.Context; import android.graphics.PorterDuff; import android.os.AsyncTask; import android.os.Bundle; @@ -41,15 +38,14 @@ import com.google.android.material.snackbar.Snackbar; import com.google.android.material.textfield.TextInputEditText; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; -import com.owncloud.android.MainApp; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; -import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; import com.owncloud.android.lib.resources.activities.GetActivitiesRemoteOperation; @@ -72,7 +68,6 @@ import org.apache.commons.httpclient.HttpStatus; import org.greenrobot.eventbus.EventBus; -import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -149,6 +144,7 @@ public class FileDetailActivitiesFragment extends Fragment implements private VersionListInterface.CommentCallback callback; @Inject UserAccountManager accountManager; + @Inject ClientFactory clientFactory; public static FileDetailActivitiesFragment newInstance(OCFile file, Account account) { FileDetailActivitiesFragment fragment = new FileDetailActivitiesFragment(); @@ -258,7 +254,9 @@ private void setupView() { PorterDuff.Mode.SRC_IN); emptyContentIcon.setImageDrawable(getResources().getDrawable(R.drawable.ic_activity_light_grey)); - adapter = new ActivityAndVersionListAdapter(getContext(), accountManager, this, this, storageManager, capability); + adapter = new ActivityAndVersionListAdapter(getContext(), accountManager, this, this, + storageManager, + capability); recyclerView.setAdapter(adapter); LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); @@ -301,9 +299,9 @@ private void fetchAndSetData(int lastGiven) { final SwipeRefreshLayout empty = swipeEmptyListRefreshLayout; final SwipeRefreshLayout list = swipeListRefreshLayout; - final Account currentAccount = accountManager.getCurrentAccount(); + final User user = accountManager.getUser(); - if (currentAccount == null) { + if (user.isAnonymous()) { activity.runOnUiThread(() -> { setEmptyContent(getString(R.string.common_error), getString(R.string.file_detail_activity_error)); list.setVisibility(View.GONE); @@ -312,15 +310,10 @@ private void fetchAndSetData(int lastGiven) { return; } - final Context context = MainApp.getAppContext(); - Thread t = new Thread(() -> { - OwnCloudAccount ocAccount; try { - ocAccount = new OwnCloudAccount(currentAccount, context); - ownCloudClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - ownCloudClient.setOwnCloudVersion(accountManager.getServerVersion(currentAccount)); + ownCloudClient = clientFactory.create(user); + ownCloudClient.setOwnCloudVersion(user.getServer().getVersion()); isLoadingActivities = true; GetActivitiesRemoteOperation getRemoteNotificationOperation; @@ -385,8 +378,7 @@ private void fetchAndSetData(int lastGiven) { } hideRefreshLayoutLoader(activity); - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException | IOException | - OperationCanceledException | AuthenticatorException | NullPointerException e) { + } catch (ClientFactory.CreationException e) { Log_OC.e(TAG, "Error fetching file details activities", e); } }); diff --git a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java index 4fc993e3003c..3d3b85aaa591 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/OCFileListFragment.java @@ -47,9 +47,11 @@ import android.widget.PopupMenu; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.device.DeviceInfo; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -169,6 +171,7 @@ public class OCFileListFragment extends ExtendedListFragment implements @Inject AppPreferences preferences; @Inject UserAccountManager accountManager; + @Inject ClientFactory clientFactory; protected FileFragment.ContainerActivity mContainerActivity; protected OCFile mFile; @@ -942,10 +945,11 @@ public void onItemClicked(OCFile file) { mContainerActivity.getFileOperationsHelper().openFile(file); } } else { - Account account = accountManager.getCurrentAccount(); - OCCapability capability = mContainerActivity.getStorageManager().getCapability(account.name); + User account = accountManager.getUser(); + OCCapability capability = mContainerActivity.getStorageManager() + .getCapability(account.getAccountName()); - if (PreviewMediaFragment.canBePreviewed(file) && accountManager.getServerVersion(account) + if (PreviewMediaFragment.canBePreviewed(file) && account.getServer().getVersion() .isMediaStreamingSupported()) { // stream media preview on >= NC14 ((FileDisplayActivity) mContainerActivity).startMediaPreview(file, 0, true, true, true); @@ -1430,26 +1434,19 @@ public void onMessageEvent(CommentsEvent event) { @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(FavoriteEvent event) { - Account currentAccount = accountManager.getCurrentAccount(); - - OwnCloudAccount ocAccount; - try { - ocAccount = new OwnCloudAccount(currentAccount, MainApp.getAppContext()); - - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); + User user = accountManager.getUser(); + OwnCloudClient client = clientFactory.create(user); ToggleFavoriteRemoteOperation toggleFavoriteOperation = new ToggleFavoriteRemoteOperation( event.shouldFavorite, event.remotePath); - RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(mClient); + RemoteOperationResult remoteOperationResult = toggleFavoriteOperation.execute(client); if (remoteOperationResult.isSuccess()) { mAdapter.setFavoriteAttributeForItemID(event.remoteId, event.shouldFavorite); } - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException | AuthenticatorException - | IOException | OperationCanceledException e) { + } catch (ClientFactory.CreationException e) { Log_OC.e(TAG, "Error processing event", e); } } @@ -1483,7 +1480,7 @@ public void onMessageEvent(final SearchEvent event) { new Handler(Looper.getMainLooper()).post(switchViewsRunnable); - final Account currentAccount = accountManager.getCurrentAccount(); + final User currentAccount = accountManager.getUser(); final RemoteOperation remoteOperation; if (currentSearchType != SearchType.SHARED_FILTER) { @@ -1503,7 +1500,8 @@ public void onMessageEvent(final SearchEvent event) { protected Object doInBackground(Object[] params) { setTitle(); if (getContext() != null && !isCancelled()) { - RemoteOperationResult remoteOperationResult = remoteOperation.execute(currentAccount, getContext()); + RemoteOperationResult remoteOperationResult = remoteOperation.execute( + currentAccount.toPlatformAccount(), getContext()); FileDataStorageManager storageManager = null; if (mContainerActivity != null && mContainerActivity.getStorageManager() != null) { @@ -1555,18 +1553,12 @@ protected void onPostExecute(Object o) { @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEvent(EncryptionEvent event) { - Account currentAccount = accountManager.getCurrentAccount(); - - OwnCloudAccount ocAccount; try { - ocAccount = new OwnCloudAccount(currentAccount, MainApp.getAppContext()); - - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); - - ToggleEncryptionRemoteOperation toggleEncryptionOperation = new ToggleEncryptionRemoteOperation( + final User user = accountManager.getUser(); + final OwnCloudClient client = clientFactory.create(user); + final ToggleEncryptionRemoteOperation toggleEncryptionOperation = new ToggleEncryptionRemoteOperation( event.localId, event.remotePath, event.shouldBeEncrypted); - RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(mClient); + final RemoteOperationResult remoteOperationResult = toggleEncryptionOperation.execute(client); if (remoteOperationResult.isSuccess()) { mAdapter.setEncryptionAttributeForItemID(event.remoteId, event.shouldBeEncrypted); @@ -1576,14 +1568,8 @@ public void onMessageEvent(EncryptionEvent event) { Snackbar.make(getRecyclerView(), R.string.common_error_unknown, Snackbar.LENGTH_LONG).show(); } - } catch (com.owncloud.android.lib.common.accounts.AccountUtils.AccountNotFoundException e) { - Log_OC.e(TAG, "Account not found", e); - } catch (AuthenticatorException e) { - Log_OC.e(TAG, "Authentication failed", e); - } catch (IOException e) { - Log_OC.e(TAG, "IO error", e); - } catch (OperationCanceledException e) { - Log_OC.e(TAG, "Operation has been canceled", e); + } catch (ClientFactory.CreationException e) { + Log_OC.e(TAG, "Cannot create client", e); } } diff --git a/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java b/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java index e405e2947bc0..a6bc85eff1b3 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/PhotoFragment.java @@ -161,7 +161,7 @@ private void searchAndDisplay() { if (!photoSearchQueryRunning && !photoSearchNoNew) { photoSearchTask = new PhotoSearchTask(getColumnsCount(), this, - accountManager.getCurrentAccount(), + accountManager.getUser(), searchRemoteOperation, mContainerActivity.getStorageManager()) .execute(); diff --git a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java index 2f6597e8378f..540c9646b647 100644 --- a/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java +++ b/src/main/java/com/owncloud/android/ui/fragment/contactsbackup/ContactListFragment.java @@ -61,6 +61,7 @@ import com.google.android.material.snackbar.Snackbar; import com.nextcloud.client.account.UserAccountManager; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.R; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; @@ -149,6 +150,7 @@ public class ContactListFragment extends FileFragment implements Injectable { private List vCards = new ArrayList<>(); private OCFile ocFile; @Inject UserAccountManager accountManager; + @Inject ClientFactory clientFactory; public static ContactListFragment newInstance(OCFile file, Account account) { ContactListFragment frag = new ContactListFragment(); @@ -191,7 +193,7 @@ public View onCreateView(@NonNull final LayoutInflater inflater, ViewGroup conta recyclerView = view.findViewById(R.id.contactlist_recyclerview); if (savedInstanceState == null) { - contactListAdapter = new ContactListAdapter(accountManager, getContext(), vCards); + contactListAdapter = new ContactListAdapter(accountManager, clientFactory, getContext(), vCards); } else { Set checkedItems = new HashSet<>(); int[] itemsArray = savedInstanceState.getIntArray(CHECKED_ITEMS_ARRAY_KEY); @@ -589,12 +591,15 @@ class ContactListAdapter extends RecyclerView.Adapter vCards) { + ContactListAdapter(UserAccountManager accountManager, ClientFactory clientFactory, Context context, + List vCards) { this.vCards = vCards; this.context = context; this.checkedVCards = new HashSet<>(); this.accountManager = accountManager; + this.clientFactory = clientFactory; } ContactListAdapter(UserAccountManager accountManager, @@ -699,6 +704,7 @@ public void onLoadFailed(Exception e, Drawable errorDrawable) { } }; DisplayUtils.downloadIcon(accountManager, + clientFactory, context, url, target, diff --git a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java index 0c6c0d1115c6..387125c67c72 100755 --- a/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java +++ b/src/main/java/com/owncloud/android/ui/helpers/FileOperationsHelper.java @@ -46,6 +46,7 @@ import com.evernote.android.job.JobRequest; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; import com.nextcloud.client.network.ConnectivityService; import com.owncloud.android.MainApp; import com.owncloud.android.R; @@ -291,14 +292,14 @@ public void openFile(OCFile file) { new Thread(new Runnable() { @Override public void run() { - Account account = currentAccount.getCurrentAccount(); + User user = currentAccount.getUser(); FileDataStorageManager storageManager = - new FileDataStorageManager(account, fileActivity.getContentResolver()); + new FileDataStorageManager(user.toPlatformAccount(), fileActivity.getContentResolver()); // a fresh object is needed; many things could have occurred to the file // since it was registered to observe again, assuming that local files // are linked to a remote file AT MOST, SOMETHING TO BE DONE; SynchronizeFileOperation sfo = - new SynchronizeFileOperation(file, null, account, true, fileActivity); + new SynchronizeFileOperation(file, null, user.toPlatformAccount(), true, fileActivity); RemoteOperationResult result = sfo.execute(storageManager, fileActivity); fileActivity.dismissLoadingDialog(); if (result.getCode() == RemoteOperationResult.ResultCode.SYNC_CONFLICT) { @@ -307,7 +308,7 @@ public void run() { Intent i = new Intent(fileActivity, ConflictsResolveActivity.class); i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file); - i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, account); + i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, user.toPlatformAccount()); fileActivity.startActivity(i); } else { if (!launchables.isEmpty()) { @@ -397,10 +398,10 @@ private Uri getFileUri(OCFile file, String... officeExtensions) { public void streamMediaFile(OCFile file) { fileActivity.showLoadingDialog(fileActivity.getString(R.string.wait_a_moment)); - final Account account = currentAccount.getCurrentAccount(); + final User user = currentAccount.getUser(); new Thread(() -> { StreamMediaFileOperation sfo = new StreamMediaFileOperation(file.getLocalId()); - RemoteOperationResult result = sfo.execute(account, fileActivity); + RemoteOperationResult result = sfo.execute(user.toPlatformAccount(), fileActivity); fileActivity.dismissLoadingDialog(); diff --git a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java index d956b2462c82..81f7d4d3b0bc 100644 --- a/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java +++ b/src/main/java/com/owncloud/android/ui/preview/PreviewImageActivity.java @@ -37,6 +37,7 @@ import android.view.View; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.MainApp; @@ -385,11 +386,11 @@ private void backToDisplayActivity() { @SuppressFBWarnings("DLS") @Override public void showDetails(OCFile file) { - final Account currentAccount = getUserAccountManager().getCurrentAccount(); + final User currentUser = getUserAccountManager().getUser(); final Intent showDetailsIntent = new Intent(this, FileDisplayActivity.class); showDetailsIntent.setAction(FileDisplayActivity.ACTION_DETAILS); showDetailsIntent.putExtra(FileActivity.EXTRA_FILE, file); - showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, currentAccount); + showDetailsIntent.putExtra(FileActivity.EXTRA_ACCOUNT, currentUser.toPlatformAccount()); showDetailsIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivity(showDetailsIntent); finish(); diff --git a/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.java b/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.java index a10646f6e525..fcf9ad155bc9 100644 --- a/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.java +++ b/src/main/java/com/owncloud/android/ui/trashbin/RemoteTrashbinRepository.java @@ -27,6 +27,8 @@ import android.content.Context; import android.os.AsyncTask; +import com.nextcloud.client.account.User; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.R; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; @@ -47,39 +49,46 @@ public class RemoteTrashbinRepository implements TrashbinRepository { private static final String TAG = RemoteTrashbinRepository.class.getSimpleName(); - private OwnCloudClient client; + private final User user; + private final ClientFactory clientFactory; - RemoteTrashbinRepository(final Context context, final Account account) { - try { - OwnCloudAccount nextcloudAccount = new OwnCloudAccount(account, context); - client = OwnCloudClientManagerFactory.getDefaultSingleton().getClientFor(nextcloudAccount, context); - } catch (Exception e) { - Log_OC.e(TAG, e.getMessage()); - } + RemoteTrashbinRepository(User user, ClientFactory clientFactory) { + this.user = user; + this.clientFactory = clientFactory; } public void removeTrashbinFile(TrashbinFile file, OperationCallback callback) { - new RemoveTrashbinFileTask(client, file, callback).execute(); + new RemoveTrashbinFileTask(user, clientFactory, file, callback).execute(); } private static class RemoveTrashbinFileTask extends AsyncTask { - private OwnCloudClient client; + private User user; + private ClientFactory clientFactory; private TrashbinFile file; private OperationCallback callback; - private RemoveTrashbinFileTask(OwnCloudClient client, TrashbinFile file, OperationCallback callback) { - this.client = client; + private RemoveTrashbinFileTask(User user, + ClientFactory clientFactory, + TrashbinFile file, + OperationCallback callback) { + this.user = user; + this.clientFactory = clientFactory; this.file = file; this.callback = callback; } @Override protected Boolean doInBackground(Void... voids) { - RemoteOperationResult result = new RemoveTrashbinFileRemoteOperation(file.getFullRemotePath()) - .execute(client); - - return result.isSuccess(); + try { + OwnCloudClient client = clientFactory.create(user); + RemoteOperationResult result = new RemoveTrashbinFileRemoteOperation(file.getFullRemotePath()) + .execute(client); + return result.isSuccess(); + } catch (ClientFactory.CreationException e) { + Log_OC.e(this, "Cannot create client", e); + return false; + } } @Override @@ -91,25 +100,32 @@ protected void onPostExecute(Boolean success) { } public void emptyTrashbin(OperationCallback callback) { - new EmptyTrashbinTask(client, callback).execute(); + new EmptyTrashbinTask(user, clientFactory, callback).execute(); } private static class EmptyTrashbinTask extends AsyncTask { - private OwnCloudClient client; + private User user; + private ClientFactory clientFactory; private OperationCallback callback; - private EmptyTrashbinTask(OwnCloudClient client, OperationCallback callback) { - this.client = client; + private EmptyTrashbinTask(User user, ClientFactory clientFactory, OperationCallback callback) { + this.user = user; + this.clientFactory = clientFactory; this.callback = callback; } @Override protected Boolean doInBackground(Void... voids) { - EmptyTrashbinRemoteOperation emptyTrashbinFileOperation = new EmptyTrashbinRemoteOperation(); - RemoteOperationResult result = emptyTrashbinFileOperation.execute(client); - - return result.isSuccess(); + try { + OwnCloudClient client = clientFactory.create(user); + EmptyTrashbinRemoteOperation emptyTrashbinFileOperation = new EmptyTrashbinRemoteOperation(); + RemoteOperationResult result = emptyTrashbinFileOperation.execute(client); + return result.isSuccess(); + } catch (ClientFactory.CreationException e) { + Log_OC.e(this, "Cannot create client", e); + return false; + } } @Override @@ -122,28 +138,36 @@ protected void onPostExecute(Boolean success) { @Override public void restoreFile(TrashbinFile file, OperationCallback callback) { - new RestoreTrashbinFileTask(file, client, callback).execute(); + new RestoreTrashbinFileTask(file, user, clientFactory, callback).execute(); } private static class RestoreTrashbinFileTask extends AsyncTask { private TrashbinFile file; - private OwnCloudClient client; + private User user; + private ClientFactory clientFactory; private TrashbinRepository.OperationCallback callback; - private RestoreTrashbinFileTask(TrashbinFile file, OwnCloudClient client, + private RestoreTrashbinFileTask(TrashbinFile file, User user, ClientFactory clientFactory, TrashbinRepository.OperationCallback callback) { this.file = file; - this.client = client; + this.user = user; + this.clientFactory = clientFactory; this.callback = callback; } @Override protected Boolean doInBackground(Void... voids) { - RemoteOperationResult result = new RestoreTrashbinFileRemoteOperation(file.getFullRemotePath(), - file.getFileName()).execute(client); - - return result.isSuccess(); + try { + OwnCloudClient client = clientFactory.create(user); + RemoteOperationResult result = new RestoreTrashbinFileRemoteOperation(file.getFullRemotePath(), + file.getFileName()).execute(client); + + return result.isSuccess(); + } catch (ClientFactory.CreationException e) { + Log_OC.e(this, "Cannot create client", e); + return false; + } } @Override @@ -156,30 +180,37 @@ protected void onPostExecute(Boolean success) { @Override public void getFolder(String remotePath, @NonNull LoadFolderCallback callback) { - new ReadRemoteTrashbinFolderTask(remotePath, client, callback).execute(); + new ReadRemoteTrashbinFolderTask(remotePath, user, clientFactory, callback).execute(); } private static class ReadRemoteTrashbinFolderTask extends AsyncTask { private String remotePath; - private OwnCloudClient client; + private User user; + private ClientFactory clientFactory; private List trashbinFiles; private LoadFolderCallback callback; - private ReadRemoteTrashbinFolderTask(String remotePath, OwnCloudClient client, LoadFolderCallback callback) { + private ReadRemoteTrashbinFolderTask(String remotePath, User user, ClientFactory clientFactory, + LoadFolderCallback callback) { this.remotePath = remotePath; - this.client = client; + this.user = user; + this.clientFactory = clientFactory; this.callback = callback; } @Override protected Boolean doInBackground(Void... voids) { - RemoteOperationResult result = new ReadTrashbinFolderRemoteOperation(remotePath).execute(client); - - if (result.isSuccess()) { - trashbinFiles = result.getData(); - return true; - } else { + try { + OwnCloudClient client = clientFactory.create(user); + RemoteOperationResult result = new ReadTrashbinFolderRemoteOperation(remotePath).execute(client); + if (result.isSuccess()) { + trashbinFiles = result.getData(); + return true; + } else { + return false; + } + } catch (ClientFactory.CreationException e) { return false; } } diff --git a/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java b/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java index a6aac5ac08d4..e43a46bcb1bf 100644 --- a/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java +++ b/src/main/java/com/owncloud/android/ui/trashbin/TrashbinActivity.java @@ -23,7 +23,6 @@ */ package com.owncloud.android.ui.trashbin; -import android.accounts.Account; import android.content.Intent; import android.os.Bundle; import android.view.Menu; @@ -34,7 +33,10 @@ import android.widget.TextView; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; import com.nextcloud.client.di.Injectable; +import com.nextcloud.client.network.ClientFactory; import com.nextcloud.client.preferences.AppPreferences; import com.owncloud.android.R; import com.owncloud.android.lib.resources.trashbin.model.TrashbinFile; @@ -92,6 +94,8 @@ public class TrashbinActivity extends FileActivity implements public String noResultsMessage; @Inject AppPreferences preferences; + @Inject CurrentAccountProvider accountProvider; + @Inject ClientFactory clientFactory; private Unbinder unbinder; private TrashbinListAdapter trashbinListAdapter; private TrashbinPresenter trashbinPresenter; @@ -101,29 +105,20 @@ public class TrashbinActivity extends FileActivity implements @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - - final Account currentAccount = getUserAccountManager().getCurrentAccount(); - final RemoteTrashbinRepository trashRepository = new RemoteTrashbinRepository(this, currentAccount); + final User user = accountProvider.getUser(); + final RemoteTrashbinRepository trashRepository = new RemoteTrashbinRepository(user, clientFactory); trashbinPresenter = new TrashbinPresenter(trashRepository, this); - setContentView(R.layout.trashbin_activity); unbinder = ButterKnife.bind(this); - - // setup toolbar setupToolbar(); - - // setup drawer setupDrawer(R.id.nav_trashbin); - ThemeUtils.setColoredTitle(getSupportActionBar(), R.string.trashbin_activity_title, this); } @Override protected void onStart() { super.onStart(); - active = true; - setupContent(); } @@ -142,7 +137,7 @@ private void setupContent() { getStorageManager(), preferences, this, - getUserAccountManager().getCurrentAccount() + getUserAccountManager().getUser() ); recyclerView.setAdapter(trashbinListAdapter); recyclerView.setHasFixedSize(true); diff --git a/src/main/java/com/owncloud/android/utils/DisplayUtils.java b/src/main/java/com/owncloud/android/utils/DisplayUtils.java index 047268759979..9fa2ee6a040f 100644 --- a/src/main/java/com/owncloud/android/utils/DisplayUtils.java +++ b/src/main/java/com/owncloud/android/utils/DisplayUtils.java @@ -55,6 +55,7 @@ import com.caverock.androidsvg.SVG; import com.google.android.material.snackbar.Snackbar; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.datamodel.ArbitraryDataProvider; @@ -515,6 +516,7 @@ public static void setAvatar(@NonNull Account account, } public static void downloadIcon(CurrentAccountProvider currentAccountProvider, + ClientFactory clientFactory, Context context, String iconUrl, SimpleTarget imageView, @@ -523,7 +525,8 @@ public static void downloadIcon(CurrentAccountProvider currentAccountProvider, int height) { try { if (iconUrl.endsWith(".svg")) { - downloadSVGIcon(currentAccountProvider, context, iconUrl, imageView, placeholder, width, height); + downloadSVGIcon(currentAccountProvider, clientFactory, context, iconUrl, imageView, placeholder, width, + height); } else { downloadPNGIcon(context, iconUrl, imageView, placeholder); } @@ -544,6 +547,7 @@ private static void downloadPNGIcon(Context context, String iconUrl, SimpleTarge } private static void downloadSVGIcon(CurrentAccountProvider currentAccountProvider, + ClientFactory clientFactory, Context context, String iconUrl, SimpleTarget imageView, @@ -551,7 +555,7 @@ private static void downloadSVGIcon(CurrentAccountProvider currentAccountProvide int width, int height) { GenericRequestBuilder requestBuilder = Glide.with(context) - .using(new CustomGlideUriLoader(currentAccountProvider), InputStream.class) + .using(new CustomGlideUriLoader(currentAccountProvider, clientFactory), InputStream.class) .from(Uri.class) .as(SVG.class) .transcode(new SvgDrawableTranscoder(), PictureDrawable.class) diff --git a/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java b/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java index 51345864b054..196b882af1bd 100644 --- a/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java +++ b/src/main/java/com/owncloud/android/utils/glide/CustomGlideStreamLoader.java @@ -25,6 +25,7 @@ import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.model.stream.StreamModelLoader; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.network.ClientFactory; import java.io.InputStream; @@ -34,13 +35,15 @@ public class CustomGlideStreamLoader implements StreamModelLoader { private final CurrentAccountProvider currentAccount; + private final ClientFactory clientFactory; - public CustomGlideStreamLoader(CurrentAccountProvider currentAccount) { + public CustomGlideStreamLoader(CurrentAccountProvider currentAccount, ClientFactory clientFactory) { this.currentAccount = currentAccount; + this.clientFactory = clientFactory; } @Override public DataFetcher getResourceFetcher(String url, int width, int height) { - return new HttpStreamFetcher(currentAccount, url); + return new HttpStreamFetcher(currentAccount, clientFactory, url); } } diff --git a/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.java b/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.java index 48694c0180c4..5216aea35b16 100644 --- a/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.java +++ b/src/main/java/com/owncloud/android/utils/glide/CustomGlideUriLoader.java @@ -25,6 +25,7 @@ import com.bumptech.glide.load.data.DataFetcher; import com.bumptech.glide.load.model.stream.StreamModelLoader; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.network.ClientFactory; import java.io.InputStream; @@ -34,13 +35,15 @@ public class CustomGlideUriLoader implements StreamModelLoader { private final CurrentAccountProvider currentAccount; + private final ClientFactory clientFactory; - public CustomGlideUriLoader(CurrentAccountProvider currentAccount) { + public CustomGlideUriLoader(CurrentAccountProvider currentAccount, ClientFactory clientFactory) { this.currentAccount = currentAccount; + this.clientFactory = clientFactory; } @Override public DataFetcher getResourceFetcher(Uri url, int width, int height) { - return new HttpStreamFetcher(currentAccount, url.toString()); + return new HttpStreamFetcher(currentAccount, clientFactory, url.toString()); } } diff --git a/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java b/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java index 3af6e41838e2..ae12ac31f3c6 100644 --- a/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java +++ b/src/main/java/com/owncloud/android/utils/glide/HttpStreamFetcher.java @@ -27,6 +27,8 @@ import com.bumptech.glide.Priority; import com.bumptech.glide.load.data.DataFetcher; import com.nextcloud.client.account.CurrentAccountProvider; +import com.nextcloud.client.account.User; +import com.nextcloud.client.network.ClientFactory; import com.owncloud.android.MainApp; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; @@ -47,30 +49,30 @@ public class HttpStreamFetcher implements DataFetcher { private static final String TAG = HttpStreamFetcher.class.getName(); private final String url; private final CurrentAccountProvider currentAccount; + private final ClientFactory clientFactory; - HttpStreamFetcher(final CurrentAccountProvider currentAccount, final String url) { + HttpStreamFetcher(final CurrentAccountProvider currentAccount, ClientFactory clientFactory, final String url) { this.currentAccount = currentAccount; + this.clientFactory = clientFactory; this.url = url; } @Override public InputStream loadData(Priority priority) throws Exception { - Account account = currentAccount.getCurrentAccount(); - OwnCloudAccount ocAccount = new OwnCloudAccount(account, MainApp.getAppContext()); - OwnCloudClient mClient = OwnCloudClientManagerFactory.getDefaultSingleton(). - getClientFor(ocAccount, MainApp.getAppContext()); + User user = currentAccount.getUser(); + OwnCloudClient client = clientFactory.create(user); - if (mClient != null) { + if (client != null) { GetMethod get; try { get = new GetMethod(url); get.setRequestHeader("Cookie", "nc_sameSiteCookielax=true;nc_sameSiteCookiestrict=true"); get.setRequestHeader(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE); - int status = mClient.executeMethod(get); + int status = client.executeMethod(get); if (status == HttpStatus.SC_OK) { return get.getResponseBodyAsStream(); } else { - mClient.exhaustResponse(get.getResponseBodyAsStream()); + client.exhaustResponse(get.getResponseBodyAsStream()); } } catch (Exception e) { Log_OC.e(TAG, e.getMessage(), e); diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 4aac77165312..185272de0e65 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -868,9 +868,6 @@ Music Documents Downloads - IO error - Operation has been canceled - Authentication Exception Avatar from shared user Shared with you by %1$s Resharing is not allowed From b91136c3620e8c5af7cf2eaa5ef16e9c1f89a76c Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 25 Nov 2019 10:14:38 +0100 Subject: [PATCH 47/69] Disable android backup on the QA app Signed-off-by: Alice Gaudon --- src/qa/AndroidManifest.xml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/qa/AndroidManifest.xml diff --git a/src/qa/AndroidManifest.xml b/src/qa/AndroidManifest.xml new file mode 100644 index 000000000000..33ba737d83d5 --- /dev/null +++ b/src/qa/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + From 11687c65cb2c1745bca5c8b4896cca55f7f8cb6e Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot Date: Mon, 25 Nov 2019 12:22:26 +0000 Subject: [PATCH 48/69] Drone: update FindBugs results to reflect reduced error/warning count [skip ci] Signed-off-by: nextcloud-android-bot --- scripts/analysis/findbugs-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 97964b3fde64..2da56739379b 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -422 \ No newline at end of file +418 \ No newline at end of file From b7876c4252216aaeb7163f56e6b866f9ad2d257a Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Mon, 25 Nov 2019 15:26:08 +0100 Subject: [PATCH 49/69] disable GoogleAppIndexingWarning lint check Signed-off-by: tobiasKaminsky --- build.gradle | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index b3d3254d8b88..2867606f03dd 100644 --- a/build.gradle +++ b/build.gradle @@ -105,7 +105,12 @@ android { abortOnError false htmlReport true htmlOutput file("$project.buildDir/reports/lint/lint.html") - disable 'MissingTranslation', 'GradleDependency', 'VectorPath', 'IconMissingDensityFolder', 'IconDensities' + disable 'MissingTranslation', + 'GradleDependency', + 'VectorPath', + 'IconMissingDensityFolder', + 'IconDensities', + 'GoogleAppIndexingWarning' } dexOptions { From 9f24b254f9111cac7a3f1ed22cd4658738aca76d Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Mon, 25 Nov 2019 14:33:17 +0000 Subject: [PATCH 50/69] [tx-robot] updated from transifex --- .../android/cs-CZ/short_description.txt | 2 +- src/main/res/values-b+en+001/strings.xml | 4 - src/main/res/values-ca/strings.xml | 4 - src/main/res/values-cs-rCZ/strings.xml | 178 +++++++++--------- src/main/res/values-da/strings.xml | 4 - src/main/res/values-de/strings.xml | 4 - src/main/res/values-el/strings.xml | 2 - src/main/res/values-es/strings.xml | 4 - src/main/res/values-eu/strings.xml | 10 +- src/main/res/values-fi-rFI/strings.xml | 4 - src/main/res/values-fr/strings.xml | 4 - src/main/res/values-gl/strings.xml | 4 - src/main/res/values-hr/strings.xml | 4 - src/main/res/values-hu-rHU/strings.xml | 14 +- src/main/res/values-it/strings.xml | 4 - src/main/res/values-iw/strings.xml | 4 - src/main/res/values-ja-rJP/strings.xml | 3 - src/main/res/values-ko/strings.xml | 3 - src/main/res/values-lt-rLT/strings.xml | 22 ++- src/main/res/values-lv/strings.xml | 1 - src/main/res/values-mk/strings.xml | 4 - src/main/res/values-nb-rNO/strings.xml | 1 - src/main/res/values-nl/strings.xml | 4 - src/main/res/values-pl/strings.xml | 4 - src/main/res/values-pt-rBR/strings.xml | 4 - src/main/res/values-ru/strings.xml | 2 - src/main/res/values-sk-rSK/strings.xml | 4 - src/main/res/values-sl/strings.xml | 4 +- src/main/res/values-sq/strings.xml | 4 - src/main/res/values-sr/strings.xml | 14 +- src/main/res/values-sv/strings.xml | 4 - src/main/res/values-tr/strings.xml | 20 +- src/main/res/values-zh-rCN/strings.xml | 4 - src/main/res/values-zh-rTW/strings.xml | 4 - .../android/cs-CZ/short_description.txt | 2 +- 35 files changed, 136 insertions(+), 222 deletions(-) diff --git a/src/generic/fastlane/metadata/android/cs-CZ/short_description.txt b/src/generic/fastlane/metadata/android/cs-CZ/short_description.txt index 056cf107f752..01a56a453767 100644 --- a/src/generic/fastlane/metadata/android/cs-CZ/short_description.txt +++ b/src/generic/fastlane/metadata/android/cs-CZ/short_description.txt @@ -1 +1 @@ -Produktivní platforma hostovaná u vás, kde vaše data jsou opravdu vaše \ No newline at end of file +Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše \ No newline at end of file diff --git a/src/main/res/values-b+en+001/strings.xml b/src/main/res/values-b+en+001/strings.xml index b0acec47d7f9..63444868a0a9 100644 --- a/src/main/res/values-b+en+001/strings.xml +++ b/src/main/res/values-b+en+001/strings.xml @@ -528,17 +528,13 @@ Incorrect password Login via QR code Protecting your data - self-hosted productivity platform Browse and share all actions at your fingertips - Activity, shares, offline files everything quickly accessible All your accounts in one place Automatic upload for your photos & videos - Sync calendar & contacts - with DAVx5 (formerly DAVdroid) Search users and groups Select all Select template diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index aa95adf28923..dcfd0c5a9553 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -536,17 +536,13 @@ Contrasenya incorrecta Inicia la sessió via un codi QR Protegint les vostres dades - plataforma de productivitat auto-hostatjada Explorar i compartir totes les accions als vostres dits - Activitat, comparticions, fitxers fóra de línia tot molt ràpidament accesible Tots els vostres comptes En un sol lloc Pujada automàtica pels vostres vídeos i imatges - Sincronitzar el calendari i els contactes - amb DAVx5 (formalment DAVdroid) Cerca usuaris i grups Selecciona-ho tot Selecciona una plantilla diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index 838055e1afc8..1efe7a6819ee 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -43,7 +43,7 @@ Zkontrolovat server Spojení navázáno Zadejte stávající heslo - Váš server nevrací správný přihlašovací identifikátor, obraťte se na svého správce systému + Vámi používaný server nevrací správný identifikátor uživatele – obraťte se na správce serveru Adresa serveru Adresa serveru https://… Byla použita neplatná přihlašovací URL @@ -51,9 +51,9 @@ Server nenalezen Žádné síťové spojení Zabezpečené spojení není k dispozici - Neplatné nastavení serveru - Neúspěšné přihlášení - Přístup zamítnut autorizačním serverem + Nesprávně formulované nastavení serveru + Neúspěšné ověření se + Přístup zamítnut pověřovacím (autorizace) serverem Heslo Zabezpečené spojení je přesměrováváno nezabezpečenou trasou. Obnovit připojení @@ -63,10 +63,10 @@ Testuje se připojení Čekání na odpověď ze serveru bylo příliš dlouhé Pokus o přihlášení… - Nesprávné přihlašovací jméno nebo heslo + Nesprávné uživatelské jméno nebo heslo Neznámá chyba: %1$s Došlo k neznámé HTTP chybě! - Nastala neznámá chyba + Došlo k neznámé chybě Nedaří se najít hostitele %1$s nepodporuje vícero účtů Uživatelské jméno @@ -100,7 +100,7 @@ Zrušit synchronizaci Vybrat účet Smazat - Error + Chyba Nedostatek paměti Neznámá chyba Načítání… @@ -115,7 +115,7 @@ Přepnout účet neznámý Ano - Vyzkoušet noční sestavení + Vyzkoušet vývojové sestavení Zahrnuje všechny nadcházející funkce a pohybuje se na hraně stability. Mohou se vyskytnout chyby, a pokud ano, nahlaste nám je prosím. diskuzní fórum Pomozte ostatním na @@ -129,10 +129,10 @@ Získat kandidáta na vydání z aplikace F-Droid Získat kandidáta na vydání z Google Play store Kandidát na vydání - Kandidát na vydání (RC) je snímkem nadcházejícího vydání a očekává se, že bude stabilní. Testování vašeho individuálního nastavení by to mohlo pomoci to zajistit. Přihlaste se k testování v Obchodě Play nebo se podívejte do F-Droid v části „Verze“ a přihlaste se ručně. + Kandidát na vydání (RC) je zachyceným stavem nadcházejícího vydání a očekává se, že bude stabilní. Testování vašeho individuálního nastavení by to mohlo pomoci to zajistit. Přihlaste se k testování v katalogu aplikací Play nebo se podívejte do F-Droid v části „Verze“ a přihlaste se ručně. Našli jste chybu? Něco podivného? Pomozte testováním - Nahlásit chybu na Github + Nahlásit problém prostřednictvím portálu Github Chcete nám pomoci zkoušet nadcházející verzi? Nastavit Opravdu chcete odstranit %1$s? @@ -148,11 +148,11 @@ Obnovit vybrané kontakty Vyberte účet, který chcete importovat Ikona uživatele v seznamu kontaktů - Práva nejsou přidělena, nic nebylo naimportováno. + Nejsou udělena oprávnění, proto nebylo nic naimportováno. Automatické zálohování Zálohovat nyní - Poslední záloha - Nikdy + Nejnovější záloha + nikdy Zvolte datum Zálohování je naplánováno a brzy začne Import je naplánován a brzy začne @@ -177,7 +177,7 @@ Vytvořit novou složku Vytvořit novou prezentaci Vytvořit nový sešit - Přihlašovací údaje zakázány + Přihlašovací údaje znepřístupněny Neznámé Neplatné pověření Odebrat účet @@ -194,8 +194,8 @@ Stáhnout si nejnovější vývojářskou verzi Nepodařilo se stáhnout %1$s Stahování se nezdařilo, přihlaste se znovu - Stažení se nezdařilo - Tento soubor už není dostupný na serveru + Stahování se nezdařilo + Tento soubor už není na serveru k dispozici %1$d%% Stahuje se %2$s Stahování… %1$s staženo @@ -210,7 +210,7 @@ Všechny soubory Oblíbené Domů - Upozornění + Oznámení Na přístroji Fotky Nedávno přidané @@ -234,7 +234,7 @@ Zadejte heslo pro rozšifrování soukromého klíče. Tato složka není prázdná. Vytváření nových klíčů… - Všech 12 slov dohromady tvoří velmi silné heslo, které pouze vám umožní prohlížet a používat vaše zašifrované soubory. Poznamenejte si ho někam na bezpečné místo. + Všech 12 slov dohromady tvoří velmi silné heslo, které pouze vám umožní prohlížet a používat vaše zašifrované soubory. Poznamenejte si ho a někam bezpečně uložte. End-to-end šifrování zakázáno na serveru. Šifrování funguje pouze v KitKat (4.4) a výše. Poznamenejte si někam své 12 slovné šifrovací heslo @@ -243,7 +243,7 @@ Ukládání klíčů Nastavit šifrování Nepodařilo se uložit klíče, zkuste to znovu. - Chyba při rozšifrovávání. Chybné heslo? + Chyba při rozšifrovávání. Nesprávné heslo? Zadejte název souboru %1$s nelze zkopírovat do místní složky %2$s Kritická chyba: operace nelze provést @@ -269,37 +269,37 @@ Nepodařilo se načíst podrobnosti. Soubor Ponechat - Nahrajte nějaký obsah, nebo synchronizujte s vašimi zařízeními! - Zatím není nic oblíbeno + Nahrajte nějaký obsah, nebo synchronizujte s vašimi zařízeními. + Zatím žádné oblíbené položky Vaše vyhledávání nenalezlo žádné oblíbené soubory - Zde budou zobrazeny soubory a složky označené jako oblíbené + Zde budou zobrazeny soubory a složky, které označíte jako oblíbené Nejsou zde žádné soubory V této složce nebylo nic nalezeno Žádné výsledky - Žádná fotka - Žádné video + Žádné fotky + Žádná videa Nic tu není. Můžete přidat složku. Nenalezeny žádné nedávno přidané soubory Žádné nedávno přidané soubory. Nenalezeny žádné soubory změněné za uplynulých 7 dnů - Žádné soubory nebyly v posledních sedmi dnech změněny. + V uplynulých sedmi dnech nebyly změněny žádné soubory. Zkusit hledat v jiné složce? Zde budou zobrazeny vámi sdílené soubory a složky - Zatím nebylo nic sdílené - Nahrajte nějaké fotky nebo aktivujte automatické nahrávání. + Zatím nebylo nic sdíleno + Nahrajte nějaké fotky nebo zapněte automatické nahrávání. Žádné fotky. - Nahrajte nějaká videa nebo aktivujte automatické nahrávání. + Nahrajte nějaká videa nebo zapněte automatické nahrávání. Žádná videa. složka Načítání… - Pro tento typ souboru nebyla nalezena aplikace! + Není nastaveno pomocí jaké aplikace otevírat tento typ souboru. před několika sekundami Prověřování cílového umístění… Čištění… Aktualizace popisu umístění úložiště - Datová složka už existuje. Zvolte z následujících možností: + Datová složka už existuje. Vyberte z následujících možností: Nextcloud složka už existuje - Je třeba více místa + Je zapotřebí více místa Nedaří se číst ze zdrojového souboru Nedaří se zapsat do cílového souboru Nezdar během migrace @@ -310,7 +310,7 @@ Příprava migrace… Obnovování nastavení účtu… Ukládání nastavení účtu… - Opravdu chcete změnit složku úložiště na %1$s?\n\nPoznámka: Všechna data budou stažena znovu. + Pořád ještě chcete chcete změnit popis umístění úložiště na %1$s?\n\nPoznámka: Všechna data bude třeba znovu stáhnout. Zdrojová složka není čitelná! Aktualizace rejstříku… Použít @@ -330,9 +330,9 @@ Název souboru Toto je funkce Nextcloud, přejděte na něj. Mějte svá data zabezpečená a pod svou správou - Zabezpečená spolupráce & výměna souborů - Snadno použitelný webový e-mailový klient, kalendáře & kontakty - Sdílení obrazovky, online schůzky & webové konference + Zabezpečená spolupráce a výměna souborů + Snadno použitelný webový e-mailový klient, kalendáře a kontakty + Sdílení obrazovky, schůzky na dálku a webové konference složka Složka už existuje Vytvořit @@ -363,35 +363,35 @@ vst/výstup. chyba Zjistit více Odkaz - Náhled seznamu + Zobrazit jako seznam V této složce nejsou žádné soubory. Soubor nenalezen na souborovém systému - Nejsou zde další složky. + Nejsou zde žádné další složky. Záznamy událostí v aplikaci %1$s pro Android - Nebyla nalezena aplikace k odesílání záznamů událostí. Nainstalujte e-mailového klienta. + Nebyla nalezena žádná aplikace, přes kterou by bylo možné odeslat záznamy událostí. Nainstalujte si e-mailového klienta. Přihlásit Smazat záznamy událostí - Obnovit + Načíst znovu Prohledat záznamy událostí Posílat záznamy událostí e-mailem - Logy: %1$d kB, dotaz nalezl %2$d / %3$d v %4$d ms + Záznamy událostí: %1$d kB, dotazu odpovídalo %2$d / %3$d v %4$d ms Načítání… - Logy: %1$d kB, žádný filtr + Záznamy událostí: %1$d kB, žádný filtr Záznamy událostí - Server je v režimu údržby + Na serveru probíhá údržba Vyčistit data - Nastavení, databáze a certifikáty serverů z %1$s data budou natrvalo smazány. \n\nStažené soubory tak, jak jsou.\n\nTento proces může chvíli trvat. + Nastavení, databáze a certifikáty serverů z dat %1$s budou natrvalo smazány. \n\nStažené soubory zůstanou beze změny.\n\nTento proces může chvíli trvat. Spravovat úložný prostor - Multimediální soubor nelze proudově odesílat + Soubor s médii se nedaří proudově vysílat (stream) Nepodařilo se přečíst soubor médií Soubor médií nemá platné kódování Překročen časový limit pokusu o přehrání souboru - Multimediální soubor se nedaří přehrát vestavěným přehrávačem + Soubor s médii se nedaří přehrát vestavěným přehrávačem Nepodporovaný kodek médií Tlačítko Rychle vpřed Hudební přehrávač %1$s Tlačítko Přehrát/Pozastavit - Tlačítko Přetočit + Tlačítko přeskočit na začátek/konec %1$s (přehrává) Nejnovější jako první Nejstarší jako první @@ -399,15 +399,15 @@ Z - A Největší jako první Nejmenší jako první - Při pokusu o přesun tohoto souboru či složky nastala chyba + Při pokusu o přesun tohoto souboru či složky došlo k chybě Není možné přesunout složku do její vlastní podsložky Soubor už v cílové složce existuje - Soubor se nedaří přesunout. Zkontrolujte zda existuje + Soubor se nedaří přesunout – zkontrolujte zda existuje Přesunout do… Při čekání na server došlo k chybě. Operace nemohla být dokončena Při připojení k serveru došlo k chybě Při čekání na server došlo k chybě. Operace nemohla být dokončena - Operace nemohla být dokončena, server není dostupný + Operace nemohla být dokončena, protože server není dostupný Nový komentář… Zjištěna nová složka s médii %1$s. foto @@ -440,32 +440,32 @@ Vraťte se sem později. Bez připojení k Internetu Operace byla zrušena - Verze aplikace na straně serveru dosáhla konce své životnosti – přejděte na novou! + Verze aplikace na straně serveru už je příliš stará na to, aby šla nadále používat – přejděte na novou! Další nabídka Zadejte svůj bezpečnostní kód Bezpečnostní kód bude vyžadován při každém spuštění aplikace Zadejte svůj bezpečnostní kód - Bezpečnostní kód se liší - Zopakujte svůj bezpečnostní kód + Zadání bezpečnostního kódu se neshodují + Zopakujte zadání svého bezpečnostního kódu Odstraňte svůj bezpečnostní kód Bezpečnostní kód odstraněn Bezpečnostní kód uložen Nesprávný bezpečnostní kód Umožnit Odepřít - Vyžadována dodatečná oprávnění pro nahrávání & stahování souborů. + Vyžadována dodatečná oprávnění pro nahrávání a stahování souborů. Nenalezena aplikace pro nastavení obrázku 389 KB - placeholder.txt + jen_jako_vypln.txt 12:23:45 Toto je zástupný text 2012/05/18 12:23 odp. Zákaz kontroly úspory energie může způsobit nahrávání souborů i v případě nízkého stavu baterie! smazáno - ponechán v původní složce - přesunut do složky aplikace + ponecháno v původní složce + přesunuto do složky aplikace Přidat účet - Synchronizovat kalendář & kontakty + Synchronizovat kalendář a kontakty Adresu serveru pro účet se pro DAVx5 (dříve známé pod názvem DAVdroid) nepodařilo přeložit F-Droid ani Google Play není nainstalováno Nastavit pro stávající účet DAVx5 (dříve známé pod názvem DAVdroid) (v1.3.0+) @@ -501,7 +501,7 @@ Získat zdrojový kód Popis umístění úložiště Místní složka - Vzdálená složka + Složka na protějšku Motiv Tmavý Světlý @@ -513,8 +513,8 @@ Push oznámení zakázáno kvůli závislostem na proprietárních službách Google Play. Žádné push oznámení kvůli zastaralé relaci přihlášení. Zvažte opětovné přidání svého účtu. Push oznámení momentálně nejsou k dispozici. - Vyzkoušejte %1$s ve svém telefonu! - Rád bych vás pozval(a) k používání %1$s ve vašem telefonu.\nKe stažení zde: %2$s + Vyzkoušejte %1$s ve svém zařízení! + Rád(a) bych Vás/Tě pozval(a) k používání %1$s na vašem/tvém zařízení.\nKe stažení zde: %2$s %1$s nebo %2$s Nepodařilo se najít soubor! Odstranění se nezdařilo @@ -522,10 +522,10 @@ Odebrat Smazáno Zadejte nový název - Nepodařilo se přejmenovat místní kopii, použijte jiný název + Nepodařilo se přejmenovat místní kopii, zkuste použít jiný název Přejmenování není možné, název už je použit - Sdílet sdílenou složku dál není umožněno - Opětovné sdílení není povoleno + Příjemcům tohoto sdílení není dovoleno ho nasdílet dál dalším + Příjemcům tohoto sdílení není dovoleno ho nasdílet dál dalším Nejsou k dispozici žádné zmenšené obrázky. Zmenšená verze obrázku není k dispozici. Stáhnout obrázek v plné velikosti? Obnovit soubor @@ -536,18 +536,14 @@ Nesprávné heslo Přihlášení prostřednictvím QR kódu Ochrana vašich dat - Platforma pro produktivitu, hostovaná u vás Procházet a sdílet všechny akce na dosah ruky - Aktivity, sdílení, soubory offline vše pohotově přístupné Všechny vaše účty na jednom místě Automatické nahrání pro vaše fotky a videa - Synchronizovat kalendář a kontakty - pomocí DAVx5 (dříve DAVdroid) - Prohledat uživatele a skupiny + Hledat uživatele a skupiny Vybrat vše Vybrat šablonu Odeslat @@ -559,7 +555,7 @@ Sdílet Přidat uživatele nebo skupinu Sdílení - %1$s (email) + %1$s (e-mail) Platnost skončí %1$s Sdílet %1$s Vytvořit odkaz @@ -568,8 +564,8 @@ Bude fungovat pouze pro uživatele s přístupem do tohoto %1$s %1$s (v %2$s) Je třeba zadat heslo - Při pokusu o sdílení tohoto souboru či složky nastala chyba - Nelze sdílet. Zkontrolujte zda soubor existuje + Při pokusu o sdílení tohoto souboru či složky došlo k chybě + Nedaří se sdílet – zkontrolujte zda soubor existuje pro sdílení tohoto souboru Zadejte nepovinné heslo Zadejte heslo @@ -585,16 +581,16 @@ Zrušit sdílení %1$s (vzdálený) %1$s (konverzace) - Jméno, sdružené cloud ID, nebo e-mailová adresa… + Jméno, identifikátor v rámci sdruženého cloudu, nebo e-mailová adresa… Poznámka pro příjemce - Povolit úpravy + Umožnit úpravy Nastavit datum skončení platnosti Skrýt stahování Skrýt seznam souborů Chránit heslem (%1$s) Chránit heslem Zabezpečeno - Sdílet odkaz + Odkaz na sdílení Poslat odkaz Zrušit nastavení Sdílet s %1$s @@ -619,7 +615,7 @@ Podrobnosti Totožnost serveru se nepodařilo ověřit Země: - Běžný název: + Běžný název (CN): Umístění: Organizace: Organizační jednotka: @@ -634,12 +630,12 @@ Pro: - Žádné informace o této chybě Certifikát se nedaří uložit - Certifikát nemohl být zobrazen. + Certifikát se nepodařilo zobrazit. Přejete si přesto tomuto certifikátu důvěřovat? - Certifikátu serveru skončila platnost - Certifikát serveru není důvěryhodný - certifikát serveru ještě nezačal platit (datum začátku platnosti je v budoucnosti) - - URL adresa neodpovídá názvu stroje v certifikátu + - URL adresa neodpovídá názvu hostitele v certifikátu Kamera Zvolte umístění úložiště Výchozí @@ -669,8 +665,8 @@ Připomínky zasílejte na https://github.com/nextcloud/android/issues, diskuto Kompletní privátní řešení pro všechy vaše soubory, fotky, kalendáře a kontakty. Toto je oficiální vývojová verze aktualizovaná denně. Obsahuje nejnovější fukcionaliti, ale může být nestabilní a ohrozit vaše data. Je pro uživatele, kteří jsou ochotni testovat a hlásit chyby na které narazí. Nepoužívejte ji pro svoje produkční prostředí. Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nainstalovány souběžně. - Produktivní platforma hostovaná u vás, kde vaše data jsou opravdu vaše - Produktivní platforma hostovaná u vás, kde vaše data jsou opravdu vaše (vývojová verze) + Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše + Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše (vývojová verze) Proudově vysílat s… Vnitřní proudové vysílání není možné Médium si namísto toho stáhněte nebo použijte externí aplikaci. @@ -679,13 +675,13 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Nalezeny konflikty Složka %1$s už neexistuje Nepodařilo se synchronizovat %1$s - Chybné heslo pro %1$s + Nesprávné heslo pro %1$s Automatická synchronizace souborů se nezdařila Synchronizace se nezdařila Synchronizace se nezdařila, přihlaste se znovu Obsah souboru už je synchronizován Synchronizaci složky %1$s se nedaří dokončit - Počínaje verzí 1.3.16, soubory nahrané z tohoto zařízení jsou zkopírovány do místní složky %1$s, aby se zabránilo ztrátě dat když je jeden soubor synchronizován s vícero účty.\n\nKvůli této změně byly všechny soubory, nahrané starší verzí zkopírovány do složky %2$s. Nicméně, chyba při synchronizaci účtu zabránila dokončení této operace. Buď tyto soubory můžete ponechat, jak jsou a smazat odkaz na %3$s, nebo je přesunout do složky %1$s ponechat odkaz na %4$s.\n\nNíže jsou vypsány místní soubory a vzdálené soubory v %5$s na který byly odkazovány. + Od verze 1.3.16, soubory nahrané z tohoto zařízení jsou zkopírovány do místní složky %1$s, aby se zabránilo ztrátě dat, když je jeden soubor synchronizován s vícero účty.\n\nKvůli této změně byly všechny soubory, nahrané starší verzí zkopírovány do složky %2$s. Nicméně, chyba při synchronizaci účtu zabránila dokončení této operace. Buď tyto soubory můžete ponechat, jak jsou a smazat odkaz na %3$s, nebo je přesunout do složky %1$s ponechat odkaz na %4$s.\n\nNíže jsou vypsány místní soubory a vzdálené soubory v %5$s na který byly odkazovány. Některé místní soubory byly zapomenuty Stahuje se nejnovější verzi souboru. Tlačítko Stav synchronizace @@ -713,8 +709,8 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Existuje nepřečtený komentář Zrušit šifrování Odebrat z oblíbených - Při pokusu o zrušení sdílení tohoto souboru či složky nastala chyba - Nedaří se ukončit sdílení. Zkontrolujte že soubor existuje + Při pokusu o zrušení sdílení tohoto souboru či složky došlo k chybě + Nedaří se ukončit sdílení – kontrolujte zda soubor existuje pro zrušení sdílení tohoto souboru Zrušení sdílení se nezdařilo Přístup prostřednictvím nedůvěryhodné domény. Více informací naleznete v dokumentaci. @@ -728,19 +724,19 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Nahrát z kamery Název souboru Typ souboru - Soubor zkratky pro Google mapy(%s) - Soubor zástupce na Internet(%s) - Soubor textového úryvku(.txt) + Soubor s odkazem do map Google (%s) + Soubor s odkazem na webovou stránku (%s) + Soubor textového úryvku (.txt) Název a typ vstupního souboru k nahrání Nahrát soubory Tlačítko akce Nahrát položku Nic k nahrání - Nahrajte nějaký obsah nebo aktivujte automatické nahrávání. + Nahrajte nějaký obsah nebo zapněte automatické nahrávání. Místní úložiště je plné Soubor se nepodařilo zkopírovat na místní úložiště Uzamčení složky se nezdařilo - Pro zkopírování vybraných souborů do složky %1$s není dostatek volného místa. Chcete je místo toho přesunout? + Pro zkopírování vybraných souborů do složky %1$s není dostatek volného místa. Chcete je tam namísto toho přesunout? Konflikt synchronizace, vyřešte ručně Neznámá chyba Vybrat @@ -748,15 +744,15 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Obdržená data neobsahují žádný platný soubor. %1$s nemá oprávnění číst přijatý soubor Došlo k chybě při kopírování souboru do dočasné složky. Zkuste zopakovat odeslání. - Soubor označený k nahrání nebyl v tomto umístění nalezen. Zkontrolujte zda soubor existuje. + Soubor označený k nahrání nebyl nalezen. Zkontrolujte zda soubor existuje. Soubor se nedaří nahrát Žádný soubor k nahrání Název složky - Vybrat složku k nahrávání + Vybrat složku pro nahrávání Nepodařilo se nahrát %1$s Nahrání se nezdařilo, znovu se přihlaste Odesílání se nezdařilo - Nahrát možnost: + Předvolba nahrávání: Přesunout soubor do složky %1$s zdrojová složka je pouze pro čtení, soubor bude pouze nahrán Ponechat soubor ve zdrojové složce diff --git a/src/main/res/values-da/strings.xml b/src/main/res/values-da/strings.xml index d63a3d0af89a..8978bcdfb70f 100644 --- a/src/main/res/values-da/strings.xml +++ b/src/main/res/values-da/strings.xml @@ -532,17 +532,13 @@ Enheds legitimationsoplysninger er sat op Forkert kodeord Login med QR kode Beskytter dine data - Vært i eget hus produktivitets platform Defiler og del Alle funktioner lige ved hånden - Aktiviteter, delinger, offline filer alt hurtigt tilgængeligt Alle dine konti på et sted Automatisk upload Til dine billeder & videoer - Synk kalender & kontakter - med DAVx5 (forhenværende DAVdroid) Søg brugere og grupper Vælg alle Vælg model diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index db5626110b82..07ef53acf682 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -536,17 +536,13 @@ Falsches Passwort Anmelden mit QR-Code Schützt Ihre Daten - Die Selbst-Gehostete Plattform für Produktivität Durchsuchen und teilen Alle Aktionen schnell erreichbar - Aktivitäten, Freigaben, Offline-Dateien Alles schnell erreichbar All ihre Konten an einem Ort Automatisches Hochladen ihrer Fotos & Videos - Synchronisieren sie Kalender & Kontakte - mit DAVx5 (zuvor DAVdroid) Nutzer und Gruppen suchen Alle auswählen Vorlage auswählen diff --git a/src/main/res/values-el/strings.xml b/src/main/res/values-el/strings.xml index afb81608f1cd..f4b927e29700 100644 --- a/src/main/res/values-el/strings.xml +++ b/src/main/res/values-el/strings.xml @@ -522,8 +522,6 @@ σε ένα σημείο Αυτόματη μεταφόρτωση για τις φωτογραφίες & τα βίντεό σας - Συγχρονισμός ημερολογίου & επαφών - με DAVx5 (πρώην DAVdroid) Αναζήτηση χρηστών και ομάδων Επιλογή όλων Επιλογή προτύπου diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 9db39e4efd30..4a8bf1830a60 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -536,17 +536,13 @@ Contraseña incorrecta Entrar vía código QR Protegiendo sus datos - plataforma de productividad auto hospedada Navegar y compartir todas las acciones en la punta de sus dedos - Actividad, compartir, archivos offline todo accesible rápidamente Todas sus cuentas en un lugar Subida automática para sus fotos & vídeos - Sincronice calendario & contactos - con DAVx5 (anteriormente DAVdroid) Buscar usuarios y grupos Seleccionar todo Escoge plantilla diff --git a/src/main/res/values-eu/strings.xml b/src/main/res/values-eu/strings.xml index b76a021d19bb..aba3f5fed3a9 100644 --- a/src/main/res/values-eu/strings.xml +++ b/src/main/res/values-eu/strings.xml @@ -261,6 +261,7 @@ Lehengoratu saioa hasteko metodo zaharrera Gogokoenetara gehitu Gogokoa + fitxategia Ezabatu Huts egin du zehaztapenak kargatzen Fitxategia @@ -329,6 +330,7 @@ Lankidetza & fitxategi partekatze segurua Web posta, egutegi & kontaktu erabilerraza Pantaila partekatzea, online bilerak &web konferentziak + karpeta Karpeta dagoeneko existitzen da Sortu Karpetaren ikonoa @@ -410,6 +412,7 @@ Jakinarazpen berria Bertsio berria sortu da Ez dago eskuragarri estekak kudeatzeko aplikaziorik + Bakarrik kontu bat onartzen da Ez dago eskuragarri PDFak kudeatzeko aplikaziorik Bidali Ezin da oharrik bidali @@ -496,6 +499,9 @@ Biltegiratze helbidea Karpeta lokala Urruneko karpeta + Gaia + Iluna + Argia Irudi aurreikuspena Ez dago aurreikusteko fitxategi lokalik Ezin izan da irudia erakutsi @@ -526,17 +532,13 @@ Pasahitz okerra Saioa hasi QR kode bidez Zure datuak babesten - norberak ostatatutako produktibitate plataforma Arakatu eta partekatu ekintza guztiak zure atzamarretan - Jarduera, partekatzeak, lineaz kanpoko fitxategiak guztia azkar eskuragarri Zure kontu guztiak leku bakar batean Igoera automatikoa zure argazki eta bideoentzat - Egutegia eta kontaktuak sinkronizatu - DAVx5ekin (lehen DAVdroid) Bilatu erabiltzaile eta taldeak Hautatu dena Aukeratu txantiloia diff --git a/src/main/res/values-fi-rFI/strings.xml b/src/main/res/values-fi-rFI/strings.xml index 72c60006ae98..af655a3e6be3 100644 --- a/src/main/res/values-fi-rFI/strings.xml +++ b/src/main/res/values-fi-rFI/strings.xml @@ -530,17 +530,13 @@ GNU yleinen lisenssi, versio 2 Väärä salasana Kirjaudu QR-koodilla Tietojasi suojaten - Itse ylläpidettävä tuottavuusalusta Selaa ja jaa kaikki toiminnot lähettyvillä - Toiminnot, jaot ja yhteydettömän tilan tiedostot kaikki käytettävissä nopeasti Kaikki tilisi yhdessä paikassa Automaattinen lähetys kuvillesi ja videoillesi - Synkronoi kalenteri ja yhteystiedot - DAVx5:llä (aiemmin DAVdroid) Etsi käyttäjiä ja ryhmiä Valitse kaikki Valitse mallipohja diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index fcd563157e28..ce1085f0ff5e 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -538,17 +538,13 @@ Attention la suppression est irréversible. Mot de passe incorrect Connexion par code QR Protection de vos données - plateforme de productivité auto-hébergée Parcourir et partager toutes les actions à portée de main - Activité, partages, fichiers hors-ligne tout accessible rapidement Tous vos comptes en un seul endroit Téléversement automatique pour vos photos & vidéos - Synchronisez votre calendrier & vos contacts - avec DAVx5 (anciennement DAVdroid) Rechercher parmi les utilisateurs et groupes Tout sélectionner Sélectionnez un modèle diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index 2a59cc39c129..a05c71af6494 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -537,17 +537,13 @@ Contrasinal incorrecto Acceder cun código QR Protexendo os seus datos - plataforma de produtividade de aloxamento propio Navegar e compartir todas as accións ao seu alcance - Actividade, comparticións, ficheiros sen conexión todo accesíbel rapidamente Todas as súas contas nun só lugar Envío automático para as súas fotos e vídeos - Sincronizar o calendario e os contactos - con DAVx5 (anteriormente coñecido como DAVdroid) Buscar usuarios e grupos Seleccionar todo Seleccionar o modelo diff --git a/src/main/res/values-hr/strings.xml b/src/main/res/values-hr/strings.xml index e42102fafa01..b228f168d6c5 100644 --- a/src/main/res/values-hr/strings.xml +++ b/src/main/res/values-hr/strings.xml @@ -528,17 +528,13 @@ Pogrešna zaporka Prijavite se putem QR koda Zaštita podataka - samopostavljena platforma za pospješivanje produktivnosti Pretražujte i dijelite jednostavan pristup svim mogućnostima i radnjama - Aktivnosti, dijeljenje, izvanmrežne datoteke brz pristup svemu Svi vaši računi na jednom mjestu Automatsko otpremanje fotografija i videozapisa - Sinkroniziraj kalendar i kontakte - s aplikacijom DAVx5 (ranije poznatom kao DAVdroid) Pretraži korisnike i grupe Odaberi sve Odaberi predložak diff --git a/src/main/res/values-hu-rHU/strings.xml b/src/main/res/values-hu-rHU/strings.xml index 94e211f40195..95d273b956e3 100644 --- a/src/main/res/values-hu-rHU/strings.xml +++ b/src/main/res/values-hu-rHU/strings.xml @@ -263,7 +263,9 @@ Visszatérés a régi bejelentkezési módhoz Hozzáadás a kedvencekhez Kedvenc + fájlhoz Törlés + Hiba a fájl tevékenységeinek lekérésekor A részletek betöltése sikertelen Fájl Megtartás @@ -331,6 +333,7 @@ Biztonságos csoportmunka és fájlmegosztás Könnyen használható webes levelezés, naptárkezelés és névjegyek Képernyőmegosztás, online megbeszélések és webes konferenciák + mappához A mappa már létezik Létrehozás Mappaikon @@ -412,6 +415,7 @@ Új értesítés Új verzió létrehozva Nincs elérhető alkalmazás hivatkozások kezeléséhez + Csak egy fiók engedélyezett Nincs elérhető alkalmazás PDF kezeléséhez Küldés A jegyzet nem küldhető el @@ -498,6 +502,9 @@ Tároló útvonal Helyi mappa Távoli mappa + Téma + Sötét + Világos Kép előnézete Nincs helyi fájl az előnézethez A kép nem jeleníthető meg @@ -509,6 +516,7 @@ Próbálja ki a %1$sot az eszközén! Meg akarom hívni a %1$s használatára az eszközén.\nTöltse le itt: %2$s %1$s vagy %2$s + A fájl nem található. Törlés sikertelen Az értesítés eltávolítása sikertelen. Eltávolítás @@ -528,17 +536,13 @@ Hibás jelszó Belépés QR-kóddal Az adatai védelme - saját üzemeltetésű irodai platform Tallózás és megosztás minden művelet karnyújtásnyira - Tevékenységek, megosztások, offline fájlok minden gyorsan elérhető Az összes fiókja egy helyen Automatikus feltöltés a képeinek és videóinak - Naptár és névjegyek szinkronizálása - DAVx5 (régi nevén DAVdroid) segítségével Felhasználók és csoportok keresése Összes kiválasztása Válasszon sablont @@ -556,6 +560,8 @@ %1$s megosztása Hivatkozás beszerzése %1$s (csoport) + Belső hivatkozás megosztása + Csak azoknál a felhasználóknál működik, akiknek hozzáférése van ehhez a %1$s %1$s ( itt: %2$s ) Meg kell adnia a jelszót Hiba történt a fájl vagy mappa megosztásakor diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index c0bbc3d8cab2..1874a0eb4a05 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -536,17 +536,13 @@ Password errata Accedi tramite codice QR Proteggere i tuoi dati - piattaforma di produttività auto-gestita Sfoglia e condividi tutte le azioni a portata di mano - attività, condivisioni, file non in linea tutto rapidamente accessibile Tutti i tuoi account in un posto Caricamento automatico per e tue foto e i video - Sincronizza calendario e contatti - con DAVx5 (originariamente DAVdroid) Cerca utenti e gruppi Seleziona tutto Seleziona modello diff --git a/src/main/res/values-iw/strings.xml b/src/main/res/values-iw/strings.xml index bb79a328fb2c..bd6420aa2804 100644 --- a/src/main/res/values-iw/strings.xml +++ b/src/main/res/values-iw/strings.xml @@ -525,17 +525,13 @@ סיסמא שגוייה כניסה עם קוד QR הגנה על הנתונים שלך - פלטפורמת כלי משרד באירוח עצמי עיון ושיתוף כל הפעולות בהינף יד - פעילות, שיתופים, קבצים בלתי מקוונים הכול נגיש במהירות כל החשבונות שלך במקום אחד העלאה אוטומטית לתמונות ולסרטונים שלך - סנכרון לוח שנה ואנשי קשר - עם DAVx5 (לשעבר DAVdroid) חיפוש משתמשים וקבוצות בחר הכל בחירת תבנית diff --git a/src/main/res/values-ja-rJP/strings.xml b/src/main/res/values-ja-rJP/strings.xml index b84ed37a8cfa..654f3e16b341 100644 --- a/src/main/res/values-ja-rJP/strings.xml +++ b/src/main/res/values-ja-rJP/strings.xml @@ -526,12 +526,9 @@ QRコードを用いてログイン あなたのデータを保護 閲覧と共有 - アクティビティ、共有、オフラインファイル あなたのすべてのアカウント ひとつの場所に 自動アップロード - コンタクト&カレンダーを同期 - DAVx5(旧称:DAVdroid)で ユーザーとグループを検索 すべて選択 テンプレートを選択する diff --git a/src/main/res/values-ko/strings.xml b/src/main/res/values-ko/strings.xml index b1c00c446261..81955c34f08f 100644 --- a/src/main/res/values-ko/strings.xml +++ b/src/main/res/values-ko/strings.xml @@ -533,16 +533,13 @@ 잘못된 암호 QR 코드로 로그인 데이터 보호 - 자체 호스팅 생산성 플랫폼 탐색하고 공유 모든 터치 액션 - 활동, 공유, 오프라인 파일 빠르게 접근 가능한 모든 것. 모든 계정 한곳에서 자동 업로드 사진 & 동영상 - 달력 및 &연락처 동기화 사용자와 그룹 검색 모두 선택 템플릿 선택 diff --git a/src/main/res/values-lt-rLT/strings.xml b/src/main/res/values-lt-rLT/strings.xml index a5857ab8adb1..d16c9176d9c9 100644 --- a/src/main/res/values-lt-rLT/strings.xml +++ b/src/main/res/values-lt-rLT/strings.xml @@ -38,7 +38,7 @@ Šiame įrenginyje jau yra paskyra šiam naudotojui ir serveriui Įvestas naudotojas neatitinka šios paskyros naudotojo Nežinoma serverio versija - Nepavyko autentifikuotis šiame serveryje + Nepavyko nustatyti tapatybės šiame serveryje Patikrinti serverį Ryšys užmegztas Prašome įvesti dabartinį slaptažodį @@ -162,11 +162,11 @@ Atsisiunčiama… %1$s atsisiųsta Atsisiųsti - Dar neatsiųsta + Kol kas neatsisiųsta Užverti šoninę juostą Veiklos Visi failai - Mėgstamiausi + Mėgstami Namai Pranešimai Įrenginyje @@ -208,13 +208,13 @@ Nuostatos Nepavyko perduoti failo į atsiuntimų tvarkytuvę Atgal - Pridėti prie svarbiausių - Svarbiausias + Pridėti į mėgstamus + Mėgstami Ištrinti Failas Įkelkite tam tikrą turinį (medžiagą) arba sinchronizuokite su savo įrenginiais. - Nieko dar nepavyko - Pagal Jūsų pateiktą užklausą neradome šių failų. + Kol kas nėra mėgstamų + Jūsų paieška negrąžino jokių mėgstamų failų. Čia bus rodomi failai ir aplankai, kuriuos pažymėsite kaip mėgstamus Čia nėra failų Šiame aplanke nėra jokių rezultatų @@ -225,7 +225,7 @@ Neseniai pridėtų failų nerasta Nėra jokių paskiausiai pridėtų failų. Gal tai yra kitame aplanke? - Visi aplankai (failai), kuriais norite pasidalinti bus šioje vietoje + Čia atsiras visi jūsų bendrinami failai ar aplankai Niekuo nesidalinate Įkelkite nuotraukas arba įjunkite automatinį įkėlimą. Nuotraukų nėra. @@ -266,6 +266,10 @@ Failo pavadinime yra bent vienas neteisingas simbolis Failo pavadinimas Tai yra Nextcloud ypatybė, prašome atsinaujinti. + Saugiai laikykite ir valdykite savo duomenis + Saugus bendradarbiavimas ir apsikeitimas failais + Lengvai naudojamas el. paštas, kalendorius bei adresatai + Ekrano bendrinimas, internetiniai susitikimai bei konferencijos Aplankas jau yra Sukurti Aplanko piktograma @@ -434,7 +438,7 @@ %1$s (šiame %2$s) Privalote įvesti slaptažodį Įvyko klaida bandant dalinti šį failą ar aplanką - Nepavyko pasidalinti. Patikrinkite ar failas egzistuoja + Nepavyko pradėti bendrinti. Patikrinkite ar failas yra dalintis failu Įveskite slaptažodį Nustatyti slaptažodį diff --git a/src/main/res/values-lv/strings.xml b/src/main/res/values-lv/strings.xml index ad57e31b7df0..e42c538e8b12 100644 --- a/src/main/res/values-lv/strings.xml +++ b/src/main/res/values-lv/strings.xml @@ -358,7 +358,6 @@ Atjaunot dzēstu datni Nepieciešama parole Nepareiza parole - Darbības, kopīgošana, bezsaistes faili Meklēt lietotājus un grupas Atzīmēt visu Sūtīt diff --git a/src/main/res/values-mk/strings.xml b/src/main/res/values-mk/strings.xml index 6c3c3fa7e529..64085e64f204 100644 --- a/src/main/res/values-mk/strings.xml +++ b/src/main/res/values-mk/strings.xml @@ -517,17 +517,13 @@ Погрешна лозинка Најавете се преку QR код Заштитете ги вашите податоци - продуктивна платформа која можете сами да ја хостирате Прелистај и сподели сите активности се во вашите раце - Активности, акции, офлајн датотеки сè брзо достапно Сите ваши сметки на едно место Автоматско прикачување за вашите слики & видеа - Синхронизирај календар & контакти - со DAVx5 (поранешен DAVdroid) Пребарувај корисници и групи Избери се Избери шаблон diff --git a/src/main/res/values-nb-rNO/strings.xml b/src/main/res/values-nb-rNO/strings.xml index f788638207ba..ae2aa018a060 100644 --- a/src/main/res/values-nb-rNO/strings.xml +++ b/src/main/res/values-nb-rNO/strings.xml @@ -525,7 +525,6 @@ på et plass Automatisk opplastning for dine bilder & videoer - Synkronisere & kontakter Søk etter brukere og grupper Velg alle Velg mal diff --git a/src/main/res/values-nl/strings.xml b/src/main/res/values-nl/strings.xml index 179271592cb3..959db3f3c953 100644 --- a/src/main/res/values-nl/strings.xml +++ b/src/main/res/values-nl/strings.xml @@ -536,17 +536,13 @@ Onjuist wachtwoord Inloggen via QR code Beschermt jouw gegevens - eigen-hosting productiviteitsplatform Blader en deel alle taken onder je vindertoppen - Activiteiten, delen, off-line bestanden alles snel toegankelijk Al je accounts op één plek Automatisch uploaden voor je foto\'s & video\'s - Synchroniseren agenda & contactpersonen - met DAVx5 (voorheen DAVdroid) Zoeken naar gebruikers en groepen Alles selecteren Kies sjabloon diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 32b612e7b541..8065d00f0710 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -536,17 +536,13 @@ Złe hasło Zaloguj się za pomocą kodu QR Ochrona Twoich danych - samo-hostująca platforma produkcyjna Przeglądaj i udostępniaj wszystkie działania na wyciągnięcie ręki - Aktywność, udostępnienia, pliki offline wszystko szybko dostępne Wszystkie Twoje konta w jednym miejscu Automatyczne przesyłanie dla Twoich zdjęć & wideo - Synchronizuj kontakty kalendarza & - z DAVx5 (dawniej DAVdroid) Szukaj użytkowników i grup Wybierz wszystko Wybierz szablon diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index b8d92d27ef9c..2eaf023de39b 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -536,17 +536,13 @@ Senha incorreta Entrar via código QR Protegendo seus dados - plataforma de produtividade auto-hospedada Navegar e compartilhar todas as ações na ponta de seus dedos - Atividade, compartilhamentos e arquivos offline tudo rapidamente acessível Todas as suas contas em um lugar Envio automático para suas fotos & vídeos - Sincronizar calendário & contatos - com DAVx5 (também chamado DAVdroid) Pesquisar usuários e grupos Selecionar tudo Selecionar modelo diff --git a/src/main/res/values-ru/strings.xml b/src/main/res/values-ru/strings.xml index b3fa25f8126c..d7b8f2f3aa63 100644 --- a/src/main/res/values-ru/strings.xml +++ b/src/main/res/values-ru/strings.xml @@ -533,8 +533,6 @@ в одном месте Автоматическая загрузка ваших фото & видео - Синхронизуйте календарь & контакты - с DAVx5 (бывший DAVdroid) Поиск пользователей и групп Выбрать все Выбрать шаблон diff --git a/src/main/res/values-sk-rSK/strings.xml b/src/main/res/values-sk-rSK/strings.xml index 6da9ea60ae1f..730b22f1387d 100644 --- a/src/main/res/values-sk-rSK/strings.xml +++ b/src/main/res/values-sk-rSK/strings.xml @@ -531,17 +531,13 @@ Nesprávne heslo Prihlásiť sa pomocou QR kódu Chránte vaše údaje - Platforma pre produktivitu s vlastným hosťovaním Prehliadajte a zdieľajte všetky akcie na dosah ruky - Aktivity, zdieľania, offline súbory všetko rýchlo prístupné Všetky vaše účty na jednom mieste Automatické nahrávanie vašich fotiek a videí - Synchronizácia kalendára a kontaktov - pomocou DAVx5 (pôvodne DAVdroid) Vyhľadať používateľov alebo skupiny Vybrať všetko Vybrať šablónu diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index cab3b950f762..dbe33e560de9 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -372,7 +372,7 @@ Preišči dnevnike Pošlji dnevnike po elektronski pošti Dnevniki: %1$d kB, poizvedba kaže na %2$d / %3$d zadetkov v %4$d ms - Poteka nalaganje… + Poteka nalaganje … Dnevniki: %1$d kB, brez filtra Dnevniki Strežnik je v načinu vzdrževanja @@ -523,8 +523,6 @@ na enem mestu Samodejno pošiljanje za vaše slike in posnetke - Usklajevalnik koledarja in stikov - z DAVx5 (prej poimenovan DAVdroid) Iskanje uporabnikov in skupin Izberi vse IZbor predloge diff --git a/src/main/res/values-sq/strings.xml b/src/main/res/values-sq/strings.xml index d51314b5384e..fb26f8d71b48 100644 --- a/src/main/res/values-sq/strings.xml +++ b/src/main/res/values-sq/strings.xml @@ -536,17 +536,13 @@ Fjalëkalim i gabuar Identifikohu nëpërmjet QR code Mbrojtja e të dhënave tuaja - platformë e vetë-pritur e produktivitetit Shfletoni dhe shpërndani të gjitha veprimet në majë të gishtave tuaj - Aktiviteti, aksionet, skedarët jashtë linje gjithçka shpejt e arritshme Të gjitha llogaritë tuaja në një vend Ngarkimi automatik për fotot tuaja & videot - Kalendar i sinkronizuar & kontaktet - me DAVx5 (më parë i njohur si DAVdroid) Kërkoni për grupe dhe përdorues Zgjidhi të gjitha Zgjidh shabllonin diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index 0cb0719fc238..a98c37e2d1dc 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -170,7 +170,7 @@ Копирај везу Копирање/премештање у шифровану фасциклу није подржано. Копирај у… - Не могу да преузмем целу слику + Није могуће преузети целу слику Не могу да добавим адресу Не могу да направим фасциклу Направите фајл из шаблона… @@ -264,6 +264,7 @@ Врати се на стари начин пријаве Додај у омиљене Омиљени + Фајл Обриши Грешка при дохватању активности фајла Грешка при учитавању детаља @@ -333,6 +334,7 @@ Безбедна сарадња & и размена фајлова Веб пошта лака за коришћење, календар & контакти Дељење екрана, састанци на интернету & веб конференције + Фолдер Фасцикла већ постоји Направи Икона фасцикле @@ -501,6 +503,9 @@ Путања до складишта Локална фасцикла Удаљена фасцикла + Тема + Тамно + Светло Преглед слике Нема локалног фајла за преглед Не могу да прикажем слику @@ -512,6 +517,7 @@ Испробај %1$s на свом уређају! Желим да те позовем да испробаш %1$s на свом уређају.\nПреузми га овде: %2$s %1$s или %2$s + Није могуће пронаћи фајл! Брисање није успело Грешка при уклањању обавештења. Уклони @@ -531,17 +537,13 @@ Погрешна лозинка Пријава преко QR кода Штити Ваше податке - платформа за продуктивност коју и Ви можете да хостујете Претражи и дели све радње под Вашим прстима - Активности, дељења, фајлови доступни без интернета све брзо доступно Сви Ваши налози на једном месту Аутоматска отпремања Ваших слика & видео записа - Синхронизуј календар & контакте - преко DAVx5-а (бивши DAVdroid) Претражи кориснике и групе Означи све Одаберите шаблон @@ -559,6 +561,8 @@ Подели %1$s Добави везу %1$s (група) + Подели интерну везу + Ради само за корисике са приступом овоме %1$s %1$s (на %2$s) Морате унети лозинку Дошло је до грешке приликом покушаја дељења овог фајла или фасцикле diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 09c1b10a4653..175b4168c4d3 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -536,17 +536,13 @@ Fel lösenord Logga in via QR-kod Skydda din data - själv-hostad produktivitetsplattform Bläddra och dela alla åtgärder till hands - Aktivitet, delningar, offline-filer allt snabbt tillgängligt Alla dina konton på ett ställe Automatisk uppladdning för dina bilder & videor - Synkronisera kalender & kontakter - med DAVx5 (tidigare DAVdroid) Sök användare och grupper Välj alla Välj mall diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 6730bd45937e..763156210e93 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -30,7 +30,7 @@ Henüz bir işlem yapılmamış Henüz ekleme, değiştirme, paylaşım gibi bir işlem yapılmamış Gönder - Bağlantıyı gönder… + Bağlantıyı şuraya gönder… İşlem %1$s üzerine ekle İlişkili hesap bulunamadı! @@ -51,7 +51,7 @@ Sunucu bulunamadı Ağ bağlantısı yok Güvenli bağlantı kullanılamıyor. - Sunucu ayarları doğru değil + Sunucu yapılandırması doğru değil Kimlik doğrulanamadı Kimlik doğrulama sunucusu erişime izin vermedi Parola @@ -189,7 +189,7 @@ Yeni bir sürüm yok. Telefonunuz için gerekli algoritma bulunamadı. Devre Dışı Bırak - Yoksay + Yok say Bildirimi yok say Son geliştirici sürümünü indirin %1$s indirmesi tamamlanamadı @@ -235,7 +235,7 @@ Bu klasör boş değil. Yeni anahtarlar üretiliyor… 12 sözcüklü parola çok güçlüdür ve şifrelenmiş dosyalarınızı yalnız sizin görüntüleyip kullanabilmenizi sağlar. Lütfen bir yere yazarak güvenli bir yerde saklayın. - Sunucu üzerinde uçtan uca şifreleme devre dışı bırakıldı. + Sunucu üzerinde uçtan uca şifreleme devre dışı bırakılmış. Şifreleme yalnız KitKat(4.4) ve üzerindeki sürümlerde kullanılabilir 12 sözcüklü şifreleme parolanızı not edin Parola… @@ -308,8 +308,8 @@ Tamamlandı Değiştirilsin Aktarıma hazırlanılıyor… - Hesap ayarları geri yükleniyor… - Hesap ayarları kaydediliyor… + Hesap yapılandırması geri yükleniyor… + Hesap yapılandırması kaydediliyor… Depolama yolunu gene de %1$s olarak değiştirmek istediğinizden emin misiniz?\n\nNot: Tüm verilerin yeniden indirilmesi gerekecek. Kaynak klasör okunabilir değil! Dizin güncelleniyor… @@ -483,8 +483,8 @@ GNU Genel Kamu Lisansı 2. Sürüm Yardım İzlenim - Özgün dosya için - Özgün dosya için… + Özgün dosya şu olacak… + Özgün dosya şu olacak… Yıla ve aya göre alt klasörlere kaydedilsin Alt klasörler kullanılsın Lisans @@ -536,17 +536,13 @@ Parola yanlış QR kodu ile oturum aç Verilerinizi korumak - sizin barındırdığınız üretkenlik platformu Gözat ve paylaş tüm işlemler parmaklarınızın ucunda - İşlemler, paylaşımlar, çevrimdışı dosyalar her şey kolayca erişilebilir Tüm hesaplarınız tek bir yerde Otomatik yükleme fotoğraf ve görüntüleriniz için - Takvim ve kişileri eşitleme - DAVx5 (eski adı DAVdroid) ile Kullanıcı ve Grup Arama Tümünü seç Kalıp seçin diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index dce2c431dda8..e9018f4b08e9 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -534,17 +534,13 @@ 错误密码 通过扫描二维码登录 保护您的数据 - 自托管生产力平台 浏览与分享 所有操控尽在您的指尖 - 活动,共享,离线文件 快速访问所有数据 您所有的账号 都在一处 自动上传 您的照片&视频 - 同步日历&联系人 - 通过 DAVx5(原名DAVdroid) 搜索用户和组 选择全部 选择模板 diff --git a/src/main/res/values-zh-rTW/strings.xml b/src/main/res/values-zh-rTW/strings.xml index 6a2617e536bb..8eddf4483369 100644 --- a/src/main/res/values-zh-rTW/strings.xml +++ b/src/main/res/values-zh-rTW/strings.xml @@ -530,17 +530,13 @@ 密碼錯誤 使用 QR Code 登入 保護您的資料 - 自建具有生產力的平台 瀏覽與分享 全在您一指之間 - 活動,分享,離線檔案 可快速存取所有資源 您所有的帳戶 在一個地方 自動上傳 您的照片&影片 - 同步行事曆&聯絡人 - 透過DAVx5(前身為DAVdroid) 搜尋使用者或群組 全選 選擇範本 diff --git a/src/versionDev/fastlane/metadata/android/cs-CZ/short_description.txt b/src/versionDev/fastlane/metadata/android/cs-CZ/short_description.txt index af93a0364e56..01dc4806ace1 100644 --- a/src/versionDev/fastlane/metadata/android/cs-CZ/short_description.txt +++ b/src/versionDev/fastlane/metadata/android/cs-CZ/short_description.txt @@ -1 +1 @@ -Produktivní platforma hostovaná u vás, kde vaše data jsou opravdu vaše (vývojová \ No newline at end of file +Platforma pro produktivitu hostovaná u vás, kde vaše data jsou opravdu vaše (výv \ No newline at end of file From 10960bf3a2e295e6eb563784761f6b8eaf4d6b31 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Tue, 26 Nov 2019 03:10:16 +0000 Subject: [PATCH 51/69] [tx-robot] updated from transifex --- src/main/res/values-cs-rCZ/strings.xml | 4 ++++ src/main/res/values-de/strings.xml | 4 ++++ src/main/res/values-fr/strings.xml | 3 +++ src/main/res/values-pl/strings.xml | 4 ++++ src/main/res/values-pt-rBR/strings.xml | 4 ++++ src/main/res/values-sr/strings.xml | 4 ++++ src/main/res/values-tr/strings.xml | 4 ++++ 7 files changed, 27 insertions(+) diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index 1efe7a6819ee..0a0e70eef3f0 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -536,13 +536,17 @@ Nesprávné heslo Přihlášení prostřednictvím QR kódu Ochrana vašich dat + nástroje pro produktivitu, hostované u vás Procházet a sdílet všechny akce na dosah ruky + Aktivita, sdílení, … vše pohotově přístupné Všechny vaše účty na jednom místě Automatické nahrání pro vaše fotky a videa + Kalendář a kontakty + Synchronizace s DAVx5 Hledat uživatele a skupiny Vybrat vše Vybrat šablonu diff --git a/src/main/res/values-de/strings.xml b/src/main/res/values-de/strings.xml index 07ef53acf682..ef2e0f5e2482 100644 --- a/src/main/res/values-de/strings.xml +++ b/src/main/res/values-de/strings.xml @@ -536,13 +536,17 @@ Falsches Passwort Anmelden mit QR-Code Schützt Ihre Daten + Selbst gehostete Produktivität Durchsuchen und teilen Alle Aktionen schnell erreichbar + Aktivitäten, Freigaben, … Alles schnell erreichbar All ihre Konten an einem Ort Automatisches Hochladen ihrer Fotos & Videos + Kalender & Kontakte + Synchronisiere mit DAVx5 Nutzer und Gruppen suchen Alle auswählen Vorlage auswählen diff --git a/src/main/res/values-fr/strings.xml b/src/main/res/values-fr/strings.xml index ce1085f0ff5e..9ebfabc4af33 100644 --- a/src/main/res/values-fr/strings.xml +++ b/src/main/res/values-fr/strings.xml @@ -540,11 +540,14 @@ Attention la suppression est irréversible. Protection de vos données Parcourir et partager toutes les actions à portée de main + Activité, partages, … tout accessible rapidement Tous vos comptes en un seul endroit Téléversement automatique pour vos photos & vidéos + Agenda & Contacts + Synchroniser avec DAVx5 Rechercher parmi les utilisateurs et groupes Tout sélectionner Sélectionnez un modèle diff --git a/src/main/res/values-pl/strings.xml b/src/main/res/values-pl/strings.xml index 8065d00f0710..524b4955a7b6 100644 --- a/src/main/res/values-pl/strings.xml +++ b/src/main/res/values-pl/strings.xml @@ -536,13 +536,17 @@ Złe hasło Zaloguj się za pomocą kodu QR Ochrona Twoich danych + produkcyjny samo-hostujący Przeglądaj i udostępniaj wszystkie działania na wyciągnięcie ręki + Aktywność, udostępnienia... wszystko szybko dostępne Wszystkie Twoje konta w jednym miejscu Automatyczne przesyłanie dla Twoich zdjęć & wideo + Kontakty w kalendarzu: & + Synchronizuj z DAVx5 Szukaj użytkowników i grup Wybierz wszystko Wybierz szablon diff --git a/src/main/res/values-pt-rBR/strings.xml b/src/main/res/values-pt-rBR/strings.xml index 2eaf023de39b..dd545cf7f9f3 100644 --- a/src/main/res/values-pt-rBR/strings.xml +++ b/src/main/res/values-pt-rBR/strings.xml @@ -536,13 +536,17 @@ Senha incorreta Entrar via código QR Protegendo seus dados + produtividade auto hospedada Navegar e compartilhar todas as ações na ponta de seus dedos + Atividades, compartilhamentos, ... tudo rapidamente acessível Todas as suas contas em um lugar Envio automático para suas fotos & vídeos + Calendário & contatos + Sincronizar com DAVx5 Pesquisar usuários e grupos Selecionar tudo Selecionar modelo diff --git a/src/main/res/values-sr/strings.xml b/src/main/res/values-sr/strings.xml index a98c37e2d1dc..1effe01065e7 100644 --- a/src/main/res/values-sr/strings.xml +++ b/src/main/res/values-sr/strings.xml @@ -537,13 +537,17 @@ Погрешна лозинка Пријава преко QR кода Штити Ваше податке + продуктивност коју Ви хостујете Претражи и дели све радње под Вашим прстима + Активности, дељења, … све брзо доступно Сви Ваши налози на једном месту Аутоматска отпремања Ваших слика & видео записа + Календар & контакти + Синхронизација са DAVx5 Претражи кориснике и групе Означи све Одаберите шаблон diff --git a/src/main/res/values-tr/strings.xml b/src/main/res/values-tr/strings.xml index 763156210e93..81540685f11b 100644 --- a/src/main/res/values-tr/strings.xml +++ b/src/main/res/values-tr/strings.xml @@ -536,13 +536,17 @@ Parola yanlış QR kodu ile oturum aç Verilerinizi korumak + sizin barındırdığınız üretkenlik Gözat ve paylaş tüm işlemler parmaklarınızın ucunda + İşlemler, paylaşımlar, … her şey kolayca erişilebilir Tüm hesaplarınız tek bir yerde Otomatik yükleme fotoğraf ve görüntüleriniz için + Takvim ve kişiler + DAVx5 ile eşitleme Kullanıcı ve Grup Arama Tümünü seç Kalıp seçin From 21c0de3cddac922c5db0f8e45c022272b927e295 Mon Sep 17 00:00:00 2001 From: Tobias Kaminsky Date: Wed, 27 Nov 2019 03:37:10 +0100 Subject: [PATCH 52/69] daily dev 20191127 e92daa2fc Merge pull request #4853 from nextcloud/migrate-simple-cases-of-get-current-account-to-get-user 99d9a6923 Merge pull request #4885 from nextcloud/disable_android_backup_qa 10960bf3a [tx-robot] updated from transifex 9f24b254f [tx-robot] updated from transifex b7876c425 disable GoogleAppIndexingWarning lint check 11687c65c Drone: update FindBugs results to reflect reduced error/warning count [skip ci] 80fb800ef Merge commit 'a7eb7148fa0ceb42981366eb2ddcf0ff921e6a55' b91136c36 Disable android backup on the QA app a7eb7148f Migrate simple cases of getCurrentAccount() to getUser() 6249a06a8 daily dev 20191123 --- build.gradle | 4 ++-- .../metadata/android/en-US/changelogs/20191127.txt | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 src/versionDev/fastlane/metadata/android/en-US/changelogs/20191127.txt diff --git a/build.gradle b/build.gradle index 2867606f03dd..ee15bc37b08d 100644 --- a/build.gradle +++ b/build.gradle @@ -167,8 +167,8 @@ android { versionDev { applicationId "com.nextcloud.android.beta" dimension "default" - versionCode 20191123 - versionName "20191123" + versionCode 20191127 + versionName "20191127" } qa { diff --git a/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191127.txt b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191127.txt new file mode 100644 index 000000000000..6f8de459d7c3 --- /dev/null +++ b/src/versionDev/fastlane/metadata/android/en-US/changelogs/20191127.txt @@ -0,0 +1,10 @@ +e92daa2fc Merge pull request #4853 from nextcloud/migrate-simple-cases-of-get-current-account-to-get-user +99d9a6923 Merge pull request #4885 from nextcloud/disable_android_backup_qa +10960bf3a [tx-robot] updated from transifex +9f24b254f [tx-robot] updated from transifex +b7876c425 disable GoogleAppIndexingWarning lint check +11687c65c Drone: update FindBugs results to reflect reduced error/warning count [skip ci] +80fb800ef Merge commit 'a7eb7148fa0ceb42981366eb2ddcf0ff921e6a55' +b91136c36 Disable android backup on the QA app +a7eb7148f Migrate simple cases of getCurrentAccount() to getUser() +6249a06a8 daily dev 20191123 From 83e0ebc463408a36c079916c680444a996566b02 Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Wed, 27 Nov 2019 03:10:14 +0000 Subject: [PATCH 53/69] [tx-robot] updated from transifex --- src/main/res/values-ca/strings.xml | 3 +++ src/main/res/values-es/strings.xml | 4 ++++ src/main/res/values-gl/strings.xml | 4 ++++ src/main/res/values-it/strings.xml | 4 ++++ src/main/res/values-sl/strings.xml | 2 ++ src/main/res/values-zh-rCN/strings.xml | 8 ++++++++ 6 files changed, 25 insertions(+) diff --git a/src/main/res/values-ca/strings.xml b/src/main/res/values-ca/strings.xml index dcfd0c5a9553..a918d2111c47 100644 --- a/src/main/res/values-ca/strings.xml +++ b/src/main/res/values-ca/strings.xml @@ -538,11 +538,14 @@ Protegint les vostres dades Explorar i compartir totes les accions als vostres dits + Activitat, comparticions, … tot molt ràpidament accesible Tots els vostres comptes En un sol lloc Pujada automàtica pels vostres vídeos i imatges + Calendari i contactes + Sincronitza amb DAVx5 Cerca usuaris i grups Selecciona-ho tot Selecciona una plantilla diff --git a/src/main/res/values-es/strings.xml b/src/main/res/values-es/strings.xml index 4a8bf1830a60..3953e76ea6ce 100644 --- a/src/main/res/values-es/strings.xml +++ b/src/main/res/values-es/strings.xml @@ -536,13 +536,17 @@ Contraseña incorrecta Entrar vía código QR Protegiendo sus datos + plataforma de productividad autoalojada Navegar y compartir todas las acciones en la punta de sus dedos + Actividad, compartir… todo accesible rápidamente Todas sus cuentas en un lugar Subida automática para sus fotos & vídeos + Calendario y contactos + Sincronización con DAVx5 Buscar usuarios y grupos Seleccionar todo Escoge plantilla diff --git a/src/main/res/values-gl/strings.xml b/src/main/res/values-gl/strings.xml index a05c71af6494..e89186ab9147 100644 --- a/src/main/res/values-gl/strings.xml +++ b/src/main/res/values-gl/strings.xml @@ -537,13 +537,17 @@ Contrasinal incorrecto Acceder cun código QR Protexendo os seus datos + produtividade de aloxamento propio Navegar e compartir todas as accións ao seu alcance + Actividade, comparticións, … todo accesíbel rapidamente Todas as súas contas nun só lugar Envío automático para as súas fotos e vídeos + Calendario e contactos + Sincronizar con DAVx5 Buscar usuarios e grupos Seleccionar todo Seleccionar o modelo diff --git a/src/main/res/values-it/strings.xml b/src/main/res/values-it/strings.xml index 1874a0eb4a05..58ee37361638 100644 --- a/src/main/res/values-it/strings.xml +++ b/src/main/res/values-it/strings.xml @@ -536,13 +536,17 @@ Password errata Accedi tramite codice QR Proteggere i tuoi dati + produttività auto-gestita Sfoglia e condividi tutte le azioni a portata di mano + Attività, condivisioni, ... tutto rapidamente accessibile Tutti i tuoi account in un posto Caricamento automatico per e tue foto e i video + Calendario e contatti + Sincronizza con DAVx5 Cerca utenti e gruppi Seleziona tutto Seleziona modello diff --git a/src/main/res/values-sl/strings.xml b/src/main/res/values-sl/strings.xml index dbe33e560de9..19c913963809 100644 --- a/src/main/res/values-sl/strings.xml +++ b/src/main/res/values-sl/strings.xml @@ -448,6 +448,7 @@ Dovoli Zavrni Za prejemanje oziroma pošiljanje datotek v oblak so zahtevana dodatna dovoljenja. + Ni najdenega programa za nastavitev slike 389 KB vsebnik.txt 12:23:45 @@ -519,6 +520,7 @@ Prijava s kodo QR Zaščita podatkov Brskanje in souporaba + Dejavnosti, souporaba ... Vsi vaši računi na enem mestu Samodejno pošiljanje diff --git a/src/main/res/values-zh-rCN/strings.xml b/src/main/res/values-zh-rCN/strings.xml index e9018f4b08e9..f6804c1cfe70 100644 --- a/src/main/res/values-zh-rCN/strings.xml +++ b/src/main/res/values-zh-rCN/strings.xml @@ -263,6 +263,7 @@ 采用旧的登录方式 添加到收藏列表 收藏 + 文件 删除 获取文件动态时出错 加载详情失败 @@ -332,6 +333,7 @@ 安全的交换 & 个协作文件 易于使用的网络邮件,日历和&联系 屏幕分享,在线会议,&网络会议 + 文件夹 目录已经存在 创建 目录图标 @@ -534,13 +536,17 @@ 错误密码 通过扫描二维码登录 保护您的数据 + 自托管生产力 浏览与分享 所有操控尽在您的指尖 + 活动,共享,... 快速访问所有数据 您所有的账号 都在一处 自动上传 您的照片&视频 + 日历 & 联系人 + 与DAVx5同步 搜索用户和组 选择全部 选择模板 @@ -558,6 +564,8 @@ 分享 %1$s 获取链接 %1$s (组) + 共享内部链接 + 仅适用于有权限访问此%1$s的用户 %1$s ( 在 %2$s ) 您必须输入密码 共享文件或目录出错 From f34ba2cdca6fdf31667292a598a966b54bcb913b Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Wed, 27 Nov 2019 13:03:07 +0000 Subject: [PATCH 54/69] Bump kotlin_version from 1.3.60 to 1.3.61 Bumps `kotlin_version` from 1.3.60 to 1.3.61. Updates `kotlin-gradle-plugin` from 1.3.60 to 1.3.61 Updates `kotlin-stdlib-jdk7` from 1.3.60 to 1.3.61 - [Release notes](https://github.com/JetBrains/kotlin/releases) - [Changelog](https://github.com/JetBrains/kotlin/blob/v1.3.61/ChangeLog.md) - [Commits](https://github.com/JetBrains/kotlin/compare/v1.3.60...v1.3.61) Signed-off-by: dependabot-preview[bot] --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 21fc06fca3cc..03690d6f9844 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ import com.github.spotbugs.SpotBugsTask buildscript { - ext.kotlin_version = '1.3.60' + ext.kotlin_version = '1.3.61' repositories { google() jcenter() From 23e44243dab83554126695ae8b3b039cf306d3f3 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Fri, 1 Nov 2019 22:37:55 +0100 Subject: [PATCH 55/69] show/hide auto upload list items Signed-off-by: Andy Scherzinger --- .../android/datamodel/SyncedFolder.java | 27 +- .../datamodel/SyncedFolderDisplayItem.java | 17 +- .../datamodel/SyncedFolderProvider.java | 5 +- .../com/owncloud/android/db/ProviderMeta.java | 3 +- .../providers/FileContentProvider.java | 27 +- .../ui/activity/SyncedFoldersActivity.java | 108 ++++--- .../ui/adapter/SyncedFolderAdapter.java | 306 ++++++++++++++---- .../dialog/parcel/SyncedFolderParcelable.java | 168 +++++----- src/main/res/layout/synced_folders_empty.xml | 6 + src/main/res/layout/synced_folders_footer.xml | 38 +++ src/main/res/menu/synced_folders_adapter.xml | 28 ++ src/main/res/values/strings.xml | 6 + .../activity/SyncedFoldersActivityTest.java | 3 +- 13 files changed, 528 insertions(+), 214 deletions(-) create mode 100644 src/main/res/layout/synced_folders_empty.xml create mode 100644 src/main/res/layout/synced_folders_footer.xml create mode 100644 src/main/res/menu/synced_folders_adapter.xml diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 91b42b813469..fa4d5823c38e 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -23,14 +23,12 @@ import java.io.Serializable; -import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; /** * Synced folder entity containing all information per synced folder. */ -@AllArgsConstructor public class SyncedFolder implements Serializable, Cloneable { public static final long UNPERSISTED_ID = Long.MIN_VALUE; public static final long EMPTY_ENABLED_TIMESTAMP_MS = -1; @@ -39,14 +37,15 @@ public class SyncedFolder implements Serializable, Cloneable { @Getter @Setter private long id; @Getter @Setter private String localPath; @Getter @Setter private String remotePath; - @Getter @Setter private Boolean wifiOnly; - @Getter @Setter private Boolean chargingOnly; - @Getter @Setter private Boolean subfolderByDate; + @Getter @Setter private boolean wifiOnly; + @Getter @Setter private boolean chargingOnly; + @Getter @Setter private boolean subfolderByDate; @Getter @Setter private String account; - @Getter @Setter private Integer uploadAction; + @Getter @Setter private int uploadAction; @Getter private boolean enabled; @Getter private long enabledTimestampMs; @Getter @Setter private MediaFolderType type; + @Getter @Setter private boolean hidden; /** * constructor for new, to be persisted entity. @@ -61,12 +60,13 @@ public class SyncedFolder implements Serializable, Cloneable { * @param enabled flag if synced folder config is active * @param timestampMs the current timestamp in milliseconds * @param type the type of the folder + * @param hidden hide item flag */ - public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, - Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - long timestampMs, MediaFolderType type) { + public SyncedFolder(String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean subfolderByDate, String account, int uploadAction, boolean enabled, + long timestampMs, MediaFolderType type, boolean hidden) { this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, - enabled, timestampMs, type); + enabled, timestampMs, type, hidden); } /** @@ -74,9 +74,9 @@ public SyncedFolder(String localPath, String remotePath, Boolean wifiOnly, Boole * * @param id id */ - protected SyncedFolder(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, - Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - long timestampMs, MediaFolderType type) { + protected SyncedFolder(long id, String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean subfolderByDate, String account, int uploadAction, boolean enabled, + long timestampMs, MediaFolderType type, boolean hidden) { this.id = id; this.localPath = localPath; this.remotePath = remotePath; @@ -87,6 +87,7 @@ protected SyncedFolder(long id, String localPath, String remotePath, Boolean wif this.uploadAction = uploadAction; this.setEnabled(enabled, timestampMs); this.type = type; + this.hidden = hidden; } /** diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 7420b1889295..5b51b37e0154 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -53,24 +53,25 @@ public class SyncedFolderDisplayItem extends SyncedFolder { * @param folderName the UI info for the folder's name * @param numberOfFiles the UI info for number of files within the folder * @param type the type of the folder + * @param hidden hide item flag */ - public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, - Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, + public SyncedFolderDisplayItem(long id, String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean subfolderByDate, String account, int uploadAction, boolean enabled, long timestampMs, List filePaths, String folderName, long numberOfFiles, - MediaFolderType type) + MediaFolderType type, boolean hidden) { super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, - timestampMs, type); + timestampMs, type, hidden); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; } - public SyncedFolderDisplayItem(long id, String localPath, String remotePath, Boolean wifiOnly, Boolean chargingOnly, - Boolean subfolderByDate, String account, Integer uploadAction, Boolean enabled, - long timestampMs, String folderName, MediaFolderType type) { + public SyncedFolderDisplayItem(long id, String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean subfolderByDate, String account, int uploadAction, boolean enabled, + long timestampMs, String folderName, MediaFolderType type, boolean hidden) { super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, - timestampMs, type); + timestampMs, type, hidden); this.folderName = folderName; } } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 8a66a4058a1b..6a8caf710671 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -354,9 +354,11 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS)); MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE))); + Boolean hidden = cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1; syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, - accountName, uploadAction, enabled, enabledTimestampMs, type); + accountName, uploadAction, enabled, enabledTimestampMs, type, hidden); } return syncedFolder; } @@ -380,6 +382,7 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.getHidden()); return cv; } diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index 8bbe982b15d2..a9dad01cd914 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -31,7 +31,7 @@ */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 50; + public static final int DB_VERSION = 51; private ProviderMeta() { // No instance @@ -225,6 +225,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String SYNCED_FOLDER_SUBFOLDER_BY_DATE = "subfolder_by_date"; public static final String SYNCED_FOLDER_ACCOUNT = "account"; public static final String SYNCED_FOLDER_UPLOAD_ACTION = "upload_option"; + public static final String SYNCED_FOLDER_HIDDEN = "hidden"; // Columns of external links table public static final String EXTERNAL_LINKS_ICON_URL = "icon_url"; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 78824925e414..d1bfda3b10fe 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -833,7 +833,8 @@ private void createSyncedFoldersTable(SQLiteDatabase db) { + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date + ProviderTableMeta.SYNCED_FOLDER_ACCOUNT + " TEXT, " // account + ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION + " INTEGER, " // upload action - + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER );" // type + + ProviderTableMeta.SYNCED_FOLDER_TYPE + " INTEGER, " // type + + ProviderTableMeta.SYNCED_FOLDER_HIDDEN + " INTEGER );" // hidden ); } @@ -2045,6 +2046,24 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } + + if (oldVersion < 51 && newVersion >= 51) { + Log_OC.i(SQL, "Entering in the #51 add show/hide to folderSync table"); + db.beginTransaction(); + try { + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_HIDDEN + " INTEGER "); + + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } } @Override @@ -2057,6 +2076,12 @@ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(ALTER_TABLE + ProviderTableMeta.CAPABILITIES_TABLE_NAME + REMOVE_COLUMN + ProviderTableMeta.CAPABILITIES_END_TO_END_ENCRYPTION); } + + if(oldVersion == 50 && newVersion < 50) { + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + REMOVE_COLUMN + ProviderTableMeta.SYNCED_FOLDER_HIDDEN); + + } } } } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 972522d6336f..3d08d4193b4e 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -50,6 +50,7 @@ import com.owncloud.android.BuildConfig; import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.datamodel.ArbitraryDataProvider; import com.owncloud.android.datamodel.MediaFolder; import com.owncloud.android.datamodel.MediaFolderType; import com.owncloud.android.datamodel.MediaProvider; @@ -382,9 +383,9 @@ private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull Sy syncedFolder.getId(), syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), - syncedFolder.getWifiOnly(), - syncedFolder.getChargingOnly(), - syncedFolder.getSubfolderByDate(), + syncedFolder.isWifiOnly(), + syncedFolder.isChargingOnly(), + syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), @@ -392,7 +393,8 @@ private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull Sy filePaths, localFolder.getName(), files.length, - syncedFolder.getType()); + syncedFolder.getType(), + syncedFolder.isHidden()); } /** @@ -408,9 +410,9 @@ private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedF syncedFolder.getId(), syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), - syncedFolder.getWifiOnly(), - syncedFolder.getChargingOnly(), - syncedFolder.getSubfolderByDate(), + syncedFolder.isWifiOnly(), + syncedFolder.isChargingOnly(), + syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), @@ -418,7 +420,8 @@ private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedF mediaFolder.filePaths, mediaFolder.folderName, mediaFolder.numberOfFiles, - mediaFolder.type); + mediaFolder.type, + syncedFolder.isHidden()); } /** @@ -443,7 +446,8 @@ private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull Media mediaFolder.filePaths, mediaFolder.folderName, mediaFolder.numberOfFiles, - mediaFolder.type); + mediaFolder.type, + false); } private File[] getFileList(File localFolder) { @@ -523,8 +527,8 @@ public boolean onOptionsItemSelected(MenuItem item) { Log.d(TAG, "Show custom folder dialog"); SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( SyncedFolder.UNPERSISTED_ID, null, null, true, false, - false, getAccount().name, - FileUploader.LOCAL_BEHAVIOUR_FORGET, false, clock.getCurrentTime(), null, MediaFolderType.CUSTOM); + false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, + clock.getCurrentTime(), null, MediaFolderType.CUSTOM, false); onSyncFolderSettingsClick(0, emptyCustomFolder); } @@ -581,6 +585,14 @@ public void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem synce mSyncedFolderPreferencesDialogFragment.show(ft, SYNCED_FOLDER_PREFERENCES_DIALOG_TAG); } + @Override + public void onVisibilityToggleClick(int section, SyncedFolderDisplayItem syncedFolder) { + syncedFolder.setHidden(!syncedFolder.isHidden()); + + saveOrUpdateSyncedFolder(syncedFolder); + mAdapter.setSyncFolderItem(section, syncedFolder); + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_REMOTE_FOLDER @@ -606,39 +618,21 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(), - clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType()); - long storedId = mSyncedFolderProvider.storeSyncedFolder(newCustomFolder); - if (storedId != -1) { - newCustomFolder.setId(storedId); - if (newCustomFolder.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(newCustomFolder); - } - } + clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.getHidden()); + + saveOrUpdateSyncedFolder(newCustomFolder); mAdapter.addSyncFolderItem(newCustomFolder); } else { SyncedFolderDisplayItem item = mAdapter.get(syncedFolder.getSection()); - item = updateSyncedFolderItem(item, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder + updateSyncedFolderItem(item, syncedFolder.getId(), syncedFolder.getLocalPath(), + syncedFolder.getRemotePath(), syncedFolder .getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder .getUploadAction(), syncedFolder.getEnabled()); - if (syncedFolder.getId() == UNPERSISTED_ID) { - // newly set up folder sync config - long storedId = mSyncedFolderProvider.storeSyncedFolder(item); - if (storedId != -1) { - item.setId(storedId); - if (item.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); - } - } - } else { - // existing synced folder setup to be updated - mSyncedFolderProvider.updateSyncFolder(item); - if (item.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); - } - } + saveOrUpdateSyncedFolder(item); - mAdapter.setSyncFolderItem(syncedFolder.getSection(), item); + // TODO test if notifiyItemChanged is suffiecient (should improve performance) + mAdapter.notifyDataSetChanged(); } mSyncedFolderPreferencesDialogFragment = null; @@ -648,6 +642,40 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { } } + private void saveOrUpdateSyncedFolder(SyncedFolderDisplayItem item) { + if (item.getId() == UNPERSISTED_ID) { + // newly set up folder sync config + storeSyncedFolder(item); + } else { + // existing synced folder setup to be updated + mSyncedFolderProvider.updateSyncFolder(item); + if (item.isEnabled()) { + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); + } else { + String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); + + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). + getContentResolver()); + arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); + } + } + } + + private void storeSyncedFolder(SyncedFolderDisplayItem item) { + ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). + getContentResolver()); + long storedId = mSyncedFolderProvider.storeSyncedFolder(item); + if (storedId != -1) { + item.setId(storedId); + if (item.isEnabled()) { + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); + } else { + String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); + arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); + } + } + } + @Override public void onCancelSyncedFolderPreference() { mSyncedFolderPreferencesDialogFragment = null; @@ -670,9 +698,9 @@ public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) * @param subfolderByDate created sub folders * @param uploadAction upload action * @param enabled is sync enabled - * @return the updated item */ - private SyncedFolderDisplayItem updateSyncedFolderItem(SyncedFolderDisplayItem item, + private void updateSyncedFolderItem(SyncedFolderDisplayItem item, + long id, String localPath, String remotePath, Boolean wifiOnly, @@ -680,6 +708,7 @@ private SyncedFolderDisplayItem updateSyncedFolderItem(SyncedFolderDisplayItem i Boolean subfolderByDate, Integer uploadAction, Boolean enabled) { + item.setId(id); item.setLocalPath(localPath); item.setRemotePath(remotePath); item.setWifiOnly(wifiOnly); @@ -687,7 +716,6 @@ private SyncedFolderDisplayItem updateSyncedFolderItem(SyncedFolderDisplayItem i item.setSubfolderByDate(subfolderByDate); item.setUploadAction(uploadAction); item.setEnabled(enabled, clock.getCurrentTime()); - return item; } @Override diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index 7588b323da7f..2c636b1b3b87 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -23,11 +23,14 @@ import android.content.Context; import android.view.LayoutInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.PopupMenu; import android.widget.RelativeLayout; import android.widget.TextView; @@ -54,64 +57,167 @@ */ public class SyncedFolderAdapter extends SectionedRecyclerViewAdapter { - private final Context mContext; + private final Context context; private final Clock clock; - private final int mGridWidth; - private final int mGridTotal; - private final ClickListener mListener; - private final List mSyncFolderItems; - private final boolean mLight; + private final int gridWidth; + private final int gridTotal; + private final ClickListener clickListener; + private final List syncFolderItems; + private final List filteredSyncFolderItems; + private final boolean light; + private final int VIEW_TYPE_EMPTY = Integer.MAX_VALUE; + private boolean hideItems; public SyncedFolderAdapter(Context context, Clock clock, int gridWidth, ClickListener listener, boolean light) { - mContext = context; + this.context = context; this.clock = clock; - mGridWidth = gridWidth; - mGridTotal = gridWidth * 2; - mListener = listener; - mSyncFolderItems = new ArrayList<>(); - mLight = light; + this.gridWidth = gridWidth; + gridTotal = gridWidth * 2; + clickListener = listener; + syncFolderItems = new ArrayList<>(); + filteredSyncFolderItems = new ArrayList<>(); + this.light = light; + this.hideItems = true; shouldShowHeadersForEmptySections(true); + shouldShowFooters(true); + } + + private void toggleHiddenItemsVisibility() { + hideItems = !hideItems; + filteredSyncFolderItems.clear(); + filteredSyncFolderItems.addAll(filterHiddenItems(syncFolderItems, hideItems)); + notifyDataSetChanged(); } public void setSyncFolderItems(List syncFolderItems) { - mSyncFolderItems.clear(); - mSyncFolderItems.addAll(syncFolderItems); + this.syncFolderItems.clear(); + this.syncFolderItems.addAll(syncFolderItems); + + this.filteredSyncFolderItems.clear(); + this.filteredSyncFolderItems.addAll(filterHiddenItems(this.syncFolderItems, hideItems)); } public void setSyncFolderItem(int location, SyncedFolderDisplayItem syncFolderItem) { - mSyncFolderItems.set(location, syncFolderItem); + // show all items OR + if (hideItems && syncFolderItem.isHidden() && filteredSyncFolderItems.contains(syncFolderItem)) { + filteredSyncFolderItems.remove(location); + } else { + if (filteredSyncFolderItems.contains(syncFolderItem)) { + filteredSyncFolderItems.set(filteredSyncFolderItems.indexOf(syncFolderItem), syncFolderItem); + } else { + filteredSyncFolderItems.add(syncFolderItem); + } + } + + if (syncFolderItems.contains(syncFolderItem)) { + syncFolderItems.set(syncFolderItems.indexOf(syncFolderItem), syncFolderItem); + } else { + syncFolderItems.add(syncFolderItem); + } + notifyDataSetChanged(); } public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) { - mSyncFolderItems.add(syncFolderItem); - notifyDataSetChanged(); + syncFolderItems.add(syncFolderItem); + + // add item for display when either all items should be shown (!hideItems) + // or if items should be hi + if (!hideItems || !syncFolderItem.isHidden()) { + filteredSyncFolderItems.add(syncFolderItem); + notifyDataSetChanged(); + } } public void removeItem(int section) { - mSyncFolderItems.remove(section); - notifyDataSetChanged(); + if (filteredSyncFolderItems.contains(syncFolderItems.get(section))) { + filteredSyncFolderItems.remove(syncFolderItems.get(section)); + notifyDataSetChanged(); + } + syncFolderItems.remove(section); + } + + /** + * Filter for hidden items + * + * @param items Collection of items to filter + * @return Non-hidden items + */ + private List filterHiddenItems(List items, boolean hide) { + if (hide) { + List ret = new ArrayList<>(); + + for (SyncedFolderDisplayItem item : items) { + if (!item.isHidden() && !ret.contains(item)) { + ret.add(item); + } + } + + return ret; + } else { + return items; + } } @Override public int getSectionCount() { - return mSyncFolderItems.size(); + if (filteredSyncFolderItems.size() > 0) { + return filteredSyncFolderItems.size() + 1; + } else { + return 0; + } } @Override public int getItemCount(int section) { - List filePaths = mSyncFolderItems.get(section).getFilePaths(); + if (section < filteredSyncFolderItems.size()) { + List filePaths = filteredSyncFolderItems.get(section).getFilePaths(); - if (filePaths != null) { - return mSyncFolderItems.get(section).getFilePaths().size(); + if (filePaths != null) { + return filteredSyncFolderItems.get(section).getFilePaths().size(); + } else { + return 1; + } } else { return 1; } } public SyncedFolderDisplayItem get(int section) { - return mSyncFolderItems.get(section); + return filteredSyncFolderItems.get(section); + } + + @Override + public int getItemViewType(int section, int relativePosition, int absolutePosition) { + if (isLastSection(section)) { + return VIEW_TYPE_EMPTY; + } else { + return VIEW_TYPE_ITEM; + } + } + + @Override + public int getHeaderViewType(int section) { + if (isLastSection(section)) { + return VIEW_TYPE_EMPTY; + } else { + return VIEW_TYPE_HEADER; + } + } + + @Override + public int getFooterViewType(int section) { + if (isLastSection(section) && showFooter()) { + return VIEW_TYPE_FOOTER; + } else { + // only show footer after last item and only if folders have been hidden + return VIEW_TYPE_EMPTY; + } + } + + private boolean showFooter() { + return syncFolderItems.size() > filteredSyncFolderItems.size(); } /** @@ -122,9 +228,9 @@ public SyncedFolderDisplayItem get(int section) { * @return the section index of the looked up synced folder, -1 if not present */ public int getSectionByLocalPathAndType(String localPath, int type) { - for (int i = 0; i < mSyncFolderItems.size(); i++) { - if (mSyncFolderItems.get(i).getLocalPath().equalsIgnoreCase(localPath) && - mSyncFolderItems.get(i).getType().getId().equals(type)) { + for (int i = 0; i < filteredSyncFolderItems.size(); i++) { + if (filteredSyncFolderItems.get(i).getLocalPath().equalsIgnoreCase(localPath) && + filteredSyncFolderItems.get(i).getType().getId().equals(type)) { return i; } } @@ -134,70 +240,95 @@ public int getSectionByLocalPathAndType(String localPath, int type) { @Override public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int section, boolean expanded) { - HeaderViewHolder holder = (HeaderViewHolder) commonHolder; + if (section < filteredSyncFolderItems.size()) { + HeaderViewHolder holder = (HeaderViewHolder) commonHolder; + holder.mainHeaderContainer.setVisibility(View.VISIBLE); + + holder.title.setText(filteredSyncFolderItems.get(section).getFolderName()); + + if (MediaFolderType.VIDEO == filteredSyncFolderItems.get(section).getType()) { + holder.type.setImageResource(R.drawable.video_32dp); + } else if (MediaFolderType.IMAGE == filteredSyncFolderItems.get(section).getType()) { + holder.type.setImageResource(R.drawable.image_32dp); + } else { + holder.type.setImageResource(R.drawable.folder_star_32dp); + } - holder.mainHeaderContainer.setVisibility(View.VISIBLE); + holder.syncStatusButton.setVisibility(View.VISIBLE); + holder.syncStatusButton.setTag(section); + holder.syncStatusButton.setOnClickListener(v -> { + filteredSyncFolderItems.get(section).setEnabled( + !filteredSyncFolderItems.get(section).isEnabled(), + clock.getCurrentTime() + ); + setSyncButtonActiveIcon(holder.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled()); + clickListener.onSyncStatusToggleClick(section, filteredSyncFolderItems.get(section)); + }); + setSyncButtonActiveIcon(holder.syncStatusButton, filteredSyncFolderItems.get(section).isEnabled()); + + if (light) { + holder.menuButton.setVisibility(View.GONE); + } else { + holder.menuButton.setVisibility(View.VISIBLE); + holder.menuButton.setTag(section); + holder.menuButton.setOnClickListener(v -> onOverflowIconClicked(section, + filteredSyncFolderItems.get(section), + v)); + } + } + } - holder.title.setText(mSyncFolderItems.get(section).getFolderName()); + private void onOverflowIconClicked(int section, SyncedFolderDisplayItem item, View view) { + PopupMenu popup = new PopupMenu(context, view); + popup.inflate(R.menu.synced_folders_adapter); + popup.setOnMenuItemClickListener(i -> optionsItemSelected(i,section,item)); - if (MediaFolderType.VIDEO == mSyncFolderItems.get(section).getType()) { - holder.type.setImageResource(R.drawable.video_32dp); - } else if (MediaFolderType.IMAGE == mSyncFolderItems.get(section).getType()) { - holder.type.setImageResource(R.drawable.image_32dp); + if (item.isHidden()) { + popup.getMenu() + .findItem(R.id.action_auto_upload_folder_toggle_visibility) + .setTitle(R.string.autoupload_show_folder); } else { - holder.type.setImageResource(R.drawable.folder_star_32dp); + popup.getMenu() + .findItem(R.id.action_auto_upload_folder_toggle_visibility) + .setTitle(R.string.autoupload_hide_folder); } + popup.show(); + } - holder.syncStatusButton.setVisibility(View.VISIBLE); - holder.syncStatusButton.setTag(section); - holder.syncStatusButton.setOnClickListener(v -> { - mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled(), clock.getCurrentTime()); - setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); - }); - setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - - holder.syncStatusButton.setVisibility(View.VISIBLE); - holder.syncStatusButton.setTag(section); - holder.syncStatusButton.setOnClickListener(v -> { - mSyncFolderItems.get(section).setEnabled(!mSyncFolderItems.get(section).isEnabled(), clock.getCurrentTime()); - setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - mListener.onSyncStatusToggleClick(section, mSyncFolderItems.get(section)); - }); - setSyncButtonActiveIcon(holder.syncStatusButton, mSyncFolderItems.get(section).isEnabled()); - - if (mLight) { - holder.menuButton.setVisibility(View.GONE); + private boolean optionsItemSelected(MenuItem menuItem, int section, SyncedFolderDisplayItem item) { + if (menuItem.getItemId() == R.id.action_auto_upload_folder_toggle_visibility) { + clickListener.onVisibilityToggleClick(section, item); } else { - holder.menuButton.setVisibility(View.VISIBLE); - holder.menuButton.setTag(section); - holder.menuButton.setOnClickListener(v -> mListener.onSyncFolderSettingsClick(section, - mSyncFolderItems.get(section))); + // default: R.id.action_create_custom_folder + clickListener.onSyncFolderSettingsClick(section, item); } + return true; } @Override public void onBindFooterViewHolder(SectionedViewHolder holder, int section) { - // not needed + if (isLastSection(section) && showFooter()) { + FooterViewHolder footerHolder = (FooterViewHolder) holder; + footerHolder.title.setOnClickListener(v -> toggleHiddenItemsVisibility()); + } } - @Override public void onBindViewHolder(SectionedViewHolder commonHolder, int section, int relativePosition, int absolutePosition) { - if (mSyncFolderItems.get(section).getFilePaths() != null) { + if (section < filteredSyncFolderItems.size() && filteredSyncFolderItems.get(section).getFilePaths() != null) { MainViewHolder holder = (MainViewHolder) commonHolder; - File file = new File(mSyncFolderItems.get(section).getFilePaths().get(relativePosition)); + File file = new File(filteredSyncFolderItems.get(section).getFilePaths().get(relativePosition)); ThumbnailsCacheManager.MediaThumbnailGenerationTask task = - new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.image, mContext); + new ThumbnailsCacheManager.MediaThumbnailGenerationTask(holder.image, context); ThumbnailsCacheManager.AsyncMediaThumbnailDrawable asyncDrawable = new ThumbnailsCacheManager.AsyncMediaThumbnailDrawable( - mContext.getResources(), - ThumbnailsCacheManager.mDefaultImg, - task + context.getResources(), + ThumbnailsCacheManager.mDefaultImg, + task ); holder.image.setImageDrawable(asyncDrawable); @@ -206,11 +337,11 @@ public void onBindViewHolder(SectionedViewHolder commonHolder, int section, int // set proper tag holder.image.setTag(file.hashCode()); - holder.itemView.setTag(relativePosition % mGridWidth); + holder.itemView.setTag(relativePosition % gridWidth); - if (mSyncFolderItems.get(section).getNumberOfFiles() > mGridTotal && relativePosition >= mGridTotal - 1) { + if (filteredSyncFolderItems.get(section).getNumberOfFiles() > gridTotal && relativePosition >= gridTotal - 1) { holder.counterValue.setText(String.format(Locale.US, "%d", - mSyncFolderItems.get(section).getNumberOfFiles() - mGridTotal)); + filteredSyncFolderItems.get(section).getNumberOfFiles() - gridTotal)); holder.counterBar.setVisibility(View.VISIBLE); holder.thumbnailDarkener.setVisibility(View.VISIBLE); } else { @@ -226,15 +357,26 @@ public SectionedViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int vie if (viewType == VIEW_TYPE_HEADER) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_item_header, parent, false); return new HeaderViewHolder(v); + } else if (viewType == VIEW_TYPE_FOOTER) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_footer, parent, false); + return new FooterViewHolder(v); + } else if (viewType == VIEW_TYPE_EMPTY) { + View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.synced_folders_empty, parent, false); + return new EmptyViewHolder(v); } else { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.grid_sync_item, parent, false); return new MainViewHolder(v); } } + private boolean isLastSection(int section) { + return section >= getSectionCount() - 1; + } + public interface ClickListener { void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem); void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem); + void onVisibilityToggleClick(int section, SyncedFolderDisplayItem item); } static class HeaderViewHolder extends SectionedViewHolder { @@ -259,7 +401,29 @@ private HeaderViewHolder(View itemView) { } } + static class FooterViewHolder extends SectionedViewHolder { + @BindView(R.id.footer_container) + public LinearLayout mainFooterContainer; + + @BindView(R.id.footer_text) + public TextView title; + + private FooterViewHolder(View itemView) { + super(itemView); + ButterKnife.bind(this, itemView); + } + } + + static class EmptyViewHolder extends SectionedViewHolder { + private EmptyViewHolder(View itemView) { + super(itemView); + } + } + static class MainViewHolder extends SectionedViewHolder { + @BindView(R.id.grid_item_container) + public FrameLayout item_container; + @BindView(R.id.thumbnail) public ImageView image; @@ -281,7 +445,7 @@ private MainViewHolder(View itemView) { private void setSyncButtonActiveIcon(ImageButton syncStatusButton, boolean enabled) { if (enabled) { syncStatusButton.setImageDrawable(ThemeUtils.tintDrawable(R.drawable.ic_cloud_sync_on, - ThemeUtils.primaryColor(mContext))); + ThemeUtils.primaryColor(context))); } else { syncStatusButton.setImageResource(R.drawable.ic_cloud_sync_off); } diff --git a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java index c435857cc961..d867e568a4c6 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java +++ b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java @@ -34,63 +34,67 @@ */ @NoArgsConstructor public class SyncedFolderParcelable implements Parcelable { - private String mFolderName; - private String mLocalPath; - private String mRemotePath; - private Boolean mWifiOnly = false; - private Boolean mChargingOnly = false; - private Boolean mEnabled = false; - private Boolean mSubfolderByDate = false; - private Integer mUploadAction; - private MediaFolderType mType; - private long mId; - private String mAccount; - private int mSection; + private String folderName; + private String localPath; + private String remotePath; + private Boolean wifiOnly = false; + private Boolean chargingOnly = false; + private Boolean enabled = false; + private Boolean subfolderByDate = false; + private Integer uploadAction; + private MediaFolderType type; + private Boolean hidden = false; + private long id; + private String account; + private int section; public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, int section) { - mId = syncedFolderDisplayItem.getId(); - mFolderName = syncedFolderDisplayItem.getFolderName(); - mLocalPath = syncedFolderDisplayItem.getLocalPath(); - mRemotePath = syncedFolderDisplayItem.getRemotePath(); - mWifiOnly = syncedFolderDisplayItem.getWifiOnly(); - mChargingOnly = syncedFolderDisplayItem.getChargingOnly(); - mEnabled = syncedFolderDisplayItem.isEnabled(); - mSubfolderByDate = syncedFolderDisplayItem.getSubfolderByDate(); - mType = syncedFolderDisplayItem.getType(); - mAccount = syncedFolderDisplayItem.getAccount(); - mUploadAction = syncedFolderDisplayItem.getUploadAction(); - mSection = section; + id = syncedFolderDisplayItem.getId(); + folderName = syncedFolderDisplayItem.getFolderName(); + localPath = syncedFolderDisplayItem.getLocalPath(); + remotePath = syncedFolderDisplayItem.getRemotePath(); + wifiOnly = syncedFolderDisplayItem.getWifiOnly(); + chargingOnly = syncedFolderDisplayItem.getChargingOnly(); + enabled = syncedFolderDisplayItem.isEnabled(); + subfolderByDate = syncedFolderDisplayItem.getSubfolderByDate(); + type = syncedFolderDisplayItem.getType(); + account = syncedFolderDisplayItem.getAccount(); + uploadAction = syncedFolderDisplayItem.getUploadAction(); + this.section = section; + hidden = syncedFolderDisplayItem.getHidden(); } private SyncedFolderParcelable(Parcel read) { - mId = read.readLong(); - mFolderName = read.readString(); - mLocalPath = read.readString(); - mRemotePath = read.readString(); - mWifiOnly = read.readInt()!= 0; - mChargingOnly = read.readInt() != 0; - mEnabled = read.readInt() != 0; - mSubfolderByDate = read.readInt() != 0; - mType = MediaFolderType.getById(read.readInt()); - mAccount = read.readString(); - mUploadAction = read.readInt(); - mSection = read.readInt(); + id = read.readLong(); + folderName = read.readString(); + localPath = read.readString(); + remotePath = read.readString(); + wifiOnly = read.readInt()!= 0; + chargingOnly = read.readInt() != 0; + enabled = read.readInt() != 0; + subfolderByDate = read.readInt() != 0; + type = MediaFolderType.getById(read.readInt()); + account = read.readString(); + uploadAction = read.readInt(); + section = read.readInt(); + hidden = read.readInt() != 0; } @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeLong(mId); - dest.writeString(mFolderName); - dest.writeString(mLocalPath); - dest.writeString(mRemotePath); - dest.writeInt(mWifiOnly ? 1 : 0); - dest.writeInt(mChargingOnly ? 1 : 0); - dest.writeInt(mEnabled ? 1 : 0); - dest.writeInt(mSubfolderByDate ? 1 : 0); - dest.writeInt(mType.getId()); - dest.writeString(mAccount); - dest.writeInt(mUploadAction); - dest.writeInt(mSection); + dest.writeLong(id); + dest.writeString(folderName); + dest.writeString(localPath); + dest.writeString(remotePath); + dest.writeInt(wifiOnly ? 1 : 0); + dest.writeInt(chargingOnly ? 1 : 0); + dest.writeInt(enabled ? 1 : 0); + dest.writeInt(subfolderByDate ? 1 : 0); + dest.writeInt(type.getId()); + dest.writeString(account); + dest.writeInt(uploadAction); + dest.writeInt(section); + dest.writeInt(hidden ? 1 : 0); } public static final Creator CREATOR = @@ -113,75 +117,83 @@ public int describeContents() { } public String getFolderName() { - return mFolderName; + return folderName; } public void setFolderName(String mFolderName) { - this.mFolderName = mFolderName; + this.folderName = mFolderName; } public String getLocalPath() { - return mLocalPath; + return localPath; } public void setLocalPath(String mLocalPath) { - this.mLocalPath = mLocalPath; + this.localPath = mLocalPath; } public String getRemotePath() { - return mRemotePath; + return remotePath; } public void setRemotePath(String mRemotePath) { - this.mRemotePath = mRemotePath; + this.remotePath = mRemotePath; } public Boolean getWifiOnly() { - return mWifiOnly; + return wifiOnly; } public void setWifiOnly(Boolean mWifiOnly) { - this.mWifiOnly = mWifiOnly; + this.wifiOnly = mWifiOnly; } public Boolean getChargingOnly() { - return mChargingOnly; + return chargingOnly; } public void setChargingOnly(Boolean mChargingOnly) { - this.mChargingOnly = mChargingOnly; + this.chargingOnly = mChargingOnly; } public Boolean getEnabled() { - return mEnabled; + return enabled; } public void setEnabled(boolean mEnabled) { - this.mEnabled = mEnabled; + this.enabled = mEnabled; } public Boolean getSubfolderByDate() { - return mSubfolderByDate; + return subfolderByDate; } public void setSubfolderByDate(Boolean mSubfolderByDate) { - this.mSubfolderByDate = mSubfolderByDate; + this.subfolderByDate = mSubfolderByDate; } public MediaFolderType getType() { - return mType; + return type; } public void setType(MediaFolderType mType) { - this.mType = mType; + this.type = mType; + } + + public Boolean getHidden() { + return hidden; + } + + public void setHidden(boolean mHidden) { + this.hidden = mHidden; } public Integer getUploadAction() { - return mUploadAction; + return uploadAction; } public Integer getUploadActionInteger() { - switch (mUploadAction) { + switch (uploadAction) { case FileUploader.LOCAL_BEHAVIOUR_FORGET: return 0; case FileUploader.LOCAL_BEHAVIOUR_MOVE: @@ -195,38 +207,38 @@ public Integer getUploadActionInteger() { public void setUploadAction(String mUploadAction) { switch (mUploadAction) { case "LOCAL_BEHAVIOUR_FORGET": - this.mUploadAction = FileUploader.LOCAL_BEHAVIOUR_FORGET; + this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_FORGET; break; case "LOCAL_BEHAVIOUR_MOVE": - this.mUploadAction = FileUploader.LOCAL_BEHAVIOUR_MOVE; + this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_MOVE; break; case "LOCAL_BEHAVIOUR_DELETE": - this.mUploadAction = FileUploader.LOCAL_BEHAVIOUR_DELETE; + this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_DELETE; break; } } public long getId() { - return mId; + return id; } - public void setId(long mId) { - this.mId = mId; + public void setId(long id) { + this.id = id; } public String getAccount() { - return mAccount; + return account; } - public void setAccount(String mAccount) { - this.mAccount = mAccount; + public void setAccount(String account) { + this.account = account; } public int getSection() { - return mSection; + return section; } - public void setSection(int mSection) { - this.mSection = mSection; + public void setSection(int section) { + this.section = section; } } diff --git a/src/main/res/layout/synced_folders_empty.xml b/src/main/res/layout/synced_folders_empty.xml new file mode 100644 index 000000000000..b17c9141d0d0 --- /dev/null +++ b/src/main/res/layout/synced_folders_empty.xml @@ -0,0 +1,6 @@ + + + + diff --git a/src/main/res/layout/synced_folders_footer.xml b/src/main/res/layout/synced_folders_footer.xml new file mode 100644 index 000000000000..1112c5d0649a --- /dev/null +++ b/src/main/res/layout/synced_folders_footer.xml @@ -0,0 +1,38 @@ + + + + + + + diff --git a/src/main/res/menu/synced_folders_adapter.xml b/src/main/res/menu/synced_folders_adapter.xml new file mode 100644 index 000000000000..50b8a92ea3df --- /dev/null +++ b/src/main/res/menu/synced_folders_adapter.xml @@ -0,0 +1,28 @@ + + + + + + + diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 185272de0e65..33ddc3bb7456 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -627,6 +627,9 @@ Set up a custom folder Create new custom folder setup + Show folder + Hide folder + Configure Configure folders @@ -901,4 +904,7 @@ folder file Share internal link + Hide folder in auto upload list + Hide folder + Show hidden folders diff --git a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 2861b3e3c058..81de268fdab1 100644 --- a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -181,6 +181,7 @@ private SyncedFolderDisplayItem create(String folderName, Boolean enabled) { new ArrayList(), folderName, 2, - MediaFolderType.IMAGE); + MediaFolderType.IMAGE, + false); } } From ca6918f9fcd0ae309aad58ba80d247fc9eb9062e Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sat, 2 Nov 2019 18:06:20 +0100 Subject: [PATCH 56/69] show/hide improvements when list is completed hidden + nicer empty UI + action button Signed-off-by: Andy Scherzinger --- .../ui/activity/SyncedFoldersActivity.java | 121 +++++++++++++++--- .../ui/adapter/SyncedFolderAdapter.java | 23 ++++ src/main/res/layout/empty_list.xml | 16 ++- src/main/res/layout/synced_folders_layout.xml | 34 +---- src/main/res/menu/synced_folders_adapter.xml | 2 +- src/main/res/values/strings.xml | 10 +- 6 files changed, 151 insertions(+), 55 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 3d08d4193b4e..c5f8a70d842b 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -38,9 +38,12 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; +import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ProgressBar; import android.widget.TextView; +import com.google.android.material.button.MaterialButton; import com.nextcloud.client.account.User; import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; @@ -89,6 +92,10 @@ import androidx.fragment.app.FragmentTransaction; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; +import butterknife.BindView; +import butterknife.ButterKnife; +import butterknife.OnClick; +import butterknife.Unbinder; import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID; @@ -104,10 +111,28 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA private static final String SYNCED_FOLDER_PREFERENCES_DIALOG_TAG = "SYNCED_FOLDER_PREFERENCES_DIALOG"; private static final String TAG = SyncedFoldersActivity.class.getSimpleName(); - private RecyclerView mRecyclerView; + @BindView(R.id.empty_list_view) + public LinearLayout emptyContentContainer; + + @BindView(R.id.empty_list_icon) + public ImageView emptyContentIcon; + + @BindView(R.id.empty_list_progress) + public ProgressBar emptyContentProgressBar; + + @BindView(R.id.empty_list_view_headline) + public TextView emptyContentHeadline; + + @BindView(R.id.empty_list_view_text) + public TextView emptyContentMessage; + + @BindView(R.id.empty_list_view_action) + public MaterialButton emptyContentActionButton; + + @BindView(android.R.id.list) + public RecyclerView mRecyclerView; + private SyncedFolderAdapter mAdapter; - private LinearLayout mProgress; - private TextView mEmpty; private SyncedFolderProvider mSyncedFolderProvider; private SyncedFolderPreferencesDialogFragment mSyncedFolderPreferencesDialogFragment; private boolean showSidebar = true; @@ -127,6 +152,7 @@ protected void onCreate(Bundle savedInstanceState) { } setContentView(R.layout.synced_folders_layout); + ButterKnife.bind(this); if (getIntent() != null && getIntent().getExtras() != null) { final String accountName = getIntent().getExtras().getString(NotificationJob.KEY_NOTIFICATION_ACCOUNT); @@ -220,15 +246,13 @@ private void showPowerCheckDialog() { * sets up the UI elements and loads all media/synced folders. */ private void setupContent() { - mRecyclerView = findViewById(android.R.id.list); - - mProgress = findViewById(android.R.id.progress); - mEmpty = findViewById(android.R.id.empty); - final int gridWidth = getResources().getInteger(R.integer.media_grid_width); boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light); mAdapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion); mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock); + emptyContentIcon.setImageResource(R.drawable.nav_synced_folders); + emptyContentActionButton.setBackgroundColor(ThemeUtils.primaryColor(this)); + emptyContentActionButton.setTextColor(ThemeUtils.fontColor(this)); final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); mAdapter.setLayoutManager(lm); @@ -240,6 +264,13 @@ private void setupContent() { load(gridWidth * 2, false); } + @OnClick(R.id.empty_list_view_action) + public void showHiddenItems() { + mAdapter.toggleHiddenItemsVisibility(); + emptyContentContainer.setVisibility(View.GONE); + mRecyclerView.setVisibility(View.VISIBLE); + } + /** * loads all media/synced folders, adds them to the recycler view adapter and shows the list. * @@ -249,7 +280,7 @@ private void load(final int perFolderMediaItemLimit, boolean force) { if (mAdapter.getItemCount() > 0 && !force) { return; } - setListShown(false); + showLoadingContent(); final List mediaFolders = MediaProvider.getImageFolders(getContentResolver(), perFolderMediaItemLimit, this, false); mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(), perFolderMediaItemLimit, @@ -274,7 +305,7 @@ private void load(final int perFolderMediaItemLimit, boolean force) { mAdapter.setSyncFolderItems(syncFolderItems); mAdapter.notifyDataSetChanged(); - setListShown(true); + showList(); if (!TextUtils.isEmpty(path)) { int section = mAdapter.getSectionByLocalPathAndType(path, type); @@ -493,15 +524,31 @@ private Map createSyncedFoldersMap(List sync } /** - * show/hide recycler view list or the empty message / progress info. - * - * @param shown flag if list should be shown + * show recycler view list or the empty message info (in case list is empty). */ - private void setListShown(boolean shown) { + private void showList() { if (mRecyclerView != null) { - mRecyclerView.setVisibility(shown ? View.VISIBLE : View.GONE); - mProgress.setVisibility(shown ? View.GONE : View.VISIBLE); - mEmpty.setVisibility(shown && mAdapter.getItemCount() == 0 ? View.VISIBLE : View.GONE); + mRecyclerView.setVisibility(View.VISIBLE ); + emptyContentProgressBar.setVisibility(View.GONE); + + if (mAdapter.getSectionCount() == 0 && mAdapter.getUnfilteredSectionCount() > mAdapter.getSectionCount()) { + emptyContentContainer.setVisibility(View.VISIBLE); + int hiddenFoldersCount = mAdapter.getHiddenFolderCount(); + + showEmptyContent(getString(R.string.drawer_synced_folders), + getResources().getQuantityString(R.plurals.synced_folders_hidden_folders_count, + hiddenFoldersCount, + hiddenFoldersCount), + getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders, + hiddenFoldersCount, + hiddenFoldersCount)); + } else if (mAdapter.getSectionCount() == 0 && mAdapter.getUnfilteredSectionCount() == 0) { + emptyContentContainer.setVisibility(View.VISIBLE); + showEmptyContent(getString(R.string.drawer_synced_folders), + getString(R.string.synced_folders_no_results)); + } else { + emptyContentContainer.setVisibility(View.GONE); + } } } @@ -593,6 +640,45 @@ public void onVisibilityToggleClick(int section, SyncedFolderDisplayItem syncedF mAdapter.setSyncFolderItem(section, syncedFolder); } + private void showEmptyContent(String headline, String message) { + showEmptyContent(headline, message, false); + emptyContentActionButton.setVisibility(View.GONE); + } + + private void showEmptyContent(String headline, String message, String action) { + showEmptyContent(headline, message, false); + emptyContentActionButton.setText(action); + emptyContentActionButton.setVisibility(View.VISIBLE); + } + + private void showLoadingContent() { + showEmptyContent( + getString(R.string.drawer_synced_folders), + getString(R.string.synced_folders_loading_folders), + true + ); + emptyContentActionButton.setVisibility(View.GONE); + } + + private void showEmptyContent(String headline, String message, boolean loading) { + if (emptyContentContainer != null) { + emptyContentContainer.setVisibility(View.VISIBLE); + mRecyclerView.setVisibility(View.GONE); + + emptyContentHeadline.setText(headline); + emptyContentMessage.setText(message); + emptyContentMessage.setVisibility(View.VISIBLE); + + if (loading) { + emptyContentProgressBar.setVisibility(View.VISIBLE); + emptyContentIcon.setVisibility(View.GONE); + } else { + emptyContentProgressBar.setVisibility(View.GONE); + emptyContentIcon.setVisibility(View.VISIBLE); + } + } + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_REMOTE_FOLDER @@ -742,7 +828,6 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String permissi @Override protected void onResume() { super.onResume(); - setDrawerMenuItemChecked(R.id.nav_synced_folders); } diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index 2c636b1b3b87..1aa354dd89db 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -169,6 +169,14 @@ public int getSectionCount() { } } + public int getUnfilteredSectionCount() { + if (syncFolderItems.size() > 0) { + return syncFolderItems.size() + 1; + } else { + return 0; + } + } + @Override public int getItemCount(int section) { if (section < filteredSyncFolderItems.size()) { @@ -310,6 +318,13 @@ public void onBindFooterViewHolder(SectionedViewHolder holder, int section) { if (isLastSection(section) && showFooter()) { FooterViewHolder footerHolder = (FooterViewHolder) holder; footerHolder.title.setOnClickListener(v -> toggleHiddenItemsVisibility()); + footerHolder.title.setText( + context.getResources().getQuantityString( + R.plurals.synced_folders_show_hidden_folders, + getHiddenFolderCount(), + getHiddenFolderCount() + ) + ); } } @@ -373,6 +388,14 @@ private boolean isLastSection(int section) { return section >= getSectionCount() - 1; } + public int getHiddenFolderCount() { + if (syncFolderItems != null && filteredSyncFolderItems != null) { + return syncFolderItems.size() - filteredSyncFolderItems.size(); + } else { + return 0; + } + } + public interface ClickListener { void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem); void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem); diff --git a/src/main/res/layout/empty_list.xml b/src/main/res/layout/empty_list.xml index 2951f2ae12c9..4dfc592e5fac 100644 --- a/src/main/res/layout/empty_list.xml +++ b/src/main/res/layout/empty_list.xml @@ -19,6 +19,7 @@ License along with this program. If not, see . --> @@ -62,7 +63,18 @@ android:layout_gravity="center_horizontal" android:ellipsize="end" android:gravity="center" + android:paddingTop="@dimen/standard_half_padding" + android:paddingBottom="@dimen/standard_half_padding" android:text="@string/file_list_empty" android:visibility="gone"/> - \ No newline at end of file + + + diff --git a/src/main/res/layout/synced_folders_layout.xml b/src/main/res/layout/synced_folders_layout.xml index 9973ea5bcc3d..2846dec1e0ab 100644 --- a/src/main/res/layout/synced_folders_layout.xml +++ b/src/main/res/layout/synced_folders_layout.xml @@ -50,39 +50,7 @@ android:orientation="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"> - - - - - - - - + + android:title="@string/autoupload_hide_folder" /> Choose remote folder… Choose local folder… Loading folders… - No media folders found. + No media folders found + + Show %1$d hidden folder + Show %1$d hidden folders + + + %1$d hidden folder + %1$d hidden folders + Preferences for auto uploading Instant uploading has been revamped completely. Re-configure your auto upload from within the main menu.\n\nEnjoy the new and extended auto uploading. For %1$s From bedc2b48ee104c7256df25424210e572034c5f63 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sat, 2 Nov 2019 18:21:24 +0100 Subject: [PATCH 57/69] housekeeping, removing unused resources Signed-off-by: Andy Scherzinger --- src/main/res/layout/synced_folders_footer.xml | 3 ++- src/main/res/values/dims.xml | 2 -- src/main/res/values/strings.xml | 3 --- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/res/layout/synced_folders_footer.xml b/src/main/res/layout/synced_folders_footer.xml index 1112c5d0649a..46cc018b65c5 100644 --- a/src/main/res/layout/synced_folders_footer.xml +++ b/src/main/res/layout/synced_folders_footer.xml @@ -18,6 +18,7 @@ License along with this program. If not, see . --> diff --git a/src/main/res/values/dims.xml b/src/main/res/values/dims.xml index 1db7bb7af6b7..42a3bd131bd6 100644 --- a/src/main/res/values/dims.xml +++ b/src/main/res/values/dims.xml @@ -133,8 +133,6 @@ 32dp 32dp 24dp - 72dp - 72dp -3dp 12dp 16sp diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index aa0edd4d13b6..d53671c4ccbe 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -912,7 +912,4 @@ folder file Share internal link - Hide folder in auto upload list - Hide folder - Show hidden folders From 7c62ad6b06492400ed7156d671fc73543c31f268 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sat, 2 Nov 2019 18:45:18 +0100 Subject: [PATCH 58/69] unify use of boolean, make use of lombok getter/setters, remove unneeded boolean boxing Signed-off-by: Andy Scherzinger --- .../datamodel/SyncedFolderProvider.java | 20 +-- .../owncloud/android/jobs/FilesSyncJob.java | 6 +- .../ui/activity/SyncedFoldersActivity.java | 21 ++- .../ui/adapter/SyncedFolderAdapter.java | 6 +- ...SyncedFolderPreferencesDialogFragment.java | 16 +- .../dialog/parcel/SyncedFolderParcelable.java | 140 +++--------------- .../activity/SyncedFoldersActivityTest.java | 12 +- 7 files changed, 56 insertions(+), 165 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index 6a8caf710671..f5fc3009d777 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -338,23 +338,23 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH)); String remotePath = cursor.getString(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH)); - Boolean wifiOnly = cursor.getInt(cursor.getColumnIndex( + boolean wifiOnly = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1; - Boolean chargingOnly = cursor.getInt(cursor.getColumnIndex( + boolean chargingOnly = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1; - Boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex( + boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1; String accountName = cursor.getString(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT)); - Integer uploadAction = cursor.getInt(cursor.getColumnIndex( + int uploadAction = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION)); - Boolean enabled = cursor.getInt(cursor.getColumnIndex( + boolean enabled = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED)) == 1; long enabledTimestampMs = cursor.getLong(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS)); MediaFolderType type = MediaFolderType.getById(cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE))); - Boolean hidden = cursor.getInt(cursor.getColumnIndex( + boolean hidden = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1; syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, @@ -374,15 +374,15 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol ContentValues cv = new ContentValues(); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_LOCAL_PATH, syncedFolder.getLocalPath()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH, syncedFolder.getRemotePath()); - cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.getWifiOnly()); - cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.getChargingOnly()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.isWifiOnly()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.isChargingOnly()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs()); - cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.getSubfolderByDate()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.isSubfolderByDate()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ACCOUNT, syncedFolder.getAccount()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_UPLOAD_ACTION, syncedFolder.getUploadAction()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_TYPE, syncedFolder.getType().getId()); - cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.getHidden()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN, syncedFolder.isHidden()); return cv; } diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 5c7a9771aac1..32b3002d7f9c 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -192,10 +192,10 @@ private void syncFolder(Context context, Resources resources, boolean lightVersi remotePath = resources.getString(R.string.syncedFolder_remote_folder); } else { - needsCharging = syncedFolder.getChargingOnly(); - needsWifi = syncedFolder.getWifiOnly(); + needsCharging = syncedFolder.isChargingOnly(); + needsWifi = syncedFolder.isWifiOnly(); uploadAction = syncedFolder.getUploadAction(); - subfolderByDate = syncedFolder.getSubfolderByDate(); + subfolderByDate = syncedFolder.isSubfolderByDate(); remotePath = syncedFolder.getRemotePath(); } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index c5f8a70d842b..e683d1cff003 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -95,7 +95,6 @@ import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; -import butterknife.Unbinder; import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; import static com.owncloud.android.datamodel.SyncedFolderDisplayItem.UNPERSISTED_ID; @@ -702,9 +701,9 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { if (MediaFolderType.CUSTOM == syncedFolder.getType() && syncedFolder.getId() == UNPERSISTED_ID) { SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem( SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), - syncedFolder.getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), - syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.getEnabled(), - clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.getHidden()); + syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), + syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), + clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden()); saveOrUpdateSyncedFolder(newCustomFolder); mAdapter.addSyncFolderItem(newCustomFolder); @@ -712,8 +711,8 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { SyncedFolderDisplayItem item = mAdapter.get(syncedFolder.getSection()); updateSyncedFolderItem(item, syncedFolder.getId(), syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder - .getWifiOnly(), syncedFolder.getChargingOnly(), syncedFolder.getSubfolderByDate(), syncedFolder - .getUploadAction(), syncedFolder.getEnabled()); + .isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), syncedFolder + .getUploadAction(), syncedFolder.isEnabled()); saveOrUpdateSyncedFolder(item); @@ -723,7 +722,7 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { mSyncedFolderPreferencesDialogFragment = null; - if (syncedFolder.getEnabled()) { + if (syncedFolder.isEnabled()) { showBatteryOptimizationInfo(); } } @@ -789,11 +788,11 @@ private void updateSyncedFolderItem(SyncedFolderDisplayItem item, long id, String localPath, String remotePath, - Boolean wifiOnly, - Boolean chargingOnly, - Boolean subfolderByDate, + boolean wifiOnly, + boolean chargingOnly, + boolean subfolderByDate, Integer uploadAction, - Boolean enabled) { + boolean enabled) { item.setId(id); item.setLocalPath(localPath); item.setRemotePath(remotePath); diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index 1aa354dd89db..f595d76a953e 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -145,7 +145,9 @@ public void removeItem(int section) { * @return Non-hidden items */ private List filterHiddenItems(List items, boolean hide) { - if (hide) { + if (!hide) { + return items; + } else { List ret = new ArrayList<>(); for (SyncedFolderDisplayItem item : items) { @@ -155,8 +157,6 @@ private List filterHiddenItems(List= Build.VERSION_CODES.JELLY_BEAN_MR1) { - mUploadOnChargingCheckbox.setChecked(mSyncedFolder.getChargingOnly()); + mUploadOnChargingCheckbox.setChecked(mSyncedFolder.isChargingOnly()); } - mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.getSubfolderByDate()); + mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.isSubfolderByDate()); mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]); } @@ -344,7 +344,7 @@ private void setupListeners(View view) { new OnClickListener() { @Override public void onClick(View v) { - mSyncedFolder.setWifiOnly(!mSyncedFolder.getWifiOnly()); + mSyncedFolder.setWifiOnly(!mSyncedFolder.isWifiOnly()); mUploadOnWifiCheckbox.toggle(); } }); @@ -355,7 +355,7 @@ public void onClick(View v) { new OnClickListener() { @Override public void onClick(View v) { - mSyncedFolder.setChargingOnly(!mSyncedFolder.getChargingOnly()); + mSyncedFolder.setChargingOnly(!mSyncedFolder.isChargingOnly()); mUploadOnChargingCheckbox.toggle(); } }); @@ -365,7 +365,7 @@ public void onClick(View v) { new OnClickListener() { @Override public void onClick(View v) { - mSyncedFolder.setSubfolderByDate(!mSyncedFolder.getSubfolderByDate()); + mSyncedFolder.setSubfolderByDate(!mSyncedFolder.isSubfolderByDate()); mUploadUseSubfoldersCheckbox.toggle(); } }); @@ -401,7 +401,7 @@ public void onClick(View v) { view.findViewById(R.id.sync_enabled).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { - setEnabled(!mSyncedFolder.getEnabled()); + setEnabled(!mSyncedFolder.isEnabled()); } }); diff --git a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java index d867e568a4c6..2152eeb29e83 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java +++ b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java @@ -27,41 +27,43 @@ import com.owncloud.android.datamodel.SyncedFolderDisplayItem; import com.owncloud.android.files.services.FileUploader; +import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; /** * Parcelable for {@link SyncedFolderDisplayItem} objects to transport them from/to dialog fragments. */ @NoArgsConstructor public class SyncedFolderParcelable implements Parcelable { - private String folderName; - private String localPath; - private String remotePath; - private Boolean wifiOnly = false; - private Boolean chargingOnly = false; - private Boolean enabled = false; - private Boolean subfolderByDate = false; - private Integer uploadAction; - private MediaFolderType type; - private Boolean hidden = false; - private long id; - private String account; - private int section; + @Getter @Setter private String folderName; + @Getter @Setter private String localPath; + @Getter @Setter private String remotePath; + @Getter @Setter private boolean wifiOnly = false; + @Getter @Setter private boolean chargingOnly = false; + @Getter @Setter private boolean enabled = false; + @Getter @Setter private boolean subfolderByDate = false; + @Getter private Integer uploadAction; + @Getter @Setter private MediaFolderType type; + @Getter @Setter private boolean hidden = false; + @Getter @Setter private long id; + @Getter @Setter private String account; + @Getter @Setter private int section; public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, int section) { id = syncedFolderDisplayItem.getId(); folderName = syncedFolderDisplayItem.getFolderName(); localPath = syncedFolderDisplayItem.getLocalPath(); remotePath = syncedFolderDisplayItem.getRemotePath(); - wifiOnly = syncedFolderDisplayItem.getWifiOnly(); - chargingOnly = syncedFolderDisplayItem.getChargingOnly(); + wifiOnly = syncedFolderDisplayItem.isWifiOnly(); + chargingOnly = syncedFolderDisplayItem.isChargingOnly(); enabled = syncedFolderDisplayItem.isEnabled(); - subfolderByDate = syncedFolderDisplayItem.getSubfolderByDate(); + subfolderByDate = syncedFolderDisplayItem.isSubfolderByDate(); type = syncedFolderDisplayItem.getType(); account = syncedFolderDisplayItem.getAccount(); uploadAction = syncedFolderDisplayItem.getUploadAction(); this.section = section; - hidden = syncedFolderDisplayItem.getHidden(); + hidden = syncedFolderDisplayItem.isHidden(); } private SyncedFolderParcelable(Parcel read) { @@ -116,82 +118,6 @@ public int describeContents() { return 0; } - public String getFolderName() { - return folderName; - } - - public void setFolderName(String mFolderName) { - this.folderName = mFolderName; - } - - public String getLocalPath() { - return localPath; - } - - public void setLocalPath(String mLocalPath) { - this.localPath = mLocalPath; - } - - public String getRemotePath() { - return remotePath; - } - - public void setRemotePath(String mRemotePath) { - this.remotePath = mRemotePath; - } - - public Boolean getWifiOnly() { - return wifiOnly; - } - - public void setWifiOnly(Boolean mWifiOnly) { - this.wifiOnly = mWifiOnly; - } - - public Boolean getChargingOnly() { - return chargingOnly; - } - - public void setChargingOnly(Boolean mChargingOnly) { - this.chargingOnly = mChargingOnly; - } - - public Boolean getEnabled() { - return enabled; - } - - public void setEnabled(boolean mEnabled) { - this.enabled = mEnabled; - } - - public Boolean getSubfolderByDate() { - return subfolderByDate; - } - - public void setSubfolderByDate(Boolean mSubfolderByDate) { - this.subfolderByDate = mSubfolderByDate; - } - - public MediaFolderType getType() { - return type; - } - - public void setType(MediaFolderType mType) { - this.type = mType; - } - - public Boolean getHidden() { - return hidden; - } - - public void setHidden(boolean mHidden) { - this.hidden = mHidden; - } - - public Integer getUploadAction() { - return uploadAction; - } - public Integer getUploadActionInteger() { switch (uploadAction) { case FileUploader.LOCAL_BEHAVIOUR_FORGET: @@ -204,8 +130,8 @@ public Integer getUploadActionInteger() { return 0; } - public void setUploadAction(String mUploadAction) { - switch (mUploadAction) { + public void setUploadAction(String uploadAction) { + switch (uploadAction) { case "LOCAL_BEHAVIOUR_FORGET": this.uploadAction = FileUploader.LOCAL_BEHAVIOUR_FORGET; break; @@ -217,28 +143,4 @@ public void setUploadAction(String mUploadAction) { break; } } - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getAccount() { - return account; - } - - public void setAccount(String account) { - this.account = account; - } - - public int getSection() { - return section; - } - - public void setSection(int section) { - this.section = section; - } } diff --git a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 81de268fdab1..50046bb01bb3 100644 --- a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -32,8 +32,6 @@ import java.util.Collections; import java.util.List; -import third_parties.daveKoeller.AlphanumComparator; - import static org.junit.Assert.assertTrue; public class SyncedFoldersActivityTest { @@ -123,14 +121,6 @@ private boolean sortAndTest(List sortedList) { return test(sortedList, SyncedFoldersActivity.sortSyncedFolderItems(unsortedList)); } - private List sort(List sortedList) { - List unsortedList = shuffle(sortedList); - - Collections.sort(unsortedList, new AlphanumComparator<>()); - - return unsortedList; - } - private boolean test(List target, List actual) { for (int i = 0; i < target.size(); i++) { @@ -167,7 +157,7 @@ private boolean test(List target, List Date: Sat, 2 Nov 2019 21:51:04 +0000 Subject: [PATCH 59/69] Drone: update FindBugs results to reflect reduced error/warning count [skip ci] Signed-off-by: nextcloud-android-bot --- scripts/analysis/findbugs-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 2da56739379b..8e9088e93bac 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -418 \ No newline at end of file +411 \ No newline at end of file From 3e872df68cee31c2fa109107ee37b41930ab87eb Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Sat, 2 Nov 2019 23:02:58 +0100 Subject: [PATCH 60/69] add more spotbug exclusions (for 3rd party libs) Signed-off-by: Andy Scherzinger --- spotbugs-filter.xml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/spotbugs-filter.xml b/spotbugs-filter.xml index 05c145793143..1b374ca4fed1 100644 --- a/spotbugs-filter.xml +++ b/spotbugs-filter.xml @@ -17,7 +17,8 @@ - + + @@ -30,7 +31,7 @@ - + @@ -39,6 +40,10 @@ + + + + From 74bfe473c498d1a36fb5542ff79f14cc1c51304c Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Tue, 5 Nov 2019 22:26:39 +0100 Subject: [PATCH 61/69] remove action button, use checkable menu item for shown/hidden state display (design review) Signed-off-by: Andy Scherzinger --- .../ui/activity/SyncedFoldersActivity.java | 27 +++++-------------- .../ui/adapter/SyncedFolderAdapter.java | 14 +++------- src/main/res/layout/empty_list.xml | 11 -------- src/main/res/menu/synced_folders_adapter.xml | 3 ++- src/main/res/values/strings.xml | 5 ---- 5 files changed, 12 insertions(+), 48 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index e683d1cff003..a0d4bb9c4c00 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -43,7 +43,6 @@ import android.widget.ProgressBar; import android.widget.TextView; -import com.google.android.material.button.MaterialButton; import com.nextcloud.client.account.User; import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; @@ -125,9 +124,6 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA @BindView(R.id.empty_list_view_text) public TextView emptyContentMessage; - @BindView(R.id.empty_list_view_action) - public MaterialButton emptyContentActionButton; - @BindView(android.R.id.list) public RecyclerView mRecyclerView; @@ -250,8 +246,6 @@ private void setupContent() { mAdapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion); mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock); emptyContentIcon.setImageResource(R.drawable.nav_synced_folders); - emptyContentActionButton.setBackgroundColor(ThemeUtils.primaryColor(this)); - emptyContentActionButton.setTextColor(ThemeUtils.fontColor(this)); final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); mAdapter.setLayoutManager(lm); @@ -263,11 +257,13 @@ private void setupContent() { load(gridWidth * 2, false); } - @OnClick(R.id.empty_list_view_action) + @OnClick(R.id.empty_list_view_text) public void showHiddenItems() { - mAdapter.toggleHiddenItemsVisibility(); - emptyContentContainer.setVisibility(View.GONE); - mRecyclerView.setVisibility(View.VISIBLE); + if (mAdapter.getSectionCount() == 0 && mAdapter.getUnfilteredSectionCount() > mAdapter.getSectionCount()) { + mAdapter.toggleHiddenItemsVisibility(); + emptyContentContainer.setVisibility(View.GONE); + mRecyclerView.setVisibility(View.VISIBLE); + } } /** @@ -535,9 +531,6 @@ private void showList() { int hiddenFoldersCount = mAdapter.getHiddenFolderCount(); showEmptyContent(getString(R.string.drawer_synced_folders), - getResources().getQuantityString(R.plurals.synced_folders_hidden_folders_count, - hiddenFoldersCount, - hiddenFoldersCount), getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders, hiddenFoldersCount, hiddenFoldersCount)); @@ -641,13 +634,6 @@ public void onVisibilityToggleClick(int section, SyncedFolderDisplayItem syncedF private void showEmptyContent(String headline, String message) { showEmptyContent(headline, message, false); - emptyContentActionButton.setVisibility(View.GONE); - } - - private void showEmptyContent(String headline, String message, String action) { - showEmptyContent(headline, message, false); - emptyContentActionButton.setText(action); - emptyContentActionButton.setVisibility(View.VISIBLE); } private void showLoadingContent() { @@ -656,7 +642,6 @@ private void showLoadingContent() { getString(R.string.synced_folders_loading_folders), true ); - emptyContentActionButton.setVisibility(View.GONE); } private void showEmptyContent(String headline, String message, boolean loading) { diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index f595d76a953e..c0eba3eda368 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -289,17 +289,11 @@ public void onBindHeaderViewHolder(SectionedViewHolder commonHolder, final int s private void onOverflowIconClicked(int section, SyncedFolderDisplayItem item, View view) { PopupMenu popup = new PopupMenu(context, view); popup.inflate(R.menu.synced_folders_adapter); - popup.setOnMenuItemClickListener(i -> optionsItemSelected(i,section,item)); + popup.setOnMenuItemClickListener(i -> optionsItemSelected(i, section, item)); + popup.getMenu() + .findItem(R.id.action_auto_upload_folder_toggle_visibility) + .setChecked(item.isHidden()); - if (item.isHidden()) { - popup.getMenu() - .findItem(R.id.action_auto_upload_folder_toggle_visibility) - .setTitle(R.string.autoupload_show_folder); - } else { - popup.getMenu() - .findItem(R.id.action_auto_upload_folder_toggle_visibility) - .setTitle(R.string.autoupload_hide_folder); - } popup.show(); } diff --git a/src/main/res/layout/empty_list.xml b/src/main/res/layout/empty_list.xml index 4dfc592e5fac..f4ea188252e9 100644 --- a/src/main/res/layout/empty_list.xml +++ b/src/main/res/layout/empty_list.xml @@ -19,7 +19,6 @@ License along with this program. If not, see . --> - - diff --git a/src/main/res/menu/synced_folders_adapter.xml b/src/main/res/menu/synced_folders_adapter.xml index 9e6a6d08ad56..bab5c7cea05c 100644 --- a/src/main/res/menu/synced_folders_adapter.xml +++ b/src/main/res/menu/synced_folders_adapter.xml @@ -20,7 +20,8 @@ + android:title="@string/autoupload_hide_folder" + android:checkable="true"/> Show %1$d hidden folder Show %1$d hidden folders - - %1$d hidden folder - %1$d hidden folders - Preferences for auto uploading Instant uploading has been revamped completely. Re-configure your auto upload from within the main menu.\n\nEnjoy the new and extended auto uploading. For %1$s @@ -635,7 +631,6 @@ Set up a custom folder Create new custom folder setup - Show folder Hide folder Configure Configure folders From d17c8534936516a26f09e34cb484a292e660fc99 Mon Sep 17 00:00:00 2001 From: Andy Scherzinger Date: Wed, 27 Nov 2019 21:33:38 +0100 Subject: [PATCH 62/69] re-add button/action and improve upon code review Signed-off-by: Andy Scherzinger --- .../android/datamodel/SyncedFolder.java | 29 +++- .../datamodel/SyncedFolderDisplayItem.java | 34 +++-- .../providers/FileContentProvider.java | 6 - .../ui/activity/SyncedFoldersActivity.java | 129 +++++++++++------- .../ui/adapter/SyncedFolderAdapter.java | 13 +- src/main/res/layout/empty_list.xml | 11 ++ 6 files changed, 142 insertions(+), 80 deletions(-) diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index fa4d5823c38e..20a72eed10b0 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -62,9 +62,17 @@ public class SyncedFolder implements Serializable, Cloneable { * @param type the type of the folder * @param hidden hide item flag */ - public SyncedFolder(String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, - boolean subfolderByDate, String account, int uploadAction, boolean enabled, - long timestampMs, MediaFolderType type, boolean hidden) { + public SyncedFolder(String localPath, + String remotePath, + boolean wifiOnly, + boolean chargingOnly, + boolean subfolderByDate, + String account, + int uploadAction, + boolean enabled, + long timestampMs, + MediaFolderType type, + boolean hidden) { this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, timestampMs, type, hidden); } @@ -74,9 +82,18 @@ public SyncedFolder(String localPath, String remotePath, boolean wifiOnly, boole * * @param id id */ - protected SyncedFolder(long id, String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, - boolean subfolderByDate, String account, int uploadAction, boolean enabled, - long timestampMs, MediaFolderType type, boolean hidden) { + protected SyncedFolder(long id, + String localPath, + String remotePath, + boolean wifiOnly, + boolean chargingOnly, + boolean subfolderByDate, + String account, + int uploadAction, + boolean enabled, + long timestampMs, + MediaFolderType type, + boolean hidden) { this.id = id; this.localPath = localPath; this.remotePath = remotePath; diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 5b51b37e0154..09521e3bef4a 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -55,11 +55,21 @@ public class SyncedFolderDisplayItem extends SyncedFolder { * @param type the type of the folder * @param hidden hide item flag */ - public SyncedFolderDisplayItem(long id, String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, - boolean subfolderByDate, String account, int uploadAction, boolean enabled, - long timestampMs, List filePaths, String folderName, long numberOfFiles, - MediaFolderType type, boolean hidden) - { + public SyncedFolderDisplayItem(long id, + String localPath, + String remotePath, + boolean wifiOnly, + boolean chargingOnly, + boolean subfolderByDate, + String account, + int uploadAction, + boolean enabled, + long timestampMs, + List filePaths, + String folderName, + long numberOfFiles, + MediaFolderType type, + boolean hidden) { super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, timestampMs, type, hidden); this.filePaths = filePaths; @@ -67,9 +77,17 @@ public SyncedFolderDisplayItem(long id, String localPath, String remotePath, boo this.numberOfFiles = numberOfFiles; } - public SyncedFolderDisplayItem(long id, String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, - boolean subfolderByDate, String account, int uploadAction, boolean enabled, - long timestampMs, String folderName, MediaFolderType type, boolean hidden) { + public SyncedFolderDisplayItem(long id, + String localPath, + String remotePath, + boolean wifiOnly, + boolean chargingOnly, + boolean subfolderByDate, + String account, + int uploadAction, + boolean enabled, + long timestampMs, + String folderName, MediaFolderType type, boolean hidden) { super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, timestampMs, type, hidden); this.folderName = folderName; diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index d1bfda3b10fe..e66e5deb4428 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -2076,12 +2076,6 @@ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL(ALTER_TABLE + ProviderTableMeta.CAPABILITIES_TABLE_NAME + REMOVE_COLUMN + ProviderTableMeta.CAPABILITIES_END_TO_END_ENCRYPTION); } - - if(oldVersion == 50 && newVersion < 50) { - db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + - REMOVE_COLUMN + ProviderTableMeta.SYNCED_FOLDER_HIDDEN); - - } } } } diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index a0d4bb9c4c00..6eb7b53f5ac6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -21,7 +21,6 @@ package com.owncloud.android.ui.activity; -import android.accounts.Account; import android.annotation.SuppressLint; import android.app.Activity; import android.app.NotificationManager; @@ -43,6 +42,7 @@ import android.widget.ProgressBar; import android.widget.TextView; +import com.google.android.material.button.MaterialButton; import com.nextcloud.client.account.User; import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; @@ -124,12 +124,15 @@ public class SyncedFoldersActivity extends FileActivity implements SyncedFolderA @BindView(R.id.empty_list_view_text) public TextView emptyContentMessage; + @BindView(R.id.empty_list_view_action) + public MaterialButton emptyContentActionButton; + @BindView(android.R.id.list) public RecyclerView mRecyclerView; - private SyncedFolderAdapter mAdapter; - private SyncedFolderProvider mSyncedFolderProvider; - private SyncedFolderPreferencesDialogFragment mSyncedFolderPreferencesDialogFragment; + private SyncedFolderAdapter adapter; + private SyncedFolderProvider syncedFolderProvider; + private SyncedFolderPreferencesDialogFragment syncedFolderPreferencesDialogFragment; private boolean showSidebar = true; private String path; @@ -243,24 +246,26 @@ private void showPowerCheckDialog() { private void setupContent() { final int gridWidth = getResources().getInteger(R.integer.media_grid_width); boolean lightVersion = getResources().getBoolean(R.bool.syncedFolder_light); - mAdapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion); - mSyncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock); + adapter = new SyncedFolderAdapter(this, clock, gridWidth, this, lightVersion); + syncedFolderProvider = new SyncedFolderProvider(getContentResolver(), preferences, clock); emptyContentIcon.setImageResource(R.drawable.nav_synced_folders); + emptyContentActionButton.setBackgroundColor(ThemeUtils.primaryColor(this)); + emptyContentActionButton.setTextColor(ThemeUtils.fontColor(this)); final GridLayoutManager lm = new GridLayoutManager(this, gridWidth); - mAdapter.setLayoutManager(lm); + adapter.setLayoutManager(lm); int spacing = getResources().getDimensionPixelSize(R.dimen.media_grid_spacing); mRecyclerView.addItemDecoration(new MediaGridItemDecoration(spacing)); mRecyclerView.setLayoutManager(lm); - mRecyclerView.setAdapter(mAdapter); + mRecyclerView.setAdapter(adapter); load(gridWidth * 2, false); } - @OnClick(R.id.empty_list_view_text) + @OnClick(R.id.empty_list_view_action) public void showHiddenItems() { - if (mAdapter.getSectionCount() == 0 && mAdapter.getUnfilteredSectionCount() > mAdapter.getSectionCount()) { - mAdapter.toggleHiddenItemsVisibility(); + if (adapter.getSectionCount() == 0 && adapter.getUnfilteredSectionCount() > adapter.getSectionCount()) { + adapter.toggleHiddenItemsVisibility(); emptyContentContainer.setVisibility(View.GONE); mRecyclerView.setVisibility(View.VISIBLE); } @@ -272,7 +277,7 @@ public void showHiddenItems() { * @param perFolderMediaItemLimit the amount of media items to be loaded/shown per media folder */ private void load(final int perFolderMediaItemLimit, boolean force) { - if (mAdapter.getItemCount() > 0 && !force) { + if (adapter.getItemCount() > 0 && !force) { return; } showLoadingContent(); @@ -281,14 +286,14 @@ private void load(final int perFolderMediaItemLimit, boolean force) { mediaFolders.addAll(MediaProvider.getVideoFolders(getContentResolver(), perFolderMediaItemLimit, this, false)); - List syncedFolderArrayList = mSyncedFolderProvider.getSyncedFolders(); + List syncedFolderArrayList = syncedFolderProvider.getSyncedFolders(); List currentAccountSyncedFoldersList = new ArrayList<>(); User user = getUserAccountManager().getUser(); for (SyncedFolder syncedFolder : syncedFolderArrayList) { if (syncedFolder.getAccount().equals(user.getAccountName())) { // delete non-existing & disabled synced folders if (!new File(syncedFolder.getLocalPath()).exists() && !syncedFolder.isEnabled()) { - mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); + syncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); } else { currentAccountSyncedFoldersList.add(syncedFolder); } @@ -298,14 +303,14 @@ private void load(final int perFolderMediaItemLimit, boolean force) { List syncFolderItems = sortSyncedFolderItems( mergeFolderData(currentAccountSyncedFoldersList, mediaFolders)); - mAdapter.setSyncFolderItems(syncFolderItems); - mAdapter.notifyDataSetChanged(); + adapter.setSyncFolderItems(syncFolderItems); + adapter.notifyDataSetChanged(); showList(); if (!TextUtils.isEmpty(path)) { - int section = mAdapter.getSectionByLocalPathAndType(path, type); + int section = adapter.getSectionByLocalPathAndType(path, type); if (section >= 0) { - onSyncFolderSettingsClick(section, mAdapter.get(section)); + onSyncFolderSettingsClick(section, adapter.get(section)); } } } @@ -523,24 +528,31 @@ private Map createSyncedFoldersMap(List sync */ private void showList() { if (mRecyclerView != null) { - mRecyclerView.setVisibility(View.VISIBLE ); + mRecyclerView.setVisibility(View.VISIBLE); emptyContentProgressBar.setVisibility(View.GONE); - if (mAdapter.getSectionCount() == 0 && mAdapter.getUnfilteredSectionCount() > mAdapter.getSectionCount()) { - emptyContentContainer.setVisibility(View.VISIBLE); - int hiddenFoldersCount = mAdapter.getHiddenFolderCount(); - - showEmptyContent(getString(R.string.drawer_synced_folders), - getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders, - hiddenFoldersCount, - hiddenFoldersCount)); - } else if (mAdapter.getSectionCount() == 0 && mAdapter.getUnfilteredSectionCount() == 0) { - emptyContentContainer.setVisibility(View.VISIBLE); - showEmptyContent(getString(R.string.drawer_synced_folders), - getString(R.string.synced_folders_no_results)); - } else { - emptyContentContainer.setVisibility(View.GONE); - } + checkAndShowEmptyListContent(); + } + } + + private void checkAndShowEmptyListContent() { + if (adapter.getSectionCount() == 0 && adapter.getUnfilteredSectionCount() > adapter.getSectionCount()) { + emptyContentContainer.setVisibility(View.VISIBLE); + int hiddenFoldersCount = adapter.getHiddenFolderCount(); + + showEmptyContent(getString(R.string.drawer_synced_folders), + getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders, + hiddenFoldersCount, + hiddenFoldersCount), + getResources().getQuantityString(R.plurals.synced_folders_show_hidden_folders, + hiddenFoldersCount, + hiddenFoldersCount)); + } else if (adapter.getSectionCount() == 0 && adapter.getUnfilteredSectionCount() == 0) { + emptyContentContainer.setVisibility(View.VISIBLE); + showEmptyContent(getString(R.string.drawer_synced_folders), + getString(R.string.synced_folders_no_results)); + } else { + emptyContentContainer.setVisibility(View.GONE); } } @@ -597,10 +609,10 @@ public void showFiles(boolean onDeviceOnly) { @Override public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedFolderDisplayItem) { if (syncedFolderDisplayItem.getId() > UNPERSISTED_ID) { - mSyncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(), - syncedFolderDisplayItem.isEnabled()); + syncedFolderProvider.updateSyncedFolderEnabled(syncedFolderDisplayItem.getId(), + syncedFolderDisplayItem.isEnabled()); } else { - long storedId = mSyncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem); + long storedId = syncedFolderProvider.storeSyncedFolder(syncedFolderDisplayItem); if (storedId != -1) { syncedFolderDisplayItem.setId(storedId); } @@ -619,9 +631,9 @@ public void onSyncFolderSettingsClick(int section, SyncedFolderDisplayItem synce FragmentTransaction ft = fm.beginTransaction(); ft.addToBackStack(null); - mSyncedFolderPreferencesDialogFragment = SyncedFolderPreferencesDialogFragment.newInstance( + syncedFolderPreferencesDialogFragment = SyncedFolderPreferencesDialogFragment.newInstance( syncedFolderDisplayItem, section); - mSyncedFolderPreferencesDialogFragment.show(ft, SYNCED_FOLDER_PREFERENCES_DIALOG_TAG); + syncedFolderPreferencesDialogFragment.show(ft, SYNCED_FOLDER_PREFERENCES_DIALOG_TAG); } @Override @@ -629,11 +641,21 @@ public void onVisibilityToggleClick(int section, SyncedFolderDisplayItem syncedF syncedFolder.setHidden(!syncedFolder.isHidden()); saveOrUpdateSyncedFolder(syncedFolder); - mAdapter.setSyncFolderItem(section, syncedFolder); + adapter.setSyncFolderItem(section, syncedFolder); + + checkAndShowEmptyListContent(); } private void showEmptyContent(String headline, String message) { showEmptyContent(headline, message, false); + emptyContentActionButton.setVisibility(View.GONE); + } + + private void showEmptyContent(String headline, String message, String action) { + showEmptyContent(headline, message, false); + emptyContentActionButton.setText(action); + emptyContentActionButton.setVisibility(View.VISIBLE); + emptyContentMessage.setVisibility(View.GONE); } private void showLoadingContent() { @@ -642,6 +664,7 @@ private void showLoadingContent() { getString(R.string.synced_folders_loading_folders), true ); + emptyContentActionButton.setVisibility(View.GONE); } private void showEmptyContent(String headline, String message, boolean loading) { @@ -666,14 +689,14 @@ private void showEmptyContent(String headline, String message, boolean loading) @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_REMOTE_FOLDER - && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) { + && resultCode == RESULT_OK && syncedFolderPreferencesDialogFragment != null) { OCFile chosenFolder = data.getParcelableExtra(FolderPickerActivity.EXTRA_FOLDER); - mSyncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath()); + syncedFolderPreferencesDialogFragment.setRemoteFolderSummary(chosenFolder.getRemotePath()); } if (requestCode == SyncedFolderPreferencesDialogFragment.REQUEST_CODE__SELECT_LOCAL_FOLDER - && resultCode == RESULT_OK && mSyncedFolderPreferencesDialogFragment != null) { + && resultCode == RESULT_OK && syncedFolderPreferencesDialogFragment != null) { String localPath = data.getStringExtra(UploadFilesActivity.EXTRA_CHOSEN_FILES); - mSyncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath); + syncedFolderPreferencesDialogFragment.setLocalFolderSummary(localPath); } else { super.onActivityResult(requestCode, resultCode, data); } @@ -691,9 +714,9 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden()); saveOrUpdateSyncedFolder(newCustomFolder); - mAdapter.addSyncFolderItem(newCustomFolder); + adapter.addSyncFolderItem(newCustomFolder); } else { - SyncedFolderDisplayItem item = mAdapter.get(syncedFolder.getSection()); + SyncedFolderDisplayItem item = adapter.get(syncedFolder.getSection()); updateSyncedFolderItem(item, syncedFolder.getId(), syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), syncedFolder .isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), syncedFolder @@ -702,10 +725,10 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { saveOrUpdateSyncedFolder(item); // TODO test if notifiyItemChanged is suffiecient (should improve performance) - mAdapter.notifyDataSetChanged(); + adapter.notifyDataSetChanged(); } - mSyncedFolderPreferencesDialogFragment = null; + syncedFolderPreferencesDialogFragment = null; if (syncedFolder.isEnabled()) { showBatteryOptimizationInfo(); @@ -718,7 +741,7 @@ private void saveOrUpdateSyncedFolder(SyncedFolderDisplayItem item) { storeSyncedFolder(item); } else { // existing synced folder setup to be updated - mSyncedFolderProvider.updateSyncFolder(item); + syncedFolderProvider.updateSyncFolder(item); if (item.isEnabled()) { FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); } else { @@ -734,7 +757,7 @@ private void saveOrUpdateSyncedFolder(SyncedFolderDisplayItem item) { private void storeSyncedFolder(SyncedFolderDisplayItem item) { ArbitraryDataProvider arbitraryDataProvider = new ArbitraryDataProvider(MainApp.getAppContext(). getContentResolver()); - long storedId = mSyncedFolderProvider.storeSyncedFolder(item); + long storedId = syncedFolderProvider.storeSyncedFolder(item); if (storedId != -1) { item.setId(storedId); if (item.isEnabled()) { @@ -748,13 +771,13 @@ private void storeSyncedFolder(SyncedFolderDisplayItem item) { @Override public void onCancelSyncedFolderPreference() { - mSyncedFolderPreferencesDialogFragment = null; + syncedFolderPreferencesDialogFragment = null; } @Override public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { - mSyncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); - mAdapter.removeItem(syncedFolder.getSection()); + syncedFolderProvider.deleteSyncedFolder(syncedFolder.getId()); + adapter.removeItem(syncedFolder.getSection()); } /** diff --git a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java index c0eba3eda368..57c72e554905 100644 --- a/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/SyncedFolderAdapter.java @@ -83,7 +83,7 @@ public SyncedFolderAdapter(Context context, Clock clock, int gridWidth, ClickLis shouldShowFooters(true); } - private void toggleHiddenItemsVisibility() { + public void toggleHiddenItemsVisibility() { hideItems = !hideItems; filteredSyncFolderItems.clear(); filteredSyncFolderItems.addAll(filterHiddenItems(syncFolderItems, hideItems)); @@ -99,7 +99,6 @@ public void setSyncFolderItems(List syncFolderItems) { } public void setSyncFolderItem(int location, SyncedFolderDisplayItem syncFolderItem) { - // show all items OR if (hideItems && syncFolderItem.isHidden() && filteredSyncFolderItems.contains(syncFolderItem)) { filteredSyncFolderItems.remove(location); } else { @@ -123,7 +122,7 @@ public void addSyncFolderItem(SyncedFolderDisplayItem syncFolderItem) { syncFolderItems.add(syncFolderItem); // add item for display when either all items should be shown (!hideItems) - // or if items should be hi + // or if item should be shown (!.isHidden()) if (!hideItems || !syncFolderItem.isHidden()) { filteredSyncFolderItems.add(syncFolderItem); notifyDataSetChanged(); @@ -148,15 +147,15 @@ private List filterHiddenItems(List ret = new ArrayList<>(); + List result = new ArrayList<>(); for (SyncedFolderDisplayItem item : items) { - if (!item.isHidden() && !ret.contains(item)) { - ret.add(item); + if (!item.isHidden() && !result.contains(item)) { + result.add(item); } } - return ret; + return result; } } diff --git a/src/main/res/layout/empty_list.xml b/src/main/res/layout/empty_list.xml index f4ea188252e9..2cfc7790e98f 100644 --- a/src/main/res/layout/empty_list.xml +++ b/src/main/res/layout/empty_list.xml @@ -19,6 +19,7 @@ License along with this program. If not, see . --> + + From 1f4e85768198dddd5611daad884d137537914e4c Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot Date: Wed, 27 Nov 2019 20:55:41 +0000 Subject: [PATCH 63/69] Drone: update FindBugs results to reflect reduced error/warning count [skip ci] Signed-off-by: nextcloud-android-bot --- scripts/analysis/findbugs-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 8e9088e93bac..009bd2c17f2d 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -411 \ No newline at end of file +407 \ No newline at end of file From 5257fb236c5fb61c79be5eee8996c06379bf75be Mon Sep 17 00:00:00 2001 From: Nextcloud bot Date: Thu, 28 Nov 2019 03:14:35 +0000 Subject: [PATCH 64/69] [tx-robot] updated from transifex --- src/main/res/values-cs-rCZ/strings.xml | 48 +++++++++++++------------- src/main/res/values-sv/strings.xml | 3 ++ 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/src/main/res/values-cs-rCZ/strings.xml b/src/main/res/values-cs-rCZ/strings.xml index 0a0e70eef3f0..94f835e5376c 100644 --- a/src/main/res/values-cs-rCZ/strings.xml +++ b/src/main/res/values-cs-rCZ/strings.xml @@ -25,7 +25,7 @@ Odeslat Nastavení Seřadit - Obnovit účet + Znovu načíst účet Aktivní uživatel Zatím žádná aktivita Zatím žádné události jako přidání, změny či sdílení @@ -50,7 +50,7 @@ Nesprávný formát adresy serveru Server nenalezen Žádné síťové spojení - Zabezpečené spojení není k dispozici + Zabezpečené spojení není k dispozici. Nesprávně formulované nastavení serveru Neúspěšné ověření se Přístup zamítnut pověřovacím (autorizace) serverem @@ -77,7 +77,7 @@ Nahrávat pouze přes neplacené Wi-Fi připojení /AutoUpload Vytvořte nové nastavení vlastní složky - Vytvořte vlastní složku + Nastavit uživatelsky určenou složku Zakázat kontrolu úspory energie Avatar Zavřít @@ -119,7 +119,7 @@ Zahrnuje všechny nadcházející funkce a pohybuje se na hraně stability. Mohou se vyskytnout chyby, a pokud ano, nahlaste nám je prosím. diskuzní fórum Pomozte ostatním na - Kontrolujte, vylepšujte a programujte – více informací naleznete na %1$s + Kontrolujte, vylepšujte a programujte – podrobnosti naleznete na %1$s Aktivně něčím přispět Připojte se k IRC chatu: aplikace @@ -154,8 +154,8 @@ Nejnovější záloha nikdy Zvolte datum - Zálohování je naplánováno a brzy začne - Import je naplánován a brzy začne + Zálohování naplánováno a brzy začne + Import naplánován a brzy začne Nenalezen žádný soubor Nepodařilo se najít vaši poslední zálohu! Je třeba oprávnění ke čtení seznamu kontaktů @@ -179,12 +179,12 @@ Vytvořit nový sešit Přihlašovací údaje znepřístupněny Neznámé - Neplatné pověření + Neplatné přihlašovací údaje Odebrat účet Odebrat účet %s a smazat všechny místní soubory?\n\nTuto akci nelze vzít zpět. Smazat položky Odznačit vše - Je k dispozici novější verze + Je k dispozici nová verze Nejsou k dispozici žádné informace. Není k dispozici žádná novější verze. Tento algoritmus vytváření otisku (digest) není na vašem telefonu dostupný. @@ -255,7 +255,7 @@ Chyba při získávání šablon Chyba při spouštění kamery Předvolby - Vývojářský testovací reži + Vývojářský testovací režim Přidat nebo nahrát Předání souboru správci stahování se nezdařilo Soubor se nepodařilo vytisknout @@ -271,7 +271,7 @@ Ponechat Nahrajte nějaký obsah, nebo synchronizujte s vašimi zařízeními. Zatím žádné oblíbené položky - Vaše vyhledávání nenalezlo žádné oblíbené soubory + Vaše vyhledávání nenalezlo žádné oblíbené soubory. Zde budou zobrazeny soubory a složky, které označíte jako oblíbené Nejsou zde žádné soubory V této složce nebylo nic nalezeno @@ -300,8 +300,8 @@ Datová složka už existuje. Vyberte z následujících možností: Nextcloud složka už existuje Je zapotřebí více místa - Nedaří se číst ze zdrojového souboru - Nedaří se zapsat do cílového souboru + Ze zdrojového souboru se nedaří číst + Do cílového souboru se nedaří zapsat Nezdar během migrace Nepodařilo se aktualizovat rejstřík Přesouvání dat… @@ -347,7 +347,7 @@ pro přejmenování tohoto souboru Stahování souborů… Nahrávání souborů… - Některé soubory nebylo možno přesunout + Některé soubory nebylo možné přesunout Místní: %1$s Přesunout vše Vzdálené: %1$s @@ -405,7 +405,7 @@ Soubor se nedaří přesunout – zkontrolujte zda existuje Přesunout do… Při čekání na server došlo k chybě. Operace nemohla být dokončena - Při připojení k serveru došlo k chybě + Při připojování k serveru došlo k chybě Při čekání na server došlo k chybě. Operace nemohla být dokončena Operace nemohla být dokončena, protože server není dostupný Nový komentář… @@ -460,7 +460,7 @@ 12:23:45 Toto je zástupný text 2012/05/18 12:23 odp. - Zákaz kontroly úspory energie může způsobit nahrávání souborů i v případě nízkého stavu baterie! + Zákaz kontroly úspory energie může způsobit nahrávání souborů i v případě nízkého stavu nabití akumulátoru! smazáno ponecháno v původní složce přesunuto do složky aplikace @@ -468,7 +468,7 @@ Synchronizovat kalendář a kontakty Adresu serveru pro účet se pro DAVx5 (dříve známé pod názvem DAVdroid) nepodařilo přeložit F-Droid ani Google Play není nainstalováno - Nastavit pro stávající účet DAVx5 (dříve známé pod názvem DAVdroid) (v1.3.0+) + Nastavit pro stávající účet DAVx5 (dříve známé pod názvem DAVdroid) (verze 1.3.0 a novější) Synchronizace kalendáře & kontaktů úspěšně dokončena O aplikaci Podrobnosti @@ -498,7 +498,7 @@ Spravovat účty Doporučit přátelům Zobrazit skryté soubory - Získat zdrojový kód + Získat zdrojové kódy Popis umístění úložiště Místní složka Složka na protějšku @@ -506,7 +506,7 @@ Tmavý Světlý Náhled obrázku - K náhledu není žádný místní soubor + Není zde žádný místní soubor pro který zobrazit náhled Obrázek se nedaří zobrazit Je nám líto Soukromí @@ -562,7 +562,7 @@ %1$s (e-mail) Platnost skončí %1$s Sdílet %1$s - Vytvořit odkaz + Získat odkaz %1$s (skupina) Sdílet vnitřní odkaz Bude fungovat pouze pro uživatele s přístupem do tohoto %1$s @@ -635,7 +635,7 @@ - Žádné informace o této chybě Certifikát se nedaří uložit Certifikát se nepodařilo zobrazit. - Přejete si přesto tomuto certifikátu důvěřovat? + Chcete i tak tomuto certifikátu důvěřovat? - Certifikátu serveru skončila platnost - Certifikát serveru není důvěryhodný - certifikát serveru ještě nezačal platit (datum začátku platnosti je v budoucnosti) @@ -745,7 +745,7 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Neznámá chyba Vybrat Nahrát - Obdržená data neobsahují žádný platný soubor. + Obdržená data neobsahují platný soubor. %1$s nemá oprávnění číst přijatý soubor Došlo k chybě při kopírování souboru do dočasné složky. Zkuste zopakovat odeslání. Soubor označený k nahrání nebyl nalezen. Zkontrolujte zda soubor existuje. @@ -771,8 +771,8 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Nenalezen žádný účet Aktuální Neúspěšný/čekající restart - Odesláno - Čekání na nahrávání + Nahráno + Čeká se na nahrání Nahrání Zrušeno Konflikt @@ -804,7 +804,7 @@ Jak vývojová tak produkční verze je k dispozici na F-droidu a mohou být nai Chvilku strpení… Ověřování uložených přihlašovacích údajů Kopírování souboru ze soukromého úložiště - Obrázek Co je nového + Obrázek „Co je nového“ Přeskočit Co je v %1$s nového diff --git a/src/main/res/values-sv/strings.xml b/src/main/res/values-sv/strings.xml index 175b4168c4d3..103827882ec6 100644 --- a/src/main/res/values-sv/strings.xml +++ b/src/main/res/values-sv/strings.xml @@ -538,11 +538,14 @@ Skydda din data Bläddra och dela alla åtgärder till hands + Aktivitet, delningar, … allt snabbt tillgängligt Alla dina konton på ett ställe Automatisk uppladdning för dina bilder & videor + Kalender & kontakter + Synka med DAVx5 Sök användare och grupper Välj alla Välj mall From d2622069e3a466291c06a66a1e0e1c989c048126 Mon Sep 17 00:00:00 2001 From: Chris Koying Browet Date: Fri, 10 Aug 2018 13:51:19 +0200 Subject: [PATCH 65/69] ADD: [instantupload] setting to also upload existing files Signed-off-by: Chris Koying Browet --- .../java/com/owncloud/android/MainApp.java | 2 +- .../android/datamodel/SyncedFolder.java | 9 +++- .../datamodel/SyncedFolderDisplayItem.java | 11 ++-- .../datamodel/SyncedFolderProvider.java | 8 ++- .../com/owncloud/android/db/ProviderMeta.java | 3 +- .../owncloud/android/jobs/FilesSyncJob.java | 2 +- .../providers/FileContentProvider.java | 19 +++++++ .../ui/activity/SyncedFoldersActivity.java | 28 +++++++---- ...SyncedFolderPreferencesDialogFragment.java | 17 +++++++ .../dialog/parcel/SyncedFolderParcelable.java | 4 ++ .../android/utils/FilesSyncHelper.java | 23 ++++++--- .../layout/synced_folders_settings_layout.xml | 50 +++++++++++++++++++ src/main/res/values/strings.xml | 1 + .../activity/SyncedFoldersActivityTest.java | 1 + 14 files changed, 150 insertions(+), 28 deletions(-) diff --git a/src/main/java/com/owncloud/android/MainApp.java b/src/main/java/com/owncloud/android/MainApp.java index 3b7f088efed5..f4b21718bc95 100644 --- a/src/main/java/com/owncloud/android/MainApp.java +++ b/src/main/java/com/owncloud/android/MainApp.java @@ -767,7 +767,7 @@ private static void initiateExistingAutoUploadEntries(Clock clock) { for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { if (syncedFolder.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder); + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolder, true); } } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java index 20a72eed10b0..33a42b867dd7 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolder.java @@ -39,6 +39,7 @@ public class SyncedFolder implements Serializable, Cloneable { @Getter @Setter private String remotePath; @Getter @Setter private boolean wifiOnly; @Getter @Setter private boolean chargingOnly; + @Getter @Setter private boolean existing; @Getter @Setter private boolean subfolderByDate; @Getter @Setter private String account; @Getter @Setter private int uploadAction; @@ -54,6 +55,7 @@ public class SyncedFolder implements Serializable, Cloneable { * @param remotePath remote path * @param wifiOnly upload on wifi only flag * @param chargingOnly upload on charging only + * @param existing upload existing files * @param subfolderByDate create sub-folders by date (month) * @param account the account owning the synced folder * @param uploadAction the action to be done after the upload @@ -66,6 +68,7 @@ public SyncedFolder(String localPath, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean existing, boolean subfolderByDate, String account, int uploadAction, @@ -73,8 +76,8 @@ public SyncedFolder(String localPath, long timestampMs, MediaFolderType type, boolean hidden) { - this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, - enabled, timestampMs, type, hidden); + this(UNPERSISTED_ID, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, + uploadAction, enabled, timestampMs, type, hidden); } /** @@ -87,6 +90,7 @@ protected SyncedFolder(long id, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean existing, boolean subfolderByDate, String account, int uploadAction, @@ -99,6 +103,7 @@ protected SyncedFolder(long id, this.remotePath = remotePath; this.wifiOnly = wifiOnly; this.chargingOnly = chargingOnly; + this.existing = existing; this.subfolderByDate = subfolderByDate; this.account = account; this.uploadAction = uploadAction; diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java index 09521e3bef4a..b886954e1c08 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderDisplayItem.java @@ -45,6 +45,7 @@ public class SyncedFolderDisplayItem extends SyncedFolder { * @param remotePath remote path * @param wifiOnly upload on wifi only flag * @param chargingOnly upload on charging only + * @param existing also upload existing * @param subfolderByDate create sub-folders by date (month) * @param account the account owning the synced folder * @param uploadAction the action to be done after the upload @@ -60,6 +61,7 @@ public SyncedFolderDisplayItem(long id, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean existing, boolean subfolderByDate, String account, int uploadAction, @@ -70,8 +72,8 @@ public SyncedFolderDisplayItem(long id, long numberOfFiles, MediaFolderType type, boolean hidden) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, - timestampMs, type, hidden); + super(id, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, uploadAction, + enabled, timestampMs, type, hidden); this.filePaths = filePaths; this.folderName = folderName; this.numberOfFiles = numberOfFiles; @@ -82,14 +84,15 @@ public SyncedFolderDisplayItem(long id, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean existing, boolean subfolderByDate, String account, int uploadAction, boolean enabled, long timestampMs, String folderName, MediaFolderType type, boolean hidden) { - super(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, account, uploadAction, enabled, - timestampMs, type, hidden); + super(id, localPath, remotePath, wifiOnly, chargingOnly, existing, subfolderByDate, account, uploadAction, + enabled, timestampMs, type, hidden); this.folderName = folderName; } } diff --git a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java index f5fc3009d777..118c60e44a31 100644 --- a/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java +++ b/src/main/java/com/owncloud/android/datamodel/SyncedFolderProvider.java @@ -342,6 +342,8 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY)) == 1; boolean chargingOnly = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY)) == 1; + boolean existing = cursor.getInt(cursor.getColumnIndex( + ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXISTING)) == 1; boolean subfolderByDate = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE)) == 1; String accountName = cursor.getString(cursor.getColumnIndex( @@ -357,8 +359,9 @@ private SyncedFolder createSyncedFolderFromCursor(Cursor cursor) { boolean hidden = cursor.getInt(cursor.getColumnIndex( ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_HIDDEN)) == 1; - syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, subfolderByDate, - accountName, uploadAction, enabled, enabledTimestampMs, type, hidden); + syncedFolder = new SyncedFolder(id, localPath, remotePath, wifiOnly, chargingOnly, existing, + subfolderByDate, accountName, uploadAction, enabled, enabledTimestampMs, + type, hidden); } return syncedFolder; } @@ -376,6 +379,7 @@ private ContentValues createContentValuesFromSyncedFolder(SyncedFolder syncedFol cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH, syncedFolder.getRemotePath()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY, syncedFolder.isWifiOnly()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY, syncedFolder.isChargingOnly()); + cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_EXISTING, syncedFolder.isExisting()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED, syncedFolder.isEnabled()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS, syncedFolder.getEnabledTimestampMs()); cv.put(ProviderMeta.ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE, syncedFolder.isSubfolderByDate()); diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index a9dad01cd914..ffe0064222fd 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -31,7 +31,7 @@ */ public class ProviderMeta { public static final String DB_NAME = "filelist"; - public static final int DB_VERSION = 51; + public static final int DB_VERSION = 52; private ProviderMeta() { // No instance @@ -219,6 +219,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String SYNCED_FOLDER_REMOTE_PATH = "remote_path"; public static final String SYNCED_FOLDER_WIFI_ONLY = "wifi_only"; public static final String SYNCED_FOLDER_CHARGING_ONLY = "charging_only"; + public static final String SYNCED_FOLDER_EXISTING = "existing"; public static final String SYNCED_FOLDER_ENABLED = "enabled"; public static final String SYNCED_FOLDER_ENABLED_TIMESTAMP_MS = "enabled_timestamp_ms"; public static final String SYNCED_FOLDER_TYPE = "type"; diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 32b3002d7f9c..41c65899eef9 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -130,7 +130,7 @@ protected Result onRunJob(@NonNull Params params) { userAccountManager, connectivityService, powerManagementService); - FilesSyncHelper.insertAllDBEntries(preferences, clock, skipCustom); + FilesSyncHelper.insertAllDBEntries(preferences, clock, skipCustom, false); // Create all the providers we'll need final ContentResolver contentResolver = context.getContentResolver(); diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index e66e5deb4428..9150eb459399 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -828,6 +828,7 @@ private void createSyncedFoldersTable(SQLiteDatabase db) { + ProviderTableMeta.SYNCED_FOLDER_REMOTE_PATH + " TEXT, " // remote path + ProviderTableMeta.SYNCED_FOLDER_WIFI_ONLY + " INTEGER, " // wifi_only + ProviderTableMeta.SYNCED_FOLDER_CHARGING_ONLY + " INTEGER, " // charging only + + ProviderTableMeta.SYNCED_FOLDER_EXISTING + " INTEGER, " // existing + ProviderTableMeta.SYNCED_FOLDER_ENABLED + " INTEGER, " // enabled + ProviderTableMeta.SYNCED_FOLDER_ENABLED_TIMESTAMP_MS + " INTEGER, " // enable date + ProviderTableMeta.SYNCED_FOLDER_SUBFOLDER_BY_DATE + " INTEGER, " // subfolder by date @@ -2064,6 +2065,24 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (!upgraded) { Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); } + + if(oldVersion < 52 && newVersion >= 52) { + Log_OC.i(SQL, "Entering in the #52 add synced.existing"); + db.beginTransaction(); + try { + db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_EXISTING + " INTEGER "); // boolean + + upgraded = true; + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + } + + if (!upgraded) { + Log_OC.i(SQL, String.format(Locale.ENGLISH, UPGRADE_VERSION_MSG, oldVersion, newVersion)); + } } @Override diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 6eb7b53f5ac6..3572f343a360 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -416,6 +416,7 @@ private SyncedFolderDisplayItem createSyncedFolderWithoutMediaFolder(@NonNull Sy syncedFolder.getRemotePath(), syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), + syncedFolder.isExisting(), syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), @@ -443,6 +444,7 @@ private SyncedFolderDisplayItem createSyncedFolder(@NonNull SyncedFolder syncedF syncedFolder.getRemotePath(), syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), + syncedFolder.isExisting(), syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), syncedFolder.getUploadAction(), @@ -470,6 +472,7 @@ private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull Media true, false, false, + false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, @@ -577,7 +580,7 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.action_create_custom_folder: { Log.d(TAG, "Show custom folder dialog"); SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( - SyncedFolder.UNPERSISTED_ID, null, null, true, false, + SyncedFolder.UNPERSISTED_ID, null, null, true, false, false, false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, clock.getCurrentTime(), null, MediaFolderType.CUSTOM, false); onSyncFolderSettingsClick(0, emptyCustomFolder); @@ -619,7 +622,7 @@ public void onSyncStatusToggleClick(int section, SyncedFolderDisplayItem syncedF } if (syncedFolderDisplayItem.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem); + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(syncedFolderDisplayItem, true); showBatteryOptimizationInfo(); } @@ -709,18 +712,20 @@ public void onSaveSyncedFolderPreference(SyncedFolderParcelable syncedFolder) { if (MediaFolderType.CUSTOM == syncedFolder.getType() && syncedFolder.getId() == UNPERSISTED_ID) { SyncedFolderDisplayItem newCustomFolder = new SyncedFolderDisplayItem( SyncedFolder.UNPERSISTED_ID, syncedFolder.getLocalPath(), syncedFolder.getRemotePath(), - syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), - syncedFolder.getAccount(), syncedFolder.getUploadAction(), syncedFolder.isEnabled(), - clock.getCurrentTime(), new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden()); + syncedFolder.isWifiOnly(), syncedFolder.isChargingOnly(), + syncedFolder.isExisting(), syncedFolder.isSubfolderByDate(), syncedFolder.getAccount(), + syncedFolder.getUploadAction(), syncedFolder.isEnabled(), clock.getCurrentTime(), + new File(syncedFolder.getLocalPath()).getName(), syncedFolder.getType(), syncedFolder.isHidden()); saveOrUpdateSyncedFolder(newCustomFolder); adapter.addSyncFolderItem(newCustomFolder); } else { SyncedFolderDisplayItem item = adapter.get(syncedFolder.getSection()); updateSyncedFolderItem(item, syncedFolder.getId(), syncedFolder.getLocalPath(), - syncedFolder.getRemotePath(), syncedFolder - .isWifiOnly(), syncedFolder.isChargingOnly(), syncedFolder.isSubfolderByDate(), syncedFolder - .getUploadAction(), syncedFolder.isEnabled()); + syncedFolder.getRemotePath(), syncedFolder.isWifiOnly(), + syncedFolder.isChargingOnly(), syncedFolder.isExisting(), + syncedFolder.isSubfolderByDate(), syncedFolder.getUploadAction(), + syncedFolder.isEnabled()); saveOrUpdateSyncedFolder(item); @@ -743,7 +748,7 @@ private void saveOrUpdateSyncedFolder(SyncedFolderDisplayItem item) { // existing synced folder setup to be updated syncedFolderProvider.updateSyncFolder(item); if (item.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item, true); } else { String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); @@ -761,7 +766,7 @@ private void storeSyncedFolder(SyncedFolderDisplayItem item) { if (storedId != -1) { item.setId(storedId); if (item.isEnabled()) { - FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item); + FilesSyncHelper.insertAllDBEntriesForSyncedFolder(item, true); } else { String syncedFolderInitiatedKey = "syncedFolderIntitiated_" + item.getId(); arbitraryDataProvider.deleteKeyForAccount("global", syncedFolderInitiatedKey); @@ -788,6 +793,7 @@ public void onDeleteSyncedFolderPreference(SyncedFolderParcelable syncedFolder) * @param remotePath the remote path * @param wifiOnly upload on wifi only * @param chargingOnly upload on charging only + * @param existing also upload existing * @param subfolderByDate created sub folders * @param uploadAction upload action * @param enabled is sync enabled @@ -798,6 +804,7 @@ private void updateSyncedFolderItem(SyncedFolderDisplayItem item, String remotePath, boolean wifiOnly, boolean chargingOnly, + boolean existing, boolean subfolderByDate, Integer uploadAction, boolean enabled) { @@ -806,6 +813,7 @@ private void updateSyncedFolderItem(SyncedFolderDisplayItem item, item.setRemotePath(remotePath); item.setWifiOnly(wifiOnly); item.setChargingOnly(chargingOnly); + item.setExisting(existing); item.setSubfolderByDate(subfolderByDate); item.setUploadAction(uploadAction); item.setEnabled(enabled, clock.getCurrentTime()); diff --git a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java index fbc07a310131..91ae78d593fe 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java +++ b/src/main/java/com/owncloud/android/ui/dialog/SyncedFolderPreferencesDialogFragment.java @@ -77,6 +77,7 @@ public class SyncedFolderPreferencesDialogFragment extends DialogFragment { private SwitchCompat mEnabledSwitch; private AppCompatCheckBox mUploadOnWifiCheckbox; private AppCompatCheckBox mUploadOnChargingCheckbox; + private AppCompatCheckBox mUploadExistingCheckbox; private AppCompatCheckBox mUploadUseSubfoldersCheckbox; private TextView mUploadBehaviorSummary; private TextView mLocalFolderPath; @@ -189,6 +190,9 @@ private void setupDialogElements(View view) { ThemeUtils.tintCheckbox(mUploadOnChargingCheckbox, accentColor); } + mUploadExistingCheckbox = view.findViewById(R.id.setting_instant_upload_existing_checkbox); + ThemeUtils.tintCheckbox(mUploadExistingCheckbox, accentColor); + mUploadUseSubfoldersCheckbox = view.findViewById( R.id.setting_instant_upload_path_use_subfolders_checkbox); ThemeUtils.tintCheckbox(mUploadUseSubfoldersCheckbox, accentColor); @@ -227,6 +231,7 @@ private void setupDialogElements(View view) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { mUploadOnChargingCheckbox.setChecked(mSyncedFolder.isChargingOnly()); } + mUploadExistingCheckbox.setChecked(mSyncedFolder.isExisting()); mUploadUseSubfoldersCheckbox.setChecked(mSyncedFolder.isSubfolderByDate()); mUploadBehaviorSummary.setText(mUploadBehaviorItemStrings[mSyncedFolder.getUploadActionInteger()]); @@ -318,6 +323,9 @@ private void setupViews(View view, boolean enable) { view.findViewById(R.id.setting_instant_upload_on_charging_container).setAlpha(alpha); } + view.findViewById(R.id.setting_instant_upload_existing_container).setEnabled(enable); + view.findViewById(R.id.setting_instant_upload_existing_container).setAlpha(alpha); + view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setEnabled(enable); view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setAlpha(alpha); @@ -361,6 +369,15 @@ public void onClick(View v) { }); } + view.findViewById(R.id.setting_instant_upload_existing_container).setOnClickListener( + new OnClickListener() { + @Override + public void onClick(View v) { + mSyncedFolder.setExisting(!mSyncedFolder.isExisting()); + mUploadExistingCheckbox.toggle(); + } + }); + view.findViewById(R.id.setting_instant_upload_path_use_subfolders_container).setOnClickListener( new OnClickListener() { @Override diff --git a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java index 2152eeb29e83..b59cf77625d3 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java +++ b/src/main/java/com/owncloud/android/ui/dialog/parcel/SyncedFolderParcelable.java @@ -41,6 +41,7 @@ public class SyncedFolderParcelable implements Parcelable { @Getter @Setter private String remotePath; @Getter @Setter private boolean wifiOnly = false; @Getter @Setter private boolean chargingOnly = false; + @Getter @Setter private boolean existing = true; @Getter @Setter private boolean enabled = false; @Getter @Setter private boolean subfolderByDate = false; @Getter private Integer uploadAction; @@ -57,6 +58,7 @@ public SyncedFolderParcelable(SyncedFolderDisplayItem syncedFolderDisplayItem, i remotePath = syncedFolderDisplayItem.getRemotePath(); wifiOnly = syncedFolderDisplayItem.isWifiOnly(); chargingOnly = syncedFolderDisplayItem.isChargingOnly(); + existing = syncedFolderDisplayItem.isExisting(); enabled = syncedFolderDisplayItem.isEnabled(); subfolderByDate = syncedFolderDisplayItem.isSubfolderByDate(); type = syncedFolderDisplayItem.getType(); @@ -73,6 +75,7 @@ private SyncedFolderParcelable(Parcel read) { remotePath = read.readString(); wifiOnly = read.readInt()!= 0; chargingOnly = read.readInt() != 0; + existing = read.readInt() != 0; enabled = read.readInt() != 0; subfolderByDate = read.readInt() != 0; type = MediaFolderType.getById(read.readInt()); @@ -90,6 +93,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(remotePath); dest.writeInt(wifiOnly ? 1 : 0); dest.writeInt(chargingOnly ? 1 : 0); + dest.writeInt(existing ? 1 : 0); dest.writeInt(enabled ? 1 : 0); dest.writeInt(subfolderByDate ? 1 : 0); dest.writeInt(type.getId()); diff --git a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index e2074b313923..432a9220d166 100644 --- a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -79,13 +79,13 @@ private FilesSyncHelper() { // utility class -> private constructor } - public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) { + public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder, boolean syncNow) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); final long enabledTimestampMs = syncedFolder.getEnabledTimestampMs(); - if (syncedFolder.isEnabled() && enabledTimestampMs >= 0) { + if (syncedFolder.isEnabled() && (syncedFolder.isExisting() || enabledTimestampMs >= 0)) { MediaFolderType mediaType = syncedFolder.getType(); if (mediaType == MediaFolderType.IMAGE) { FilesSyncHelper.insertContentIntoDB(MediaStore.Images.Media.INTERNAL_CONTENT_URI @@ -106,7 +106,7 @@ public static void insertAllDBEntriesForSyncedFolder(SyncedFolder syncedFolder) @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { File file = path.toFile(); - if (attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) { + if (syncedFolder.isExisting() || attrs.lastModifiedTime().toMillis() >= enabledTimestampMs) { filesystemDataProvider.storeOrUpdateFileValue(path.toAbsolutePath().toString(), attrs.lastModifiedTime().toMillis(), file.isDirectory(), syncedFolder); @@ -124,17 +124,26 @@ public FileVisitResult visitFileFailed(Path file, IOException exc) { Log_OC.e(TAG, "Something went wrong while indexing files for auto upload", e); } } + + if (syncNow) { + new JobRequest.Builder(FilesSyncJob.TAG) + .setExact(1_000L) + .setUpdateCurrent(false) + .build() + .schedule(); + } } } - public static void insertAllDBEntries(AppPreferences preferences, Clock clock, boolean skipCustom) { + public static void insertAllDBEntries(AppPreferences preferences, Clock clock, boolean skipCustom, + boolean syncNow) { final Context context = MainApp.getAppContext(); final ContentResolver contentResolver = context.getContentResolver(); SyncedFolderProvider syncedFolderProvider = new SyncedFolderProvider(contentResolver, preferences, clock); for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { - if (syncedFolder.isEnabled() && (MediaFolderType.CUSTOM != syncedFolder.getType() || !skipCustom)) { - insertAllDBEntriesForSyncedFolder(syncedFolder); + if (syncedFolder.isEnabled() && (!skipCustom || syncedFolder.getType() != MediaFolderType.CUSTOM)) { + insertAllDBEntriesForSyncedFolder(syncedFolder, syncNow); } } } @@ -171,7 +180,7 @@ private static void insertContentIntoDB(Uri uri, SyncedFolder syncedFolder) { while (cursor.moveToNext()) { contentPath = cursor.getString(column_index_data); isFolder = new File(contentPath).isDirectory(); - if (cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) { + if (syncedFolder.isExisting() || cursor.getLong(column_index_date_modified) >= enabledTimestampMs / 1000.0) { filesystemDataProvider.storeOrUpdateFileValue(contentPath, cursor.getLong(column_index_date_modified), isFolder, syncedFolder); diff --git a/src/main/res/layout/synced_folders_settings_layout.xml b/src/main/res/layout/synced_folders_settings_layout.xml index ec3f63993f23..bc6b1f90721e 100644 --- a/src/main/res/layout/synced_folders_settings_layout.xml +++ b/src/main/res/layout/synced_folders_settings_layout.xml @@ -317,6 +317,56 @@ + + + + + + + + + + + + + + + + Only upload on unmetered Wi-Fi Only upload when charging + Also upload existing files /InstantUpload /AutoUpload File conflict diff --git a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java index 50046bb01bb3..29bffa5c3ce5 100644 --- a/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java +++ b/src/test/java/com/owncloud/android/ui/activity/SyncedFoldersActivityTest.java @@ -164,6 +164,7 @@ private SyncedFolderDisplayItem create(String folderName, boolean enabled) { true, true, true, + true, "test@nextcloud.com", 1, enabled, From 03d8246f664a7a4f21e47b559b52b4339b9fa237 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Wed, 20 Nov 2019 13:29:21 +0100 Subject: [PATCH 66/69] Make newly created synced folders auto upload existing files by default Signed-off-by: Alice Gaudon --- .../owncloud/android/ui/activity/SyncedFoldersActivity.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java index 3572f343a360..62e3936fe7d6 100644 --- a/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/SyncedFoldersActivity.java @@ -471,7 +471,7 @@ private SyncedFolderDisplayItem createSyncedFolderFromMediaFolder(@NonNull Media getString(R.string.instant_upload_path) + "/" + mediaFolder.folderName, true, false, - false, + true, false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, @@ -580,7 +580,7 @@ public boolean onOptionsItemSelected(MenuItem item) { case R.id.action_create_custom_folder: { Log.d(TAG, "Show custom folder dialog"); SyncedFolderDisplayItem emptyCustomFolder = new SyncedFolderDisplayItem( - SyncedFolder.UNPERSISTED_ID, null, null, true, false, false, + SyncedFolder.UNPERSISTED_ID, null, null, true, false, true, false, getAccount().name, FileUploader.LOCAL_BEHAVIOUR_FORGET, false, clock.getCurrentTime(), null, MediaFolderType.CUSTOM, false); onSyncFolderSettingsClick(0, emptyCustomFolder); From ea801ebb5e04d3e72f9729619c4b17901f1eae99 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 25 Nov 2019 14:36:58 +0100 Subject: [PATCH 67/69] Make file uploads ask the user what to do when the file already exists on remote Signed-off-by: Alice Gaudon --- .../java/com/owncloud/android/UploadIT.java | 24 ++-- .../datamodel/UploadsStorageManager.java | 6 +- .../com/owncloud/android/db/OCUpload.java | 10 +- .../com/owncloud/android/db/ProviderMeta.java | 2 +- .../files/services/FileDownloader.java | 11 ++ .../android/files/services/FileUploader.java | 61 ++++++---- .../owncloud/android/jobs/FilesSyncJob.java | 24 ++-- .../operations/SynchronizeFileOperation.java | 3 +- .../operations/UploadFileOperation.java | 101 +++++++++------- .../providers/FileContentProvider.java | 48 +++++++- .../ui/activity/ConflictsResolveActivity.java | 107 +++++++++++------ .../ui/activity/UploadListActivity.java | 8 +- .../android/ui/adapter/UploadListAdapter.java | 112 ++++++++++++++---- .../ui/dialog/ConflictsResolveDialog.java | 68 ++++++----- src/main/res/values/strings.xml | 1 + 15 files changed, 396 insertions(+), 190 deletions(-) diff --git a/src/androidTest/java/com/owncloud/android/UploadIT.java b/src/androidTest/java/com/owncloud/android/UploadIT.java index 1d468e7162aa..90df24d6daa2 100644 --- a/src/androidTest/java/com/owncloud/android/UploadIT.java +++ b/src/androidTest/java/com/owncloud/android/UploadIT.java @@ -120,7 +120,7 @@ public RemoteOperationResult testUpload(OCUpload ocUpload) { account, null, ocUpload, - false, + FileUploader.NameCollisionPolicy.DEFAULT, FileUploader.LOCAL_BEHAVIOUR_COPY, targetContext, false, @@ -140,17 +140,17 @@ public void testUploadInNonExistingFolder() { OCUpload ocUpload = new OCUpload(FileStorageUtils.getSavePath(account.name) + "/empty.txt", "/testUpload/2/3/4/1.txt", account.name); UploadFileOperation newUpload = new UploadFileOperation( - storageManager, - connectivityServiceMock, - powerManagementServiceMock, - account, - null, - ocUpload, - false, - FileUploader.LOCAL_BEHAVIOUR_COPY, - targetContext, - false, - false + storageManager, + connectivityServiceMock, + powerManagementServiceMock, + account, + null, + ocUpload, + FileUploader.NameCollisionPolicy.DEFAULT, + FileUploader.LOCAL_BEHAVIOUR_COPY, + targetContext, + false, + false ); newUpload.addRenameUploadListener(() -> { // dummy diff --git a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java index b8130bcdc4cf..94cd542cfb4e 100644 --- a/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java +++ b/src/main/java/com/owncloud/android/datamodel/UploadsStorageManager.java @@ -84,7 +84,7 @@ public long storeUpload(OCUpload ocUpload) { cv.put(ProviderTableMeta.UPLOADS_FILE_SIZE, ocUpload.getFileSize()); cv.put(ProviderTableMeta.UPLOADS_STATUS, ocUpload.getUploadStatus().value); cv.put(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR, ocUpload.getLocalAction()); - cv.put(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE, ocUpload.isForceOverwrite() ? 1 : 0); + cv.put(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY, ocUpload.getNameCollisionPolicy().serialize()); cv.put(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER, ocUpload.isCreateRemoteFolder() ? 1 : 0); cv.put(ProviderTableMeta.UPLOADS_LAST_RESULT, ocUpload.getLastResult().getValue()); cv.put(ProviderTableMeta.UPLOADS_CREATED_BY, ocUpload.getCreatedBy()); @@ -329,8 +329,8 @@ private OCUpload createOCUploadFromCursor(Cursor c) { UploadStatus.fromValue(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_STATUS))) ); upload.setLocalAction(c.getInt(c.getColumnIndex(ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR))); - upload.setForceOverwrite(c.getInt( - c.getColumnIndex(ProviderTableMeta.UPLOADS_FORCE_OVERWRITE)) == 1); + upload.setNameCollisionPolicy(FileUploader.NameCollisionPolicy.deserialize(c.getInt( + c.getColumnIndex(ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY)))); upload.setCreateRemoteFolder(c.getInt( c.getColumnIndex(ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER)) == 1); upload.setUploadEndTimestamp(c.getLong(c.getColumnIndex(ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP))); diff --git a/src/main/java/com/owncloud/android/db/OCUpload.java b/src/main/java/com/owncloud/android/db/OCUpload.java index c5259307c9f9..3d9b5b628903 100644 --- a/src/main/java/com/owncloud/android/db/OCUpload.java +++ b/src/main/java/com/owncloud/android/db/OCUpload.java @@ -77,9 +77,9 @@ public class OCUpload implements Parcelable { @Getter @Setter private int localAction; /** - * Overwrite destination file? + * What to do in case of name collision. */ - @Getter @Setter private boolean forceOverwrite; + @Getter @Setter private FileUploader.NameCollisionPolicy nameCollisionPolicy; /** * Create destination folder? @@ -172,7 +172,7 @@ private void resetData() { fileSize = -1; uploadId = -1; localAction = FileUploader.LOCAL_BEHAVIOUR_COPY; - forceOverwrite = false; + nameCollisionPolicy = FileUploader.NameCollisionPolicy.DEFAULT; createRemoteFolder = false; uploadStatus = UploadStatus.UPLOAD_IN_PROGRESS; lastResult = UploadResult.UNKNOWN; @@ -281,7 +281,7 @@ private void readFromParcel(Parcel source) { remotePath = source.readString(); accountName = source.readString(); localAction = source.readInt(); - forceOverwrite = source.readInt() == 1; + nameCollisionPolicy = FileUploader.NameCollisionPolicy.deserialize(source.readInt()); createRemoteFolder = source.readInt() == 1; try { uploadStatus = UploadStatus.valueOf(source.readString()); @@ -312,7 +312,7 @@ public void writeToParcel(Parcel dest, int flags) { dest.writeString(remotePath); dest.writeString(accountName); dest.writeInt(localAction); - dest.writeInt(forceOverwrite ? 1 : 0); + dest.writeInt(nameCollisionPolicy.serialize()); dest.writeInt(createRemoteFolder ? 1 : 0); dest.writeString(uploadStatus.name()); dest.writeLong(uploadEndTimestamp); diff --git a/src/main/java/com/owncloud/android/db/ProviderMeta.java b/src/main/java/com/owncloud/android/db/ProviderMeta.java index ffe0064222fd..dec8ee6889a1 100644 --- a/src/main/java/com/owncloud/android/db/ProviderMeta.java +++ b/src/main/java/com/owncloud/android/db/ProviderMeta.java @@ -204,7 +204,7 @@ static public class ProviderTableMeta implements BaseColumns { public static final String UPLOADS_STATUS = "status"; public static final String UPLOADS_LOCAL_BEHAVIOUR = "local_behaviour"; public static final String UPLOADS_UPLOAD_TIME = "upload_time"; - public static final String UPLOADS_FORCE_OVERWRITE = "force_overwrite"; + public static final String UPLOADS_NAME_COLLISION_POLICY = "name_collision_policy"; public static final String UPLOADS_IS_CREATE_REMOTE_FOLDER = "is_create_remote_folder"; public static final String UPLOADS_UPLOAD_END_TIMESTAMP = "upload_end_timestamp"; public static final String UPLOADS_LAST_RESULT = "last_result"; diff --git a/src/main/java/com/owncloud/android/files/services/FileDownloader.java b/src/main/java/com/owncloud/android/files/services/FileDownloader.java index 0b8e66767c31..0ac96dbe403b 100644 --- a/src/main/java/com/owncloud/android/files/services/FileDownloader.java +++ b/src/main/java/com/owncloud/android/files/services/FileDownloader.java @@ -43,6 +43,8 @@ import com.owncloud.android.authentication.AuthenticatorActivity; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.UploadsStorageManager; +import com.owncloud.android.db.OCUpload; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; @@ -86,6 +88,7 @@ public class FileDownloader extends Service public static final String EXTRA_FILE_PATH = "FILE_PATH"; public static final String EXTRA_REMOTE_PATH = "REMOTE_PATH"; public static final String EXTRA_LINKED_TO_PATH = "LINKED_TO"; + public static final String EXTRA_CONFLICT_UPLOAD = "CONFLICT_UPLOAD"; public static final String ACCOUNT_NAME = "ACCOUNT_NAME"; private static final int FOREGROUND_SERVICE_ID = 412; @@ -109,7 +112,10 @@ public class FileDownloader extends Service private Notification mNotification; + private OCUpload conflictUpload; + @Inject UserAccountManager accountManager; + @Inject UploadsStorageManager uploadsStorageManager; public static String getDownloadAddedMessage() { return FileDownloader.class.getName() + DOWNLOAD_ADDED_MESSAGE; @@ -194,6 +200,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { final String behaviour = intent.getStringExtra(OCFileListFragment.DOWNLOAD_BEHAVIOUR); String activityName = intent.getStringExtra(SendShareDialog.ACTIVITY_NAME); String packageName = intent.getStringExtra(SendShareDialog.PACKAGE_NAME); + this.conflictUpload = intent.getParcelableExtra(FileDownloader.EXTRA_CONFLICT_UPLOAD); AbstractList requestedDownloads = new Vector(); try { DownloadFileOperation newDownload = new DownloadFileOperation(account, file, behaviour, activityName, @@ -631,6 +638,10 @@ private void notifyDownloadResult(DownloadFileOperation download, // Remove success notification if (downloadResult.isSuccess()) { + if (this.conflictUpload != null) { + uploadsStorageManager.removeUpload(this.conflictUpload); + } + // Sleep 2 seconds, so show the notification before remove it NotificationUtils.cancelWithDelay(mNotificationManager, R.string.downloader_download_succeeded_ticker, 2000); diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index 9b987d34a46f..f6ba5e883e78 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -150,9 +150,10 @@ public class FileUploader extends Service public static final String KEY_ACCOUNT = "ACCOUNT"; /** - * Set to true if remote file is to be overwritten. Default action is to upload with different name. + * What {@link NameCollisionPolicy} to do when the file already exists on the remote. */ - public static final String KEY_FORCE_OVERWRITE = "KEY_FORCE_OVERWRITE"; + public static final String KEY_NAME_COLLISION_POLICY = "KEY_NAME_COLLISION_POLICY"; + /** * Set to true if remote folder is to be created if it does not exist. */ @@ -258,7 +259,7 @@ public void uploadNewFile( } } - public void uploadFileWithOverwrite( + public void uploadFileWithNameCollisionPolicy( Context context, Account account, String[] localPaths, @@ -269,7 +270,7 @@ public void uploadFileWithOverwrite( int createdBy, boolean requiresWifi, boolean requiresCharging, - boolean overwrite + NameCollisionPolicy nameCollisionPolicy ) { Intent intent = new Intent(context, FileUploader.class); @@ -282,7 +283,7 @@ public void uploadFileWithOverwrite( intent.putExtra(FileUploader.KEY_CREATED_BY, createdBy); intent.putExtra(FileUploader.KEY_WHILE_ON_WIFI_ONLY, requiresWifi); intent.putExtra(FileUploader.KEY_WHILE_CHARGING_ONLY, requiresCharging); - intent.putExtra(FileUploader.KEY_FORCE_OVERWRITE, overwrite); + intent.putExtra(FileUploader.KEY_NAME_COLLISION_POLICY, nameCollisionPolicy); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { context.startForegroundService(intent); @@ -294,11 +295,11 @@ public void uploadFileWithOverwrite( /** * Call to upload a file */ - public void uploadFileWithOverwrite(Context context, Account account, String localPath, String remotePath, int + public void uploadFileWithNameCollisionPolicy(Context context, Account account, String localPath, String remotePath, int behaviour, String mimeType, boolean createRemoteFile, int createdBy, boolean requiresWifi, - boolean requiresCharging, boolean overwrite) { + boolean requiresCharging, NameCollisionPolicy nameCollisionPolicy) { - uploadFileWithOverwrite( + uploadFileWithNameCollisionPolicy( context, account, new String[]{localPath}, @@ -309,7 +310,7 @@ public void uploadFileWithOverwrite(Context context, Account account, String loc createdBy, requiresWifi, requiresCharging, - overwrite + nameCollisionPolicy ); } @@ -338,13 +339,13 @@ public void uploadNewFile(Context context, Account account, String localPath, St * Call to update multiple files already uploaded */ public void uploadUpdate(Context context, Account account, OCFile[] existingFiles, Integer behaviour, - Boolean forceOverwrite) { + NameCollisionPolicy nameCollisionPolicy) { Intent intent = new Intent(context, FileUploader.class); intent.putExtra(FileUploader.KEY_ACCOUNT, account); intent.putExtra(FileUploader.KEY_FILE, existingFiles); intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour); - intent.putExtra(FileUploader.KEY_FORCE_OVERWRITE, forceOverwrite); + intent.putExtra(FileUploader.KEY_NAME_COLLISION_POLICY, nameCollisionPolicy); if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { context.startForegroundService(intent); @@ -357,9 +358,9 @@ public void uploadUpdate(Context context, Account account, OCFile[] existingFile * Call to update a dingle file already uploaded */ public void uploadUpdate(Context context, Account account, OCFile existingFile, Integer behaviour, - Boolean forceOverwrite) { + NameCollisionPolicy nameCollisionPolicy) { - uploadUpdate(context, account, new OCFile[]{existingFile}, behaviour, forceOverwrite); + uploadUpdate(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy); } @@ -619,7 +620,10 @@ public int onStartCommand(Intent intent, int flags, int startId) { } // at this point variable "OCFile[] files" is loaded correctly. - boolean forceOverwrite = intent.getBooleanExtra(KEY_FORCE_OVERWRITE, false); + NameCollisionPolicy nameCollisionPolicy = (NameCollisionPolicy) intent.getSerializableExtra(KEY_NAME_COLLISION_POLICY); + if(nameCollisionPolicy == null) { + nameCollisionPolicy = NameCollisionPolicy.DEFAULT; + } int localAction = intent.getIntExtra(KEY_LOCAL_BEHAVIOUR, LOCAL_BEHAVIOUR_FORGET); boolean isCreateRemoteFolder = intent.getBooleanExtra(KEY_CREATE_REMOTE_FOLDER, false); int createdBy = intent.getIntExtra(KEY_CREATED_BY, UploadFileOperation.CREATED_BY_USER); @@ -630,7 +634,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { OCUpload ocUpload = new OCUpload(file, account); ocUpload.setFileSize(file.getFileLength()); - ocUpload.setForceOverwrite(forceOverwrite); + ocUpload.setNameCollisionPolicy(nameCollisionPolicy); ocUpload.setCreateRemoteFolder(isCreateRemoteFolder); ocUpload.setCreatedBy(createdBy); ocUpload.setLocalAction(localAction); @@ -646,7 +650,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { account, file, ocUpload, - forceOverwrite, + nameCollisionPolicy, localAction, this, onWifiOnly, @@ -707,7 +711,7 @@ public int onStartCommand(Intent intent, int flags, int startId) { account, null, upload, - upload.isForceOverwrite(), // TODO should be read from DB? + upload.getNameCollisionPolicy(), // TODO should be read from DB? upload.getLocalAction(), // TODO should be read from DB? this, onWifiOnly, @@ -870,9 +874,8 @@ public void clearListeners() { * If 'file' is a directory, returns 'true' if some of its descendant files * is uploading or waiting to upload. * - * Warning: If remote file exists and !forceOverwrite the original file - * is being returned here. That is, it seems as if the original file is - * being updated when actually a new file is being uploaded. + * Warning: If remote file exists and target was renamed the original file is being returned here. + * That is, it seems as if the original file is being updated when actually a new file is being uploaded. * * @param account Owncloud account where the remote file will be stored. * @param file A file that could be in the queue of pending uploads @@ -1364,4 +1367,22 @@ private void cancelUploadsForAccount(Account account) { mPendingUploads.remove(account.name); mUploadsStorageManager.removeUploads(account.name); } + + public enum NameCollisionPolicy { + CANCEL, + RENAME, + OVERWRITE, + ASK_USER; + + public static final NameCollisionPolicy DEFAULT = RENAME; + + public static NameCollisionPolicy deserialize(int ordinal) { + NameCollisionPolicy[] values = NameCollisionPolicy.values(); + return ordinal >= 0 && ordinal < values.length ? values[ordinal] : DEFAULT; + } + + public int serialize() { + return this.ordinal(); + } + } } diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 41c65899eef9..9a04e6050493 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -199,24 +199,24 @@ private void syncFolder(Context context, Resources resources, boolean lightVersi remotePath = syncedFolder.getRemotePath(); } - requester.uploadFileWithOverwrite( - context, - account, - file.getAbsolutePath(), - FileStorageUtils.getInstantUploadFilePath( + requester.uploadFileWithNameCollisionPolicy( + context, + account, + file.getAbsolutePath(), + FileStorageUtils.getInstantUploadFilePath( file, currentLocale, remotePath, syncedFolder.getLocalPath(), lastModificationTime, subfolderByDate), - uploadAction, - mimeType, - true, // create parent folder if not existent - UploadFileOperation.CREATED_AS_INSTANT_PICTURE, - needsWifi, - needsCharging, - true + uploadAction, + mimeType, + true, // create parent folder if not existent + UploadFileOperation.CREATED_AS_INSTANT_PICTURE, + needsWifi, + needsCharging, + FileUploader.NameCollisionPolicy.ASK_USER ); filesystemDataProvider.updateFilesystemFileAsSentForUpload(path, diff --git a/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index 72e4a1ecbe61..fc0a71ccfb6d 100644 --- a/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -292,7 +292,8 @@ protected RemoteOperationResult run(OwnCloudClient client) { */ private void requestForUpload(OCFile file) { FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadUpdate(mContext, mAccount, file, FileUploader.LOCAL_BEHAVIOUR_MOVE, true); + requester.uploadUpdate(mContext, mAccount, file, FileUploader.LOCAL_BEHAVIOUR_MOVE, + FileUploader.NameCollisionPolicy.ASK_USER); mTransferWasRequested = true; } diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index 1ffe1dcecced..823760945b81 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -90,6 +90,7 @@ import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import androidx.annotation.CheckResult; import androidx.annotation.RequiresApi; @@ -111,14 +112,14 @@ public class UploadFileOperation extends SyncOperation { private OCFile mFile; /** - * Original OCFile which is to be uploaded in case file had to be renamed - * (if forceOverwrite==false and remote file already exists). + * Original OCFile which is to be uploaded in case file had to be renamed (if nameCollisionPolicy==RENAME and remote + * file already exists). */ private OCFile mOldFile; private String mRemotePath; private String mFolderUnlockToken; private boolean mRemoteFolderToBeCreated; - private boolean mForceOverwrite; + private FileUploader.NameCollisionPolicy mNameCollisionPolicy; private int mLocalBehaviour; private int mCreatedBy; private boolean mOnWifiOnly; @@ -183,7 +184,7 @@ public UploadFileOperation(UploadsStorageManager uploadsStorageManager, Account account, OCFile file, OCUpload upload, - boolean forceOverwrite, + FileUploader.NameCollisionPolicy nameCollisionPolicy, int localBehaviour, Context context, boolean onWifiOnly, @@ -218,7 +219,7 @@ public UploadFileOperation(UploadsStorageManager uploadsStorageManager, mOnWifiOnly = onWifiOnly; mWhileChargingOnly = whileChargingOnly; mRemotePath = upload.getRemotePath(); - mForceOverwrite = forceOverwrite; + mNameCollisionPolicy = nameCollisionPolicy; mLocalBehaviour = localBehaviour; mOriginalStoragePath = mFile.getStoragePath(); mContext = context; @@ -504,7 +505,11 @@ private RemoteOperationResult encryptedUpload(OwnCloudClient client, OCFile pare /**** E2E *****/ // check name collision - checkNameCollision(client, metadata, parentFile.isEncrypted()); + RemoteOperationResult collisionResult = checkNameCollision(client, metadata, parentFile.isEncrypted()); + if (collisionResult != null) { + result = collisionResult; + return collisionResult; + } String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); expectedFile = new File(expectedPath); @@ -759,7 +764,11 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) { } // check name collision - checkNameCollision(client, null, false); + RemoteOperationResult collisionResult = checkNameCollision(client, null, false); + if (collisionResult != null) { + result = collisionResult; + return collisionResult; + } String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); expectedFile = new File(expectedPath); @@ -922,24 +931,37 @@ private RemoteOperationResult copyFile(File originalFile, String expectedPath) t return new RemoteOperationResult(ResultCode.OK); } - private void checkNameCollision(OwnCloudClient client, DecryptedFolderMetadata metadata, boolean encrypted) - throws OperationCancelledException { - /// automatic rename of file to upload in case of name collision in server + @CheckResult + private RemoteOperationResult checkNameCollision(OwnCloudClient client, DecryptedFolderMetadata metadata, boolean encrypted) + throws OperationCancelledException { Log_OC.d(TAG, "Checking name collision in server"); - if (!mForceOverwrite) { - String remotePath = getAvailableRemotePath(client, mRemotePath, metadata, encrypted); - mWasRenamed = !remotePath.equals(mRemotePath); - if (mWasRenamed) { - createNewOCFile(remotePath); - Log_OC.d(TAG, "File renamed as " + remotePath); + + if (existsFile(client, mRemotePath, metadata, encrypted)) { + switch (mNameCollisionPolicy) { + case CANCEL: + Log_OC.d(TAG, "File exists; canceling"); + throw new OperationCancelledException(); + case RENAME: + mRemotePath = getNewAvailableRemotePath(client, mRemotePath, metadata, encrypted); + mWasRenamed = true; + createNewOCFile(mRemotePath); + Log_OC.d(TAG, "File renamed as " + mRemotePath); + mRenameUploadListener.onRenameUpload(); + break; + case OVERWRITE: + Log_OC.d(TAG, "Overwriting file"); + break; + case ASK_USER: + Log_OC.d(TAG, "Name collision; asking the user what to do"); + return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } - mRemotePath = remotePath; - mRenameUploadListener.onRenameUpload(); } if (mCancellationRequested.get()) { throw new OperationCancelledException(); } + + return null; } private void handleSuccessfulUpload(File temporalFile, File expectedFile, File originalFile, @@ -1039,8 +1061,8 @@ private OCFile createLocalFolder(String remotePath) { /** - * Create a new OCFile mFile with new remote path. This is required if forceOverwrite==false. - * New file is stored as mFile, original as mOldFile. + * Create a new OCFile mFile with new remote path. This is required if nameCollisionPolicy==RENAME. New file is + * stored as mFile, original as mOldFile. * * @param newRemotePath new remote path */ @@ -1064,45 +1086,36 @@ private void createNewOCFile(String newRemotePath) { } /** - * Checks if remotePath does not exist in the server and returns it, or adds - * a suffix to it in order to avoid the server file is overwritten. + * Returns a new and available (does not exists on the server) remotePath. + * This adds an incremental suffix. * * @param client OwnCloud client * @param remotePath remote path of the file * @param metadata metadata of encrypted folder * @return new remote path */ - private String getAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata, - boolean encrypted) { - boolean check = existsFile(client, remotePath, metadata, encrypted); - if (!check) { - return remotePath; - } - - int pos = remotePath.lastIndexOf('.'); + private String getNewAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata, + boolean encrypted) { + int extPos = remotePath.lastIndexOf('.'); String suffix; String extension = ""; String remotePathWithoutExtension = ""; - if (pos >= 0) { - extension = remotePath.substring(pos + 1); - remotePathWithoutExtension = remotePath.substring(0, pos); + if (extPos >= 0) { + extension = remotePath.substring(extPos + 1); + remotePathWithoutExtension = remotePath.substring(0, extPos); } + int count = 2; + boolean exists; + String newPath; do { suffix = " (" + count + ")"; - if (pos >= 0) { - check = existsFile(client, remotePathWithoutExtension + suffix + "." + extension, metadata, encrypted); - } else { - check = existsFile(client, remotePath + suffix, metadata, encrypted); - } + newPath = extPos >= 0 ? remotePathWithoutExtension + suffix + "." + extension : remotePath + suffix; + exists = existsFile(client, newPath, metadata, encrypted); count++; - } while (check); + } while (exists); - if (pos >= 0) { - return remotePathWithoutExtension + suffix + "." + extension; - } else { - return remotePath + suffix; - } + return newPath; } private boolean existsFile(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata, diff --git a/src/main/java/com/owncloud/android/providers/FileContentProvider.java b/src/main/java/com/owncloud/android/providers/FileContentProvider.java index 9150eb459399..94e115c228e7 100644 --- a/src/main/java/com/owncloud/android/providers/FileContentProvider.java +++ b/src/main/java/com/owncloud/android/providers/FileContentProvider.java @@ -802,7 +802,7 @@ private void createUploadsTable(SQLiteDatabase db) { + ProviderTableMeta.UPLOADS_STATUS + INTEGER // UploadStatus + ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + INTEGER // Upload LocalBehaviour + ProviderTableMeta.UPLOADS_UPLOAD_TIME + INTEGER - + ProviderTableMeta.UPLOADS_FORCE_OVERWRITE + INTEGER // boolean + + ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY + INTEGER // boolean + ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + INTEGER // boolean + ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + INTEGER + ProviderTableMeta.UPLOADS_LAST_RESULT + INTEGER // Upload LastResult @@ -2067,12 +2067,56 @@ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } if(oldVersion < 52 && newVersion >= 52) { - Log_OC.i(SQL, "Entering in the #52 add synced.existing"); + Log_OC.i(SQL, "Entering in the #52 add synced.existing," + + " rename uploads.force_overwrite to uploads.name_collision_policy"); db.beginTransaction(); try { + // Add synced.existing db.execSQL(ALTER_TABLE + ProviderTableMeta.SYNCED_FOLDERS_TABLE_NAME + ADD_COLUMN + ProviderTableMeta.SYNCED_FOLDER_EXISTING + " INTEGER "); // boolean + // Rename uploads.force_overwrite to uploads.name_collision_policy + String tmpTableName = ProviderTableMeta.UPLOADS_TABLE_NAME + "_old"; + db.execSQL(ALTER_TABLE + ProviderTableMeta.UPLOADS_TABLE_NAME + " RENAME TO " + tmpTableName); + createUploadsTable(db); + db.execSQL("INSERT INTO " + ProviderTableMeta.UPLOADS_TABLE_NAME + " (" + + ProviderTableMeta._ID + ", " + + ProviderTableMeta.UPLOADS_LOCAL_PATH + ", " + + ProviderTableMeta.UPLOADS_REMOTE_PATH + ", " + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + ", " + + ProviderTableMeta.UPLOADS_FILE_SIZE + ", " + + ProviderTableMeta.UPLOADS_STATUS + ", " + + ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + ", " + + ProviderTableMeta.UPLOADS_UPLOAD_TIME + ", " + + ProviderTableMeta.UPLOADS_NAME_COLLISION_POLICY + ", " + + ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + ", " + + ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + ", " + + ProviderTableMeta.UPLOADS_LAST_RESULT + ", " + + ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY + ", " + + ProviderTableMeta.UPLOADS_IS_WIFI_ONLY + ", " + + ProviderTableMeta.UPLOADS_CREATED_BY + ", " + + ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN + + ") " + + " SELECT " + + ProviderTableMeta._ID + ", " + + ProviderTableMeta.UPLOADS_LOCAL_PATH + ", " + + ProviderTableMeta.UPLOADS_REMOTE_PATH + ", " + + ProviderTableMeta.UPLOADS_ACCOUNT_NAME + ", " + + ProviderTableMeta.UPLOADS_FILE_SIZE + ", " + + ProviderTableMeta.UPLOADS_STATUS + ", " + + ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR + ", " + + ProviderTableMeta.UPLOADS_UPLOAD_TIME + ", " + + "force_overwrite" + ", " + + ProviderTableMeta.UPLOADS_IS_CREATE_REMOTE_FOLDER + ", " + + ProviderTableMeta.UPLOADS_UPLOAD_END_TIMESTAMP + ", " + + ProviderTableMeta.UPLOADS_LAST_RESULT + ", " + + ProviderTableMeta.UPLOADS_IS_WHILE_CHARGING_ONLY + ", " + + ProviderTableMeta.UPLOADS_IS_WIFI_ONLY + ", " + + ProviderTableMeta.UPLOADS_CREATED_BY + ", " + + ProviderTableMeta.UPLOADS_FOLDER_UNLOCK_TOKEN + + " FROM " + tmpTableName); + db.execSQL("DROP TABLE " + tmpTableName); + upgraded = true; db.setTransactionSuccessful(); } finally { diff --git a/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java b/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java index 7733900af6c3..5b1038423028 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java @@ -25,6 +25,8 @@ import android.os.Bundle; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.datamodel.UploadsStorageManager; +import com.owncloud.android.db.OCUpload; import com.owncloud.android.files.services.FileDownloader; import com.owncloud.android.files.services.FileUploader; import com.owncloud.android.lib.common.utils.Log_OC; @@ -32,52 +34,82 @@ import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision; import com.owncloud.android.ui.dialog.ConflictsResolveDialog.OnConflictDecisionMadeListener; +import javax.inject.Inject; + /** * Wrapper activity which will be launched if keep-in-sync file will be modified by external * application. */ public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener { + /** + * A nullable upload entry that must be removed when and if the conflict is resolved. + */ + public static final String EXTRA_CONFLICT_UPLOAD = "CONFLICT_UPLOAD"; + /** + * Specify the upload local behaviour when there is no CONFLICT_UPLOAD. + */ + public static final String EXTRA_LOCAL_BEHAVIOUR = "LOCAL_BEHAVIOUR"; private static final String TAG = ConflictsResolveActivity.class.getSimpleName(); + @Inject UploadsStorageManager uploadsStorageManager; + + private OCUpload conflictUpload; + private int localBehaviour = FileUploader.LOCAL_BEHAVIOUR_FORGET; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + if (savedInstanceState != null) { + this.conflictUpload = savedInstanceState.getParcelable(EXTRA_CONFLICT_UPLOAD); + this.localBehaviour = savedInstanceState.getInt(EXTRA_LOCAL_BEHAVIOUR); + } else { + this.conflictUpload = getIntent().getParcelableExtra(EXTRA_CONFLICT_UPLOAD); + this.localBehaviour = getIntent().getIntExtra(EXTRA_LOCAL_BEHAVIOUR, this.localBehaviour); + } + + if (this.conflictUpload != null) { + this.localBehaviour = this.conflictUpload.getLocalAction(); + } } @Override public void conflictDecisionMade(Decision decision) { + if (decision == Decision.CANCEL) { + return; + } - Integer behaviour = null; - Boolean forceOverwrite = null; + OCFile file = getFile(); + FileUploader.UploadRequester uploadRequester = new FileUploader.UploadRequester(); - switch (decision) { - case CANCEL: - finish(); - return; - case OVERWRITE: - // use local version -> overwrite on server - forceOverwrite = true; - break; - case KEEP_BOTH: - behaviour = FileUploader.LOCAL_BEHAVIOUR_MOVE; - break; - case SERVER: - // use server version -> delete local, request download - Intent intent = new Intent(this, FileDownloader.class); - intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount()); - intent.putExtra(FileDownloader.EXTRA_FILE, getFile()); - startService(intent); - finish(); - return; - default: - Log_OC.e(TAG, "Unhandled conflict decision " + decision); - return; + // Upload + if (decision == Decision.KEEP_LOCAL || decision == Decision.KEEP_BOTH) { + FileUploader.NameCollisionPolicy collisionPolicy = FileUploader.NameCollisionPolicy.OVERWRITE; + if (decision == Decision.KEEP_BOTH) { + collisionPolicy = FileUploader.NameCollisionPolicy.RENAME; + } + + uploadRequester.uploadUpdate(this, getAccount(), file, localBehaviour, collisionPolicy); + + if (this.conflictUpload != null) { + uploadsStorageManager.removeUpload(this.conflictUpload); + } + } + + // Download + if (decision == Decision.KEEP_SERVER && !this.shouldDeleteLocal()) { + // Overwrite local file + Intent intent = new Intent(this, FileDownloader.class); + intent.putExtra(FileDownloader.EXTRA_ACCOUNT, getAccount()); + intent.putExtra(FileDownloader.EXTRA_FILE, file); + if (this.conflictUpload != null) { + intent.putExtra(FileDownloader.EXTRA_CONFLICT_UPLOAD, this.conflictUpload); + } + startService(intent); } - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadUpdate(this, getAccount(), getFile(), behaviour, forceOverwrite); finish(); } @@ -87,26 +119,27 @@ protected void onStart() { if (getAccount() != null) { OCFile file = getFile(); if (getFile() == null) { - Log_OC.e(TAG, "No conflictive file received"); + Log_OC.e(TAG, "No file received"); finish(); } else { - /// Check whether the 'main' OCFile handled by the Activity is contained in the current Account - file = getStorageManager().getFileByPath(file.getRemotePath()); // file = null if not in the - // current Account - if (file != null) { - setFile(file); - ConflictsResolveDialog d = ConflictsResolveDialog.newInstance(this); - d.showDialog(this); - + // Check whether the file is contained in the current Account + if (getStorageManager().fileExists(file.getRemotePath())) { + ConflictsResolveDialog dialog = new ConflictsResolveDialog(this, !this.shouldDeleteLocal()); + dialog.showDialog(this); } else { - // account was changed to a different one - just finish + // Account was changed to a different one - just finish finish(); } } - } else { finish(); } + } + /** + * @return whether the local version of the files is to be deleted. + */ + private boolean shouldDeleteLocal() { + return localBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE; } } diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 2ca1520000e1..7cf5933b4492 100755 --- a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -45,6 +45,7 @@ import com.evernote.android.job.util.support.PersistableBundleCompat; import com.nextcloud.client.account.User; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; import com.nextcloud.java.util.Optional; @@ -121,6 +122,9 @@ public class UploadListActivity extends FileActivity { @Inject PowerManagementService powerManagementService; + @Inject + Clock clock; + @Override public void showFiles(boolean onDeviceOnly) { super.showFiles(onDeviceOnly); @@ -169,9 +173,11 @@ private void setupContent() { uploadListAdapter = new UploadListAdapter(this, uploadsStorageManager, + getStorageManager(), userAccountManager, connectivityService, - powerManagementService); + powerManagementService, + clock); final GridLayoutManager lm = new GridLayoutManager(this, 1); uploadListAdapter.setLayoutManager(lm); diff --git a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 7c503029ada9..85a4af330ac8 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -25,6 +25,7 @@ import android.accounts.Account; import android.content.ActivityNotFoundException; +import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.net.Uri; @@ -41,9 +42,12 @@ import com.afollestad.sectionedrecyclerview.SectionedRecyclerViewAdapter; import com.afollestad.sectionedrecyclerview.SectionedViewHolder; import com.nextcloud.client.account.UserAccountManager; +import com.nextcloud.client.core.Clock; import com.nextcloud.client.device.PowerManagementService; import com.nextcloud.client.network.ConnectivityService; +import com.owncloud.android.MainApp; import com.owncloud.android.R; +import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.datamodel.OCFile; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; @@ -52,7 +56,10 @@ import com.owncloud.android.db.OCUploadComparator; import com.owncloud.android.db.UploadResult; import com.owncloud.android.files.services.FileUploader; +import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.common.utils.Log_OC; +import com.owncloud.android.operations.RefreshFolderOperation; +import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimeTypeUtil; @@ -74,9 +81,11 @@ public class UploadListAdapter extends SectionedRecyclerViewAdapter - parentActivity.getFileOperationsHelper().checkCurrentCredentials( - item.getAccount(accountManager))); - } else { - // not a credentials error - itemViewHolder.itemLayout.setOnClickListener(v -> { - File file = new File(item.getLocalPath()); - if (file.exists()) { - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.retry(parentActivity, accountManager, item); - loadUploadItemsFromDb(); - } else { - DisplayUtils.showSnackMessage( - v.getRootView().findViewById(android.R.id.content), - R.string.local_file_not_found_message - ); + final UploadResult uploadResult = item.getLastResult(); + itemViewHolder.itemLayout.setOnClickListener(v -> { + if (uploadResult == UploadResult.CREDENTIAL_ERROR) { + parentActivity.getFileOperationsHelper().checkCurrentCredentials( + item.getAccount(accountManager)); + return; + } else if (uploadResult == UploadResult.SYNC_CONFLICT) { + String remotePath = item.getRemotePath(); + OCFile ocFile = storageManager.getFileByPath(remotePath); + + if (ocFile == null) { // Remote file doesn't exist, try to refresh folder + OCFile folder = storageManager.getFileByPath(new File(remotePath).getParent() + "/"); + if (folder != null && folder.isFolder()) { + this.refreshFolder(itemViewHolder, account, folder, (caller, result) -> { + itemViewHolder.status.setText(status); + if (result.isSuccess()) { + OCFile file = storageManager.getFileByPath(remotePath); + if (file != null) { + this.openConflictActivity(file, item); + } + } + }); + return; + } + + // Destination folder doesn't exist anymore } - }); - } + + if (ocFile != null) { + this.openConflictActivity(ocFile, item); + return; + } + + // Remote file doesn't exist anymore = there is no more conflict + } + + // not a credentials error + File file = new File(item.getLocalPath()); + if (file.exists()) { + FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); + requester.retry(parentActivity, accountManager, item); + loadUploadItemsFromDb(); + } else { + DisplayUtils.showSnackMessage( + v.getRootView().findViewById(android.R.id.content), + R.string.local_file_not_found_message + ); + } + }); } else { itemViewHolder.itemLayout.setOnClickListener(v -> onUploadItemClick(item)); @@ -459,6 +501,36 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati } } + private void refreshFolder(ItemViewHolder view, Account account, OCFile folder, OnRemoteOperationListener listener) { + view.itemLayout.setClickable(false); + view.status.setText(R.string.uploads_view_upload_status_fetching_server_version); + Context context = MainApp.getAppContext(); + new RefreshFolderOperation(folder, + clock.getCurrentTime(), + false, + false, + true, + storageManager, + account, + context) + .execute(account, context, (caller, result) -> { + view.itemLayout.setClickable(true); + listener.onRemoteOperationFinish(caller, result); + }, parentActivity.getHandler()); + } + + private void openConflictActivity(OCFile file, OCUpload upload) { + file.setStoragePath(upload.getLocalPath()); + + Context context = MainApp.getAppContext(); + Intent i = new Intent(context, ConflictsResolveActivity.class); + i.setFlags(i.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); + i.putExtra(ConflictsResolveActivity.EXTRA_FILE, file); + i.putExtra(ConflictsResolveActivity.EXTRA_ACCOUNT, upload.getAccount(accountManager)); + i.putExtra(ConflictsResolveActivity.EXTRA_CONFLICT_UPLOAD, upload); + context.startActivity(i); + } + /** * Gets the status text to show to the user according to the status and last result of the * the given upload. diff --git a/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java b/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java index 7c139e307af6..a231792fbada 100644 --- a/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java +++ b/src/main/java/com/owncloud/android/ui/dialog/ConflictsResolveDialog.java @@ -43,44 +43,48 @@ public class ConflictsResolveDialog extends DialogFragment { public enum Decision { CANCEL, KEEP_BOTH, - OVERWRITE, - SERVER + KEEP_LOCAL, + KEEP_SERVER, } - OnConflictDecisionMadeListener mListener; + private final OnConflictDecisionMadeListener listener; + private final boolean canKeepServer; - public static ConflictsResolveDialog newInstance(OnConflictDecisionMadeListener listener) { - ConflictsResolveDialog f = new ConflictsResolveDialog(); - f.mListener = listener; - return f; + public ConflictsResolveDialog(OnConflictDecisionMadeListener listener, boolean canKeepServer) { + this.listener = listener; + this.canKeepServer = canKeepServer; } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - return new AlertDialog.Builder(requireActivity(), R.style.Theme_ownCloud_Dialog) - .setIcon(R.drawable.ic_warning) - .setTitle(R.string.conflict_title) - .setMessage(getString(R.string.conflict_message)) - .setPositiveButton(R.string.conflict_use_local_version, - (dialog, which) -> { - if (mListener != null) { - mListener.conflictDecisionMade(Decision.OVERWRITE); - } - }) - .setNeutralButton(R.string.conflict_keep_both, - (dialog, which) -> { - if (mListener != null) { - mListener.conflictDecisionMade(Decision.KEEP_BOTH); - } - }) - .setNegativeButton(R.string.conflict_use_server_version, - (dialog, which) -> { - if (mListener != null) { - mListener.conflictDecisionMade(Decision.SERVER); - } - }) - .create(); + AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity(), R.style.Theme_ownCloud_Dialog) + .setIcon(R.drawable.ic_warning) + .setTitle(R.string.conflict_title) + .setMessage(getString(R.string.conflict_message)) + .setPositiveButton(R.string.conflict_use_local_version, + (dialog, which) -> { + if (listener != null) { + listener.conflictDecisionMade(Decision.KEEP_LOCAL); + } + }) + .setNeutralButton(R.string.conflict_keep_both, + (dialog, which) -> { + if (listener != null) { + listener.conflictDecisionMade(Decision.KEEP_BOTH); + } + }); + + if (this.canKeepServer) { + builder.setNegativeButton(R.string.conflict_use_server_version, + (dialog, which) -> { + if (listener != null) { + listener.conflictDecisionMade(Decision.KEEP_SERVER); + } + }); + } + + return builder.create(); } public void showDialog(AppCompatActivity activity) { @@ -96,8 +100,8 @@ public void showDialog(AppCompatActivity activity) { @Override public void onCancel(DialogInterface dialog) { - if (mListener != null) { - mListener.conflictDecisionMade(Decision.CANCEL); + if (listener != null) { + listener.conflictDecisionMade(Decision.CANCEL); } } diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 81b2e2a10ecf..576a5cda8b9c 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -170,6 +170,7 @@ Unknown error Waiting for Wi-Fi Waiting to exit power save mode + Fetching server version… Waiting to upload %1$s (%2$d) Downloading… From 01fe0ace50409cedb815cc69f2b3e51c287fc111 Mon Sep 17 00:00:00 2001 From: nextcloud-android-bot Date: Thu, 28 Nov 2019 14:45:35 +0000 Subject: [PATCH 68/69] Drone: update FindBugs results to reflect reduced error/warning count [skip ci] Signed-off-by: nextcloud-android-bot --- scripts/analysis/findbugs-results.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index 009bd2c17f2d..7b89b2202c95 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -407 \ No newline at end of file +406 \ No newline at end of file From 2cdc3b1943db8eca7ebfa6c4aef6d8a74af158e5 Mon Sep 17 00:00:00 2001 From: Alice Gaudon Date: Mon, 2 Dec 2019 18:29:40 +0100 Subject: [PATCH 69/69] FileUploader: require explicit NameCollisionPolicy - Changes NameConflictPolicy for manual uploads from default RENAME to ASK_USER - Removal of helper class FileUploader.UploadRequester (methods become static and go to FileUploader) Signed-off-by: Alice Gaudon --- .../android/files/services/FileUploader.java | 453 ++++++++---------- .../android/jobs/ContactsBackupJob.java | 24 +- .../owncloud/android/jobs/FilesSyncJob.java | 18 +- .../operations/SynchronizeFileOperation.java | 10 +- .../ui/activity/ConflictsResolveActivity.java | 3 +- .../ui/activity/FileDisplayActivity.java | 6 +- .../ReceiveExternalFilesActivity.java | 10 +- .../ui/activity/UploadListActivity.java | 17 +- .../android/ui/adapter/UploadListAdapter.java | 22 +- .../CopyAndUploadContentUrisTask.java | 6 +- .../android/ui/helpers/UriUploader.java | 24 +- .../android/utils/FilesSyncHelper.java | 18 +- 12 files changed, 277 insertions(+), 334 deletions(-) diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index f6ba5e883e78..10dcc8f0e7b3 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -37,6 +37,7 @@ import android.content.Intent; import android.graphics.BitmapFactory; import android.os.Binder; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -126,10 +127,6 @@ public class FileUploader extends Service public static final String KEY_REMOTE_FILE = "REMOTE_FILE"; public static final String KEY_MIME_TYPE = "MIME_TYPE"; - private Notification mNotification; - - @Inject UserAccountManager accountManager; - /** * Call this Service with only this Intent key if all pending uploads are to be retried. */ @@ -177,287 +174,227 @@ public class FileUploader extends Service public static final int LOCAL_BEHAVIOUR_FORGET = 2; public static final int LOCAL_BEHAVIOUR_DELETE = 3; - private Looper mServiceLooper; - private ServiceHandler mServiceHandler; - private IBinder mBinder; - private OwnCloudClient mUploadClient; - private Account mCurrentAccount; - private FileDataStorageManager mStorageManager; - //since there can be only one instance of an Android service, there also just one db connection. - @Inject UploadsStorageManager mUploadsStorageManager; - @Inject ConnectivityService connectivityService; - @Inject PowerManagementService powerManagementService; - - private IndexedForest mPendingUploads = new IndexedForest<>(); - /** - * {@link UploadFileOperation} object of ongoing upload. Can be null. Note: There can only be one concurrent upload! + * Upload a new file */ - private UploadFileOperation mCurrentUpload; - - private NotificationManager mNotificationManager; - private NotificationCompat.Builder mNotificationBuilder; - private int mLastPercent; + public static void uploadNewFile( + Context context, + Account account, + String localPath, + String remotePath, + int behaviour, + String mimeType, + boolean createRemoteFile, + int createdBy, + boolean requiresWifi, + boolean requiresCharging, + NameCollisionPolicy nameCollisionPolicy + ) { + uploadNewFile( + context, + account, + new String[]{localPath}, + new String[]{remotePath}, + new String[]{mimeType}, + behaviour, + createRemoteFile, + createdBy, + requiresWifi, + requiresCharging, + nameCollisionPolicy + ); + } - public static String getUploadsAddedMessage() { - return FileUploader.class.getName() + UPLOADS_ADDED_MESSAGE; + /** + * Upload multiple new files + */ + public static void uploadNewFile( + Context context, + Account account, + String[] localPaths, + String[] remotePaths, + String[] mimeTypes, + Integer behaviour, + Boolean createRemoteFolder, + int createdBy, + boolean requiresWifi, + boolean requiresCharging, + NameCollisionPolicy nameCollisionPolicy + ) { + Intent intent = new Intent(context, FileUploader.class); + + intent.putExtra(FileUploader.KEY_ACCOUNT, account); + intent.putExtra(FileUploader.KEY_LOCAL_FILE, localPaths); + intent.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths); + intent.putExtra(FileUploader.KEY_MIME_TYPE, mimeTypes); + intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour); + intent.putExtra(FileUploader.KEY_CREATE_REMOTE_FOLDER, createRemoteFolder); + intent.putExtra(FileUploader.KEY_CREATED_BY, createdBy); + intent.putExtra(FileUploader.KEY_WHILE_ON_WIFI_ONLY, requiresWifi); + intent.putExtra(FileUploader.KEY_WHILE_CHARGING_ONLY, requiresCharging); + intent.putExtra(FileUploader.KEY_NAME_COLLISION_POLICY, nameCollisionPolicy); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent); + } else { + context.startService(intent); + } } - public static String getUploadStartMessage() { - return FileUploader.class.getName() + UPLOAD_START_MESSAGE; + /** + * Upload and overwrite an already uploaded file + */ + public static void uploadUpdateFile( + Context context, + Account account, + OCFile existingFile, + Integer behaviour, + NameCollisionPolicy nameCollisionPolicy + ) { + uploadUpdateFile(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy); } - public static String getUploadFinishMessage() { - return FileUploader.class.getName() + UPLOAD_FINISH_MESSAGE; + /** + * Upload and overwrite already uploaded files + */ + public static void uploadUpdateFile( + Context context, + Account account, + OCFile[] existingFiles, + Integer behaviour, + NameCollisionPolicy nameCollisionPolicy + ) { + Intent intent = new Intent(context, FileUploader.class); + + intent.putExtra(FileUploader.KEY_ACCOUNT, account); + intent.putExtra(FileUploader.KEY_FILE, existingFiles); + intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour); + intent.putExtra(FileUploader.KEY_NAME_COLLISION_POLICY, nameCollisionPolicy); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(intent); + } else { + context.startService(intent); + } } - @Override - public void onRenameUpload() { - mUploadsStorageManager.updateDatabaseUploadStart(mCurrentUpload); - sendBroadcastUploadStarted(mCurrentUpload); + /** + * Retry a failed {@link OCUpload} identified by {@link OCUpload#getRemotePath()} + */ + public static void retryUpload(@NonNull Context context, @NonNull Account account, @NonNull OCUpload upload) { + Intent i = new Intent(context, FileUploader.class); + i.putExtra(FileUploader.KEY_RETRY, true); + i.putExtra(FileUploader.KEY_ACCOUNT, account); + i.putExtra(FileUploader.KEY_RETRY_UPLOAD, upload); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(i); + } else { + context.startService(i); + } } /** - * Helper class providing methods to ease requesting commands to {@link FileUploader} . + * Retry a subset of all the stored failed uploads. * - * Avoids the need of checking once and again what extras are needed or optional - * in the {@link Intent} to pass to {@link Context#startService(Intent)}. + * @param context Caller {@link Context} + * @param account If not null, only failed uploads to this OC account will be retried; otherwise, + * uploads of all accounts will be retried. + * @param uploadResult If not null, only failed uploads with the result specified will be retried; + * otherwise, failed uploads due to any result will be retried. */ - public static class UploadRequester { - - /** - * Call to upload several new files - */ - public void uploadNewFile( - Context context, - Account account, - String[] localPaths, - String[] remotePaths, - String[] mimeTypes, - Integer behaviour, - Boolean createRemoteFolder, - int createdBy, - boolean requiresWifi, - boolean requiresCharging - ) { - Intent intent = new Intent(context, FileUploader.class); - - intent.putExtra(FileUploader.KEY_ACCOUNT, account); - intent.putExtra(FileUploader.KEY_LOCAL_FILE, localPaths); - intent.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths); - intent.putExtra(FileUploader.KEY_MIME_TYPE, mimeTypes); - intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour); - intent.putExtra(FileUploader.KEY_CREATE_REMOTE_FOLDER, createRemoteFolder); - intent.putExtra(FileUploader.KEY_CREATED_BY, createdBy); - intent.putExtra(FileUploader.KEY_WHILE_ON_WIFI_ONLY, requiresWifi); - intent.putExtra(FileUploader.KEY_WHILE_CHARGING_ONLY, requiresCharging); - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - context.startForegroundService(intent); - } else { - context.startService(intent); - } - } + public static void retryFailedUploads( + @NonNull final Context context, + @Nullable final Account account, + @NotNull final UploadsStorageManager uploadsStorageManager, + @NotNull final ConnectivityService connectivityService, + @NotNull final UserAccountManager accountManager, + @NotNull final PowerManagementService powerManagementService, + @Nullable final UploadResult uploadResult + ) { + OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads(); + Account currentAccount = null; + boolean resultMatch; + boolean accountMatch; + + boolean gotNetwork = connectivityService.getActiveNetworkType() != JobRequest.NetworkType.ANY && + !connectivityService.isInternetWalled(); + boolean gotWifi = gotNetwork && Device.getNetworkType(context).equals(JobRequest.NetworkType.UNMETERED); + boolean charging = Device.getBatteryStatus(context).isCharging(); + boolean isPowerSaving = powerManagementService.isPowerSavingEnabled(); + + for (OCUpload failedUpload : failedUploads) { + accountMatch = account == null || account.name.equals(failedUpload.getAccountName()); + resultMatch = uploadResult == null || uploadResult.equals(failedUpload.getLastResult()); + if (accountMatch && resultMatch) { + if (currentAccount == null || !currentAccount.name.equals(failedUpload.getAccountName())) { + currentAccount = failedUpload.getAccount(accountManager); + } - public void uploadFileWithNameCollisionPolicy( - Context context, - Account account, - String[] localPaths, - String[] remotePaths, - String[] mimeTypes, - Integer behaviour, - Boolean createRemoteFolder, - int createdBy, - boolean requiresWifi, - boolean requiresCharging, - NameCollisionPolicy nameCollisionPolicy - ) { - Intent intent = new Intent(context, FileUploader.class); - - intent.putExtra(FileUploader.KEY_ACCOUNT, account); - intent.putExtra(FileUploader.KEY_LOCAL_FILE, localPaths); - intent.putExtra(FileUploader.KEY_REMOTE_FILE, remotePaths); - intent.putExtra(FileUploader.KEY_MIME_TYPE, mimeTypes); - intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour); - intent.putExtra(FileUploader.KEY_CREATE_REMOTE_FOLDER, createRemoteFolder); - intent.putExtra(FileUploader.KEY_CREATED_BY, createdBy); - intent.putExtra(FileUploader.KEY_WHILE_ON_WIFI_ONLY, requiresWifi); - intent.putExtra(FileUploader.KEY_WHILE_CHARGING_ONLY, requiresCharging); - intent.putExtra(FileUploader.KEY_NAME_COLLISION_POLICY, nameCollisionPolicy); - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - context.startForegroundService(intent); - } else { - context.startService(intent); + if (!new File(failedUpload.getLocalPath()).exists()) { + if (!failedUpload.getLastResult().equals(UploadResult.FILE_NOT_FOUND)) { + failedUpload.setLastResult(UploadResult.FILE_NOT_FOUND); + uploadsStorageManager.updateUpload(failedUpload); + } + } else { + charging = charging || Device.getBatteryStatus(context).getBatteryPercent() == 1; + if (!isPowerSaving && gotNetwork && canUploadBeRetried(failedUpload, gotWifi, charging)) { + retryUpload(context, currentAccount, failedUpload); + } + } } } + } - /** - * Call to upload a file - */ - public void uploadFileWithNameCollisionPolicy(Context context, Account account, String localPath, String remotePath, int - behaviour, String mimeType, boolean createRemoteFile, int createdBy, boolean requiresWifi, - boolean requiresCharging, NameCollisionPolicy nameCollisionPolicy) { - - uploadFileWithNameCollisionPolicy( - context, - account, - new String[]{localPath}, - new String[]{remotePath}, - new String[]{mimeType}, - behaviour, - createRemoteFile, - createdBy, - requiresWifi, - requiresCharging, - nameCollisionPolicy - ); - } - - /** - * Call to upload a new single file - */ - public void uploadNewFile(Context context, Account account, String localPath, String remotePath, int - behaviour, String mimeType, boolean createRemoteFile, int createdBy, boolean requiresWifi, - boolean requiresCharging) { - - uploadNewFile( - context, - account, - new String[]{localPath}, - new String[]{remotePath}, - new String[]{mimeType}, - behaviour, - createRemoteFile, - createdBy, - requiresWifi, - requiresCharging - ); - } - - /** - * Call to update multiple files already uploaded - */ - public void uploadUpdate(Context context, Account account, OCFile[] existingFiles, Integer behaviour, - NameCollisionPolicy nameCollisionPolicy) { - Intent intent = new Intent(context, FileUploader.class); - - intent.putExtra(FileUploader.KEY_ACCOUNT, account); - intent.putExtra(FileUploader.KEY_FILE, existingFiles); - intent.putExtra(FileUploader.KEY_LOCAL_BEHAVIOUR, behaviour); - intent.putExtra(FileUploader.KEY_NAME_COLLISION_POLICY, nameCollisionPolicy); - - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - context.startForegroundService(intent); - } else { - context.startService(intent); - } - } + private static boolean canUploadBeRetried(OCUpload upload, boolean gotWifi, boolean isCharging) { + File file = new File(upload.getLocalPath()); + boolean needsWifi = upload.isUseWifiOnly(); + boolean needsCharging = upload.isWhileChargingOnly(); - /** - * Call to update a dingle file already uploaded - */ - public void uploadUpdate(Context context, Account account, OCFile existingFile, Integer behaviour, - NameCollisionPolicy nameCollisionPolicy) { + return file.exists() && (!needsWifi || gotWifi) && (!needsCharging || isCharging); + } - uploadUpdate(context, account, new OCFile[]{existingFile}, behaviour, nameCollisionPolicy); - } + public static String getUploadsAddedMessage() { + return FileUploader.class.getName() + UPLOADS_ADDED_MESSAGE; + } + public static String getUploadStartMessage() { + return FileUploader.class.getName() + UPLOAD_START_MESSAGE; + } - /** - * Call to retry upload identified by remotePath - */ - public void retry (Context context, UserAccountManager accountManager, OCUpload upload) { - if (upload != null && context != null) { - Account account = accountManager.getAccountByName(upload.getAccountName()); - retry(context, account, upload); - } else { - throw new IllegalArgumentException("Null parameter!"); - } - } + public static String getUploadFinishMessage() { + return FileUploader.class.getName() + UPLOAD_FINISH_MESSAGE; + } - private boolean checkIfUploadCanBeRetried(OCUpload ocUpload, boolean gotWifi, boolean isCharging) { - boolean needsWifi = ocUpload.isUseWifiOnly(); - boolean needsCharging = ocUpload.isWhileChargingOnly(); - return new File(ocUpload.getLocalPath()).exists() && !(needsCharging && !isCharging) && - !(needsWifi && !gotWifi); + private Notification mNotification; + private Looper mServiceLooper; + private ServiceHandler mServiceHandler; + private IBinder mBinder; + private OwnCloudClient mUploadClient; + private Account mCurrentAccount; + private FileDataStorageManager mStorageManager; - } + @Inject UserAccountManager accountManager; + @Inject UploadsStorageManager mUploadsStorageManager; + @Inject ConnectivityService connectivityService; + @Inject PowerManagementService powerManagementService; - /** - * Retry a subset of all the stored failed uploads. - * - * @param context Caller {@link Context} - * @param account If not null, only failed uploads to this OC account will be retried; otherwise, - * uploads of all accounts will be retried. - * @param uploadResult If not null, only failed uploads with the result specified will be retried; - * otherwise, failed uploads due to any result will be retried. - */ - public void retryFailedUploads( - @NonNull final Context context, - @Nullable final Account account, - @NotNull final UploadsStorageManager uploadsStorageManager, - @NotNull final ConnectivityService connectivityService, - @NotNull final UserAccountManager accountManager, - @NotNull final PowerManagementService powerManagementService, - @Nullable final UploadResult uploadResult - ) { - OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads(); - Account currentAccount = null; - boolean resultMatch; - boolean accountMatch; - - boolean gotNetwork = connectivityService.getActiveNetworkType() != JobRequest.NetworkType.ANY && - !connectivityService.isInternetWalled(); - boolean gotWifi = gotNetwork && Device.getNetworkType(context).equals(JobRequest.NetworkType.UNMETERED); - boolean charging = Device.getBatteryStatus(context).isCharging(); - boolean isPowerSaving = powerManagementService.isPowerSavingEnabled(); - - for ( OCUpload failedUpload: failedUploads) { - accountMatch = account == null || account.name.equals(failedUpload.getAccountName()); - resultMatch = uploadResult == null || uploadResult.equals(failedUpload.getLastResult()); - if (accountMatch && resultMatch) { - if (currentAccount == null || !currentAccount.name.equals(failedUpload.getAccountName())) { - currentAccount = failedUpload.getAccount(accountManager); - } + private IndexedForest mPendingUploads = new IndexedForest<>(); - if (!new File(failedUpload.getLocalPath()).exists()) { - if (!failedUpload.getLastResult().equals(UploadResult.FILE_NOT_FOUND)) { - failedUpload.setLastResult(UploadResult.FILE_NOT_FOUND); - uploadsStorageManager.updateUpload(failedUpload); - } - } else { - charging = charging || Device.getBatteryStatus(context).getBatteryPercent() == 1; - if (!isPowerSaving && gotNetwork && checkIfUploadCanBeRetried(failedUpload, gotWifi, charging)) { - retry(context, currentAccount, failedUpload); - } - } - } - } - } + /** + * {@link UploadFileOperation} object of ongoing upload. Can be null. Note: There can only be one concurrent upload! + */ + private UploadFileOperation mCurrentUpload; - /** - * Private implementation of retry. - * - * @param context - * @param account - * @param upload - */ - private void retry(Context context, Account account, OCUpload upload) { - if (upload != null) { - Intent i = new Intent(context, FileUploader.class); - i.putExtra(FileUploader.KEY_RETRY, true); - i.putExtra(FileUploader.KEY_ACCOUNT, account); - i.putExtra(FileUploader.KEY_RETRY_UPLOAD, upload); + private NotificationManager mNotificationManager; + private NotificationCompat.Builder mNotificationBuilder; + private int mLastPercent; - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { - context.startForegroundService(i); - } else { - context.startService(i); - } - } - } + @Override + public void onRenameUpload() { + mUploadsStorageManager.updateDatabaseUploadStart(mCurrentUpload); + sendBroadcastUploadStarted(mCurrentUpload); } /** @@ -483,7 +420,7 @@ public void onCreate() { .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.notification_icon)) .setColor(ThemeUtils.primaryColor(getApplicationContext(), true)); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { builder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD); } @@ -1173,7 +1110,7 @@ private void notifyUploadStart(UploadFileOperation upload) { String.format(getString(R.string.uploader_upload_in_progress_content), 0, upload.getFileName()) ); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mNotificationBuilder.setChannelId(NotificationUtils.NOTIFICATION_CHANNEL_UPLOAD); } diff --git a/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java b/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java index 36a5a1fd89cc..66a45ca4cbc7 100644 --- a/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java +++ b/src/main/java/com/owncloud/android/jobs/ContactsBackupJob.java @@ -157,18 +157,18 @@ private void backupContact(Account account, String backupFolder) { } } - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( - getContext(), - account, - file.getAbsolutePath(), - backupFolder + filename, - FileUploader.LOCAL_BEHAVIOUR_MOVE, - null, - true, - UploadFileOperation.CREATED_BY_USER, - false, - false + FileUploader.uploadNewFile( + getContext(), + account, + file.getAbsolutePath(), + backupFolder + filename, + FileUploader.LOCAL_BEHAVIOUR_MOVE, + null, + true, + UploadFileOperation.CREATED_BY_USER, + false, + false, + FileUploader.NameCollisionPolicy.ASK_USER ); } diff --git a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java index 9a04e6050493..c234d5aee4c2 100644 --- a/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java +++ b/src/main/java/com/owncloud/android/jobs/FilesSyncJob.java @@ -140,12 +140,11 @@ protected Result onRunJob(@NonNull Params params) { Locale currentLocale = context.getResources().getConfiguration().locale; SimpleDateFormat sFormatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss", currentLocale); sFormatter.setTimeZone(TimeZone.getTimeZone(TimeZone.getDefault().getID())); - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); for (SyncedFolder syncedFolder : syncedFolderProvider.getSyncedFolders()) { if ((syncedFolder.isEnabled()) && (!skipCustom || MediaFolderType.CUSTOM != syncedFolder.getType())) { syncFolder(context, resources, lightVersion, filesystemDataProvider, currentLocale, sFormatter, - requester, syncedFolder); + syncedFolder); } } @@ -156,10 +155,15 @@ protected Result onRunJob(@NonNull Params params) { return Result.SUCCESS; } - private void syncFolder(Context context, Resources resources, boolean lightVersion, - FilesystemDataProvider filesystemDataProvider, Locale currentLocale, - SimpleDateFormat sFormatter, FileUploader.UploadRequester requester, - SyncedFolder syncedFolder) { + private void syncFolder( + Context context, + Resources resources, + boolean lightVersion, + FilesystemDataProvider filesystemDataProvider, + Locale currentLocale, + SimpleDateFormat sFormatter, + SyncedFolder syncedFolder + ) { String remotePath; boolean subfolderByDate; Integer uploadAction; @@ -199,7 +203,7 @@ private void syncFolder(Context context, Resources resources, boolean lightVersi remotePath = syncedFolder.getRemotePath(); } - requester.uploadFileWithNameCollisionPolicy( + FileUploader.uploadNewFile( context, account, file.getAbsolutePath(), diff --git a/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index fc0a71ccfb6d..d2ed15ccdd45 100644 --- a/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -291,9 +291,13 @@ protected RemoteOperationResult run(OwnCloudClient client) { * @param file OCFile object representing the file to upload */ private void requestForUpload(OCFile file) { - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadUpdate(mContext, mAccount, file, FileUploader.LOCAL_BEHAVIOUR_MOVE, - FileUploader.NameCollisionPolicy.ASK_USER); + FileUploader.uploadUpdateFile( + mContext, + mAccount, + file, + FileUploader.LOCAL_BEHAVIOUR_MOVE, + FileUploader.NameCollisionPolicy.ASK_USER + ); mTransferWasRequested = true; } diff --git a/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java b/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java index 5b1038423028..0be1bb2c4eb3 100644 --- a/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java @@ -82,7 +82,6 @@ public void conflictDecisionMade(Decision decision) { } OCFile file = getFile(); - FileUploader.UploadRequester uploadRequester = new FileUploader.UploadRequester(); // Upload if (decision == Decision.KEEP_LOCAL || decision == Decision.KEEP_BOTH) { @@ -91,7 +90,7 @@ public void conflictDecisionMade(Decision decision) { collisionPolicy = FileUploader.NameCollisionPolicy.RENAME; } - uploadRequester.uploadUpdate(this, getAccount(), file, localBehaviour, collisionPolicy); + FileUploader.uploadUpdateFile(this, getAccount(), file, localBehaviour, collisionPolicy); if (this.conflictUpload != null) { uploadsStorageManager.removeUpload(this.conflictUpload); diff --git a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java index 84df7e4fd4db..2dc513c3ed0e 100644 --- a/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.java @@ -1053,8 +1053,7 @@ private void requestUploadOfFilesFromFileSystem(String[] filePaths, int resultCo break; } - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( + FileUploader.uploadNewFile( this, getAccount(), filePaths, @@ -1064,7 +1063,8 @@ private void requestUploadOfFilesFromFileSystem(String[] filePaths, int resultCo false, // do not create parent folder if not existent UploadFileOperation.CREATED_BY_USER, false, - false + false, + FileUploader.NameCollisionPolicy.ASK_USER ); } else { diff --git a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java index 06b62f7b90c1..5a746157ee65 100755 --- a/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/ReceiveExternalFilesActivity.java @@ -887,19 +887,19 @@ private boolean somethingToUpload() { } public void uploadFile(String tmpName, String filename) { - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( + FileUploader.uploadNewFile( getBaseContext(), getAccount(), - tmpName, + tmpName, mFile.getRemotePath() + filename, FileUploader.LOCAL_BEHAVIOUR_COPY, null, true, UploadFileOperation.CREATED_BY_USER, false, - false - ); + false, + FileUploader.NameCollisionPolicy.ASK_USER + ); finish(); } diff --git a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 7cf5933b4492..492b89c9f9b2 100755 --- a/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -220,14 +220,15 @@ private void refresh() { } // retry failed uploads - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - new Thread(() -> requester.retryFailedUploads(this, - null, - uploadsStorageManager, - connectivityService, - userAccountManager, - powerManagementService, - null)).start(); + new Thread(() -> FileUploader.retryFailedUploads( + this, + null, + uploadsStorageManager, + connectivityService, + userAccountManager, + powerManagementService, + null + )).start(); // update UI uploadListAdapter.loadUploadItemsFromDb(); diff --git a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java index 85a4af330ac8..81cbe9a6025d 100755 --- a/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java +++ b/src/main/java/com/owncloud/android/ui/adapter/UploadListAdapter.java @@ -138,16 +138,15 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool uploadsStorageManager.clearSuccessfulUploads(); break; case FAILED: - new Thread(() -> new FileUploader.UploadRequester() - .retryFailedUploads( - parentActivity, - null, - uploadsStorageManager, - connectivityService, - accountManager, - powerManagementService, - null)) - .start(); + new Thread(() -> FileUploader.retryFailedUploads( + parentActivity, + null, + uploadsStorageManager, + connectivityService, + accountManager, + powerManagementService, + null + )).start(); break; default: @@ -390,8 +389,7 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati // not a credentials error File file = new File(item.getLocalPath()); if (file.exists()) { - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.retry(parentActivity, accountManager, item); + FileUploader.retryUpload(parentActivity, item.getAccount(accountManager), item); loadUploadItemsFromDb(); } else { DisplayUtils.showSnackMessage( diff --git a/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java b/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java index f60552ebfbbe..9ae8d452fb8a 100644 --- a/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java +++ b/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java @@ -219,8 +219,7 @@ protected ResultCode doInBackground(Object[] params) { } private void requestUpload(Account account, String localPath, String remotePath, int behaviour, String mimeType) { - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( + FileUploader.uploadNewFile( mAppContext, account, localPath, @@ -230,7 +229,8 @@ private void requestUpload(Account account, String localPath, String remotePath, false, // do not create parent folder if not existent UploadFileOperation.CREATED_BY_USER, false, - false + false, + FileUploader.NameCollisionPolicy.ASK_USER ); } diff --git a/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java b/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java index e4c1432d178c..9dc46e3a2235 100644 --- a/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java +++ b/src/main/java/com/owncloud/android/ui/helpers/UriUploader.java @@ -158,18 +158,18 @@ private String generateDiplayName() { * @param remotePath Absolute path in the current OC account to set to the uploaded file. */ private void requestUpload(String localPath, String remotePath) { - FileUploader.UploadRequester requester = new FileUploader.UploadRequester(); - requester.uploadNewFile( - mActivity, - mAccount, - localPath, - remotePath, - mBehaviour, - null, // MIME type will be detected from file name - false, // do not create parent folder if not existent - UploadFileOperation.CREATED_BY_USER, - false, - false + FileUploader.uploadNewFile( + mActivity, + mAccount, + localPath, + remotePath, + mBehaviour, + null, // MIME type will be detected from file name + false, // do not create parent folder if not existent + UploadFileOperation.CREATED_BY_USER, + false, + false, + FileUploader.NameCollisionPolicy.ASK_USER ); } diff --git a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java index 432a9220d166..93ad2b35159a 100644 --- a/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java +++ b/src/main/java/com/owncloud/android/utils/FilesSyncHelper.java @@ -196,8 +196,6 @@ public static void restartJobsIfNeeded(final UploadsStorageManager uploadsStorag final PowerManagementService powerManagementService) { final Context context = MainApp.getAppContext(); - FileUploader.UploadRequester uploadRequester = new FileUploader.UploadRequester(); - boolean accountExists; OCUpload[] failedUploads = uploadsStorageManager.getFailedUploads(); @@ -221,13 +219,15 @@ public static void restartJobsIfNeeded(final UploadsStorageManager uploadsStorag new Thread(() -> { if (connectivityService.getActiveNetworkType() != JobRequest.NetworkType.ANY && !connectivityService.isInternetWalled()) { - uploadRequester.retryFailedUploads(context, - null, - uploadsStorageManager, - connectivityService, - accountManager, - powerManagementService, - null); + FileUploader.retryFailedUploads( + context, + null, + uploadsStorageManager, + connectivityService, + accountManager, + powerManagementService, + null + ); } }).start(); }