android: Drop custom file abstraction + UI

Android Viewer had its own file abstraction layer.

From the (now deleted) IFile.java:

> An abstraction of the File class, intended to be implemented by different
> Document Providers.
>
> It represents a file or a directory in the context of a certain Document
> Provider. It wraps the file-related operations and provides access to the
> final document as a local File, downloading it if necessary.

However, Android already provides such an abstraction by
what is called "documents provider" there as well, s. [1].
Android Viewer has previously been adapted to
support and make use of that.
Therefore, drop the custom implementation to avoid
duplication and having to reimplement functionality
already provided otherwise.

Also, drop the custom UI elements to display and
select files implemented on top of the custom
file abstraction. Support for using the system file
picker (via the corresponding Intents) has been added
earlier and is now the only available option to open
files from within the app.

[1] https://developer.android.com/training/data-storage/shared/documents-files

Change-Id: Ide529e836a32fd7e880e5a72d971af9f9c7e74bf
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/113667
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
This commit is contained in:
Michael Weghorn
2021-04-06 14:26:06 +02:00
parent 07fd90cc7d
commit a23bd42e9b
36 changed files with 50 additions and 3271 deletions

View File

@@ -110,23 +110,6 @@
</intent-filter>
</activity>
<!-- Document Provider Settings Activity -->
<activity android:name=".storage.DocumentProviderSettingsActivity"
android:theme="@style/Theme.AppCompat.Light.DarkActionBar"
android:label="@string/storage_provider_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".storage.external.BrowserSelectorActivity" >
</activity>
<activity android:name=".storage.external.DirectoryBrowserActivity"
android:label="@string/directory_browser_label"
android:windowSoftInputMode="stateHidden">
</activity>
<activity android:name=".PresentationActivity"
android:screenOrientation="landscape" >
<meta-data

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent" android:layout_height="match_parent">
</FrameLayout>

View File

@@ -86,24 +86,6 @@
android:padding="16dp"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:padding="7dp"
android:id="@+id/text_directory_path"
android:background="@color/background_normal"
/>
<!--Document browser-->
<android.support.v7.widget.RecyclerView
android:id="@+id/file_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/background_normal"
android:orientation="vertical" />
<!--Icon and text to open system file picker via Intent -->
<LinearLayout
android:id="@+id/system_file_picker_layout"
@@ -129,14 +111,6 @@
</android.support.v4.widget.NestedScrollView>
<!-- The navigation drawer -->
<android.support.design.widget.NavigationView
android:id="@+id/navigation_drawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@color/background_normal"
app:menu="@menu/navigation_menu"
android:theme="@style/LibreOfficeTheme.NavigationView" />
</android.support.v4.widget.DrawerLayout>
@@ -145,7 +119,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:visibility="invisible"
app:backgroundTint="@color/background_normal"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"

View File

@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of the LibreOffice project.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:orientation="vertical" >
<ImageView
android:id="@+id/file_item_icon"
tools:src="@drawable/ic_folder_black_24dp"
tools:tint="@color/text_color_secondary"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="fitStart"
android:layout_gravity="center"
android:contentDescription="@string/file_icon_desc" >
</ImageView>
<TextView
android:id="@+id/file_item_name"
tools:text="file or dirname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp"
android:layout_gravity="center"
android:textSize="15sp"
android:textStyle="bold"
android:textColor="@android:color/secondary_text_light"
android:maxLines="2">
</TextView>
</LinearLayout>

View File

@@ -1,25 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of the LibreOffice project.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<GridView
android:id="@+id/file_explorer_grid_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="120dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center">
</GridView>
</LinearLayout>

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of the LibreOffice project.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ListView
android:id="@+id/file_explorer_list_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</ListView>
</LinearLayout>

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of the LibreOffice project.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal"
android:layout_marginStart="@dimen/list_item_margin"
android:layout_marginLeft="@dimen/list_item_margin"
android:layout_marginEnd="@dimen/list_item_margin"
android:layout_marginRight="@dimen/list_item_margin">
<ImageView
android:id="@+id/file_item_icon"
tools:src="@drawable/ic_folder_black_24dp"
tools:tint="@color/text_color_secondary"
android:layout_height="match_parent"
android:layout_width="@dimen/file_icon_width"
android:layout_marginEnd="@dimen/file_icon_margin_end"
android:layout_marginRight="@dimen/file_icon_margin_end"
android:layout_gravity="center"
android:contentDescription="@string/file_icon_desc" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal">
<TextView
android:id="@+id/file_item_name"
tools:text="file or dirname"
style="@style/ListItemText"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="2"
android:ellipsize="end"
android:maxLines="1"/>
<TextView
android:id="@+id/file_item_size"
tools:text="filesize"
style="@style/ListItemText"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="1" />
<TextView
android:id="@+id/file_item_date"
tools:text="date/time"
style="@style/ListItemText"
android:layout_height="match_parent"
android:layout_width="0dp"
android:layout_weight="2"
android:ellipsize="end"
android:gravity="end"
android:maxLines="1"/>
</LinearLayout>
</LinearLayout>

View File

@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF">
<LinearLayout
android:id="@+id/browser_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/up_image"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:src="@drawable/ic_menu_back"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
android:contentDescription="@string/up_description"/>
<EditText
android:id="@+id/directory_header"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:maxLines="1"
android:scrollHorizontally="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:inputType="text"/>
<Button
android:id="@+id/directory_search_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@string/search_label"/>
</LinearLayout>
<LinearLayout
android:id="@+id/browser_footer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true">
<Button
android:id="@+id/cancel_button"
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:text="@string/cancel_label"/>
<Button
android:id="@+id/confirm_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/confirm_label"/>
</LinearLayout>
<ListView
android:id="@+id/directory_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/browser_header"
android:layout_above="@id/browser_footer">
</ListView>
</RelativeLayout>

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:id="@+id/context_menu_open"
android:title="@string/open"/>
<item android:id="@+id/context_menu_share"
android:title="@string/share"/>
</menu>

View File

@@ -1,34 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group
android:checkableBehavior="single"
android:id="@+id/group_providers">
<item android:id="@+id/menu_provider_documents"
android:title="@string/local_documents"
android:icon="@drawable/ic_folder_black_24dp" />
<item android:id="@+id/menu_provider_filesystem"
android:title="@string/local_file_system"
android:icon="@drawable/ic_storage_black_24dp"/>
<item android:id="@+id/menu_provider_extsd"
android:title="@string/external_sd_file_system"
android:icon="@drawable/ic_sd_card_black_24dp"/>
<item android:id="@+id/menu_provider_otg"
android:title="@string/otg_file_system"
android:icon="@drawable/ic_usb_black_24dp"/>
<item android:id="@+id/menu_system_file_dialog"
android:title="@string/system_file_selector"
android:icon="@drawable/ic_folder_black_24dp" />
</group>
<group android:orderInCategory="100">
<item android:id="@+id/menu_storage_preferences"
android:title="@string/storage_provider_settings"
android:icon="@drawable/ic_settings_black_24dp"/>
</group>
</menu>

View File

@@ -2,60 +2,6 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/menu_filter"
android:title="@string/filter"
android:icon="@drawable/ic_filter_list_black_24dp"
app:showAsAction="ifRoom">
<menu>
<group
android:checkableBehavior="single">
<item
android:id="@+id/menu_filter_everything"
android:title="@string/filter_everything" />
<item
android:id="@+id/menu_filter_documents"
android:title="@string/filter_documents" />
<item
android:id="@+id/menu_filter_spreadsheets"
android:title="@string/filter_spreadsheets" />
<item
android:id="@+id/menu_filter_presentations"
android:title="@string/filter_presentations" />
<item
android:id="@+id/menu_filter_drawings"
android:title="@string/filter_drawings" />
</group>
</menu>
</item>
<item
android:id="@+id/menu_sort"
android:title="@string/sort"
android:icon="@drawable/ic_sort_black_24dp"
app:showAsAction="ifRoom">
<menu>
<group android:checkableBehavior="single">
<item android:id="@+id/menu_sort_size_asc"
android:title="@string/sort_smallest" />
<item android:id="@+id/menu_sort_size_desc"
android:title="@string/sort_largest" />
<item android:id="@+id/menu_sort_az"
android:title="@string/sort_az"/>
<item android:id="@+id/menu_sort_za"
android:title="@string/sort_za"/>
<item android:id="@+id/menu_sort_modified_newest"
android:title="@string/sort_newest"/>
<item android:id="@+id/menu_sort_modified_oldest"
android:title="@string/sort_oldest"/>
</group>
</menu>
</item>
<item android:id="@+id/action_settings"
android:title="@string/action_settings"
android:orderInCategory="100"/>

View File

@@ -21,67 +21,25 @@
<string name="new_drawing">Neue Zeichnung</string>
<string name="default_document_name">unbenannt</string>
<string name="system_file_selector">System-Dateidialog</string>
<string name="select_file_to_open">Datei zum Öffnen auswählen</string>
<string name="browser_app_name">LibreOffice Browser</string>
<string name="menu_search">Suchen</string>
<string name="list_view">Liste</string>
<string name="grid_view">Gitter</string>
<string name="filter">Filtern nach</string>
<string name="search_not_found">Suchbegriff nicht gefunden</string>
<string name="sort">Sortieren nach</string>
<string name="sort_smallest">Kleinste zuerst</string>
<string name="sort_largest">Größte zuerst</string>
<string name="sort_az">A-Z</string>
<string name="sort_za">Z-A</string>
<string name="sort_oldest">Älteste zuerst</string>
<string name="sort_newest">Neueste zuerst</string>
<string name="menu_sort_size">Nach Größe sortieren</string>
<string name="menu_sort_az">Von A bis Z sortieren</string>
<string name="menu_sort_modified">Nach Datum sortieren</string>
<string name="menu_preferences">Einstellungen</string>
<string name="file_icon_desc">Datei-Icon</string>
<string name="title_recents">Zuletzt verwendete Dateien</string>
<string name="title_browser">Alle Dateien</string>
<!-- Pref keys as resources ; Not currently used -->
<string name="EXPLORER_VIEW_TYPE_KEY">EXPLORER_VIEW_TYPE</string>
<string name="CURRENT_DIRECTORY_KEY">CURRENT_DIRECTORY</string>
<string name="pref_category_explorer">Dateimanager-Einstellungen</string>
<string name="pref_category_general">Allgemein</string>
<string name="pref_experimental_editing">Experimenteller Modus</string>
<string name="pref_experimental_editing_summary">Den experimentellen Editier-Modus aktivieren. Verwendung auf eigene Gefahr.</string>
<string name="pref_show_hidden_files">Versteckte Dateien/Ordner</string>
<string name="pref_show_hidden_files_summary">Anzeige versteckter Dateien/Ordner aktivieren</string>
<string name="pref_developer_mode">Entwickler-Modus</string>
<string name="pref_developer_mode_summary">Entwickler-Modus, in dem in der App UNO-Kommandos gesendet werden können. Verwendung auf eigene Gefahr.</string>
<string name="action_about">Info</string>
<string name="action_parts">Abschnitte</string>
<string name="action_settings">Einstellungen</string>
<string name="open">Öffnen</string>
<string name="share">Teilen</string>
<string name="share_via">Teilen via</string>
<!-- Document browser filters -->
<string name="filter_everything">Alles</string>
<string name="filter_documents">Dokumente</string>
<string name="filter_spreadsheets">Tabellendokumente</string>
<string name="filter_presentations">Präsentationen</string>
<string name="filter_drawings">Zeichnungen</string>
<!-- Document provider names -->
<string name="document_locations">Dokumentenorte</string>
<string name="close_document_locations">Dokumentenorte schließen</string>
<string name="local_documents">Dokumentenverzeichnis</string>
<string name="local_file_system">Lokales Dateisystem</string>
<string name="external_sd_file_system">Externe SD</string>
<string name="otg_file_system">OTG-Gerät (experimentell)</string>
<string name="usb_connected_configure">USB verbunden, richten Sie Ihr Gerät ein.</string>
<string name="ext_document_provider_error">Ungültige Wurzel-Datei. Prüfen Sie die SD-Karten-Einstellungen.</string>
<string name="legacy_extsd_missing_error">Ungültige Wurzel-Datei. Prüfen Sie die externe SD-Karte und/oder die Einstellungen</string>
<string name="otg_missing_error">Ungültige Wurzel-Datei. Prüfen Sie Ihr OTG-Gerät und/oder die Einstellungen.</string>
<!-- Edit action names -->
<string name="action_bold">Fett</string>
@@ -100,25 +58,10 @@
<string name="message_save_incomplete">Speichern unvollständig. Gab es Änderungen?</string>
<string name="message_saving_failed">Speichern des Dokuments ist fehlgeschlagen.</string>
<!-- Document provider settings -->
<string name="storage_provider_settings">Speicheranbieter-Einstellungen</string>
<string name="physical_storage_settings">Einstellungen für physikalischen Speicher</string>
<string name="external_sd_path">Pfad zur externen SD-Karte</string>
<string name="otg_device_path">Pfad zum OTG-Gerät</string>
<string name="otg_warning">Experimentelles Feature: Nur verwenden, wenn OTG-Gerät beschreibbar ist.</string>
<string name="password">Passwort</string>
<string name="action_undo">Rückgängig</string>
<string name="action_redo">Wiederherstellen</string>
<!-- Directory browser strings -->
<string name="up_description">Nach oben</string>
<string name="confirm_label">Bestätigen</string>
<string name="cancel_label">Abbrechen</string>
<string name="search_label">Los</string>
<string name="directory_browser_label">Verzeichnis auswählen</string>
<string name="bad_directory">Ungültiger Verzeichnispfad</string>
<string name="current_dir">Aktuelles Verzeichnis: %1$s</string>
<!-- Save Alert dialog strings -->
<string name="save_alert_dialog_title">Dokument vor dem Schließen speichern?</string>
<string name="save_document">SPEICHERN</string>
@@ -207,12 +150,6 @@
<string name="alert_cancel">Abbrechen</string>
<string name="unable_to_go_further">Kann nicht weiter gehen.</string>
<string name="current_uno_command">Aktuelles UNO-Kommando</string>
<string name="pref_sort_summary">Sortiermodus für Dateien auswählen: A-Z, nach Größe oder nach Datum.</string>
<string name="pref_viewmode_summary">Dateien als Gitter oder als Liste anzeigen.</string>
<string name="pref_file_explorer_title">Dateimanager-Layout</string>
<string name="pref_sort_title">Datei-Reihenfolge</string>
<string name="pref_filter_title">Standard-Dateifilter</string>
<string name="pref_filter_summary">Wählen Sie, welcher Dateifilter standardmäßig verwendet werden soll.</string>
<string name="display_language">Anzeigesprache</string>
<string name="display_language_summary">Wählen Sie die Standard-Anzeigesprache</string>
<string name="unable_to_export_pdf">PDF-Export nicht möglich</string>

View File

@@ -23,62 +23,20 @@
<string name="browser_app_name">LibreOffice Tarayıcı</string>
<string name="menu_search">Ara</string>
<string name="list_view">Liste</string>
<string name="grid_view">Grid</string>
<string name="filter">Şununla filtrele:</string>
<string name="search_not_found">Söz dizimi bulunamadı.</string>
<string name="sort">Şununla sırala:</string>
<string name="sort_smallest">Küçükten büyüğe</string>
<string name="sort_largest">Büyükten küçüğe</string>
<string name="sort_az">A-Z</string>
<string name="sort_za">Z-A</string>
<string name="sort_oldest">Eskiden yeniye</string>
<string name="sort_newest">Yeniden eskiye</string>
<string name="menu_sort_size">Boyuta Göre Sırala</string>
<string name="menu_sort_az">Alfabetik Sırala</string>
<string name="menu_sort_modified">Tarihe Göre Sırala</string>
<string name="menu_preferences">Seçenekler</string>
<string name="file_icon_desc">dosya_simgesi</string>
<string name="title_recents">Son açılan dosyalar</string>
<string name="title_browser">Tüm dosyalar</string>
<!-- Pref keys as resources ; Not currently used -->
<string name="EXPLORER_VIEW_TYPE_KEY">EXPLORER_VIEW_TYPE</string>
<string name="CURRENT_DIRECTORY_KEY">CURRENT_DIRECTORY</string>
<string name="pref_category_explorer">Dosya Gezgini Ayarları</string>
<string name="pref_category_general">Genel</string>
<string name="pref_experimental_editing">Deneysel Mod</string>
<string name="pref_experimental_editing_summary">Deneysel düzenleme modunu etkinleştirin. Kullanım riski size aittir.</string>
<string name="pref_show_hidden_files">Gizli Dosyalar ve Dizinler</string>
<string name="pref_show_hidden_files_summary">Gizli dosyalar ve dizinleri göstermeyi etkinleştir.</string>
<string name="pref_developer_mode">Geliştirici Modu</string>
<string name="pref_developer_mode_summary">Geliştirici modunu etkinleştirerek UNO komutları gönderin. Kullanım riski size aittir.</string>
<string name="action_about">Hakkında</string>
<string name="action_parts">Bölümler</string>
<string name="action_settings">Ayarlar</string>
<string name="open"></string>
<string name="share">Paylaş</string>
<string name="share_via">Şununla paylaş</string>
<!-- Document browser filters -->
<string name="filter_everything">Hepsi</string>
<string name="filter_documents">Kelime İşlemci</string>
<string name="filter_spreadsheets">Hesap Tablosu</string>
<string name="filter_presentations">Sunum</string>
<string name="filter_drawings">Çizimler</string>
<!-- Document provider names -->
<string name="document_locations">Belge dizinleri</string>
<string name="close_document_locations">Belge dizinlerini kapat</string>
<string name="local_documents">Belgeler</string>
<string name="local_file_system">Yerel dosya sistemi</string>
<string name="external_sd_file_system">Harici SD</string>
<string name="otg_file_system">OTG cihazı (deneysel)</string>
<string name="usb_connected_configure">USB bağlantısı yapıldı, cihazınızın ayarlarını yapınız.</string>
<string name="ext_document_provider_error">Geçersiz dizin. SD kart ayarlarınızı kontrol ediniz.</string>
<string name="legacy_extsd_missing_error">Geçersiz dizin. SD kartınızı veya SD kart ayarlarınızı kontrol ediniz.</string>
<string name="otg_missing_error">Geçersiz dizin. OTG cihazınızı veya OTG cihazı ayarlarınızı kontrol ediniz.</string>
<!-- Edit action names -->
<string name="action_bold">Kalın</string>
@@ -96,12 +54,6 @@
<string name="message_saving">Belge kaydediliyor…</string>
<string name="message_save_incomplete">Kayıt tamamlanmadı. Değişiklik yapıldı mı?</string>
<!-- Document provider settings -->
<string name="storage_provider_settings">Depolama sağlayıcısı ayarları</string>
<string name="physical_storage_settings">Fiziksel depolama ayarları</string>
<string name="external_sd_path">Harici SD yolu</string>
<string name="otg_device_path">OTG cihazı yolu</string>
<string name="otg_warning">Deneysel özellik: OTG cihazı yazılabilir ise kullanın</string>
<string name="password">Parola</string>
<string name="action_undo">Geri Al</string>
<string name="action_redo">Yinele</string>
@@ -203,12 +155,6 @@
<string name="alert_cancel">İptal</string>
<string name="unable_to_go_further">Daha fazla ilerlenemiyor.</string>
<string name="current_uno_command">Şuan ki UNO komutu</string>
<string name="pref_sort_summary">Dosyaların nasıl sıralanacağını seçiniz. A-Z, Z-A ya da boyuta göre.</string>
<string name="pref_viewmode_summary">Dosyaları grid veya liste şeklinde görüntüle.</string>
<string name="pref_file_explorer_title">Dosya Gezgini</string>
<string name="pref_sort_title">Dosya Sıralaması</string>
<string name="pref_filter_title">Varsayılan Dosya Filtrelemesi</string>
<string name="pref_filter_summary">Varsayılan dosya filtrelemesini seç</string>
<string name="display_language">Uygulama Dili</string>
<string name="display_language_summary">Varsayılan dili değiştir</string>
<string name="unable_to_export_pdf">Pdf dışa aktarılamıyor.</string>

View File

@@ -1,62 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer-array name="FilterTypeValues">
<item >-1</item>
<item >0</item>
<item >1</item>
<item >2</item>
</integer-array>
<string-array name="FilterTypeStringValues">
<item >-1</item>
<item >0</item>
<item >1</item>
<item >2</item>
<item >3</item>
</string-array>
<string-array name="SupportedLanguagesValues">
<item>SYSTEM_DEFAULT_LANGUAGE</item>
<item >de</item>
<item >en</item>
<item >tr</item>
</string-array>
<string-array name="SortModeStringValues">
<item >0</item>
<item >1</item>
<item >2</item>
<item >3</item>
<item >4</item>
<item >5</item>
</string-array>
<!-- View Mode names,values -->
<string-array name="ViewModeNames">
<item >@string/grid_view</item>
<item >@string/list_view</item>
</string-array>
<string-array name="ViewModeStringValues">
<item >0</item>
<item >1</item>
</string-array>
<string-array name="FilterTypeNames">
<item>@string/filter_everything</item>
<item>@string/filter_documents</item>
<item>@string/filter_spreadsheets</item>
<item>@string/filter_presentations</item>
<item>@string/filter_drawings</item>
</string-array>
<string-array name="SupportedLanguages">
<item>(System Default)</item>
<item>Deutsch</item>
<item>English</item>
<item>Turkçe</item>
</string-array>
<string-array name="SortModeNames">
<item >@string/sort_az</item>
<item >@string/sort_za</item>
<item >@string/sort_oldest</item>
<item >@string/sort_newest</item>
<item >@string/sort_largest</item>
<item >@string/sort_smallest</item>
</string-array>
</resources>

View File

@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<integer name="grid_view_integer">0</integer>
</resources>

View File

@@ -21,67 +21,24 @@
<string name="new_drawing">New Drawing</string>
<string name="default_document_name">untitled</string>
<string name="system_file_selector">System File Dialog</string>
<string name="select_file_to_open">Select file to open</string>
<string name="browser_app_name">LibreOffice Browser</string>
<string name="menu_search">Search</string>
<string name="list_view">List</string>
<string name="grid_view">Grid</string>
<string name="filter">Filter by</string>
<string name="search_not_found">Keyword not found</string>
<string name="sort">Sort by</string>
<string name="sort_smallest">Smallest first</string>
<string name="sort_largest">Largest first</string>
<string name="sort_az">A-Z</string>
<string name="sort_za">Z-A</string>
<string name="sort_oldest">Oldest first</string>
<string name="sort_newest">Newest first</string>
<string name="menu_sort_size">Sort By Size</string>
<string name="menu_sort_az">Sort A-Z</string>
<string name="menu_sort_modified">Sort by Date</string>
<string name="menu_preferences">Preferences</string>
<string name="file_icon_desc">fileicon</string>
<string name="title_recents">Recent files</string>
<string name="title_browser">All files</string>
<!-- Pref keys as resources ; Not currently used -->
<string name="EXPLORER_VIEW_TYPE_KEY">EXPLORER_VIEW_TYPE</string>
<string name="CURRENT_DIRECTORY_KEY">CURRENT_DIRECTORY</string>
<string name="pref_category_explorer">File Explorer Settings</string>
<string name="pref_category_general">General</string>
<string name="pref_experimental_editing">Experimental Mode</string>
<string name="pref_experimental_editing_summary">Enable the experimental editing mode. Use at your own risk.</string>
<string name="pref_show_hidden_files">Hidden Files/Folders</string>
<string name="pref_show_hidden_files_summary">Enable to show hidden files/folders</string>
<string name="pref_developer_mode">Developer Mode</string>
<string name="pref_developer_mode_summary">Enable developer mode where you can send UNO commands within app. Use at your own risk.</string>
<string name="action_about">About</string>
<string name="action_parts">Parts</string>
<string name="action_settings">Settings</string>
<string name="open">Open</string>
<string name="share">Share</string>
<string name="share_via">Share via</string>
<!-- Document browser filters -->
<string name="filter_everything">Everything</string>
<string name="filter_documents">Documents</string>
<string name="filter_spreadsheets">Spreadsheets</string>
<string name="filter_presentations">Presentations</string>
<string name="filter_drawings">Drawings</string>
<!-- Document provider names -->
<string name="document_locations">Document locations</string>
<string name="close_document_locations">Close document locations</string>
<string name="local_documents">Documents directory</string>
<string name="local_file_system">Local file system</string>
<string name="external_sd_file_system">External SD</string>
<string name="otg_file_system">OTG device (experimental)</string>
<string name="usb_connected_configure">USB connected, configure your device.</string>
<string name="ext_document_provider_error">Invalid root file. Check your sd card configuration.</string>
<string name="legacy_extsd_missing_error">Invalid root file. Check your external sd card and/or configuration.</string>
<string name="otg_missing_error">Invalid root file. Check your OTG device and/or configuration.</string>
<!-- Edit action names -->
<string name="action_bold">Bold</string>
@@ -100,25 +57,10 @@
<string name="message_saving_failed">Saving the document failed.</string>
<string name="message_save_incomplete">Save incomplete. Were there any changes?</string>
<!-- Document provider settings -->
<string name="storage_provider_settings">Storage provider settings</string>
<string name="physical_storage_settings">Physical storage settings</string>
<string name="external_sd_path">External SD path</string>
<string name="otg_device_path">OTG device path</string>
<string name="otg_warning">Experimental Feature: Use only if OTG device is writable.</string>
<string name="password">Password</string>
<string name="action_undo">Undo</string>
<string name="action_redo">Redo</string>
<!-- Directory browser strings -->
<string name="up_description">To parent directory</string>
<string name="confirm_label">Confirm</string>
<string name="cancel_label">Cancel</string>
<string name="search_label">Go</string>
<string name="directory_browser_label">Choose Directory</string>
<string name="bad_directory">Invalid directory path</string>
<string name="current_dir">Current Directory: %1$s</string>
<!-- Save Alert dialog strings -->
<string name="save_alert_dialog_title">Save the document before closing?</string>
<string name="save_document">SAVE</string>
@@ -207,12 +149,6 @@
<string name="alert_cancel">Cancel</string>
<string name="unable_to_go_further">Unable to go further.</string>
<string name="current_uno_command">Current UNO command</string>
<string name="pref_sort_summary">Select how to order files: A-Z, by size or by date.</string>
<string name="pref_viewmode_summary">View files as a grid or in a list.</string>
<string name="pref_file_explorer_title">File explorer layout</string>
<string name="pref_sort_title">File Order</string>
<string name="pref_filter_title">Default File Filter</string>
<string name="pref_filter_summary">Set which file filter should be used by default.</string>
<string name="display_language">Display Language</string>
<string name="display_language_summary">Set the default display language</string>
<string name="unable_to_export_pdf">Unable to export to pdf</string>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This file is part of the LibreOffice project.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory
android:title="@string/physical_storage_settings">
<PreferenceScreen
android:title="@string/external_sd_path"
android:key="pref_extsd_path_uri">
</PreferenceScreen>
<PreferenceScreen
android:title="@string/otg_device_path"
android:key="pref_otg_path_uri"
android:summary="@string/otg_warning">
</PreferenceScreen>
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -1,36 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory
android:title="@string/pref_category_explorer"
android:key="PREF_CATEGORY_EXPLORER">
<ListPreference
android:title="@string/pref_filter_title"
android:summary="@string/pref_filter_summary"
android:entries="@array/FilterTypeNames"
android:entryValues="@array/FilterTypeStringValues"
android:defaultValue="-1"
android:key="FILTER_MODE"/>
<ListPreference
android:summary="@string/pref_sort_summary"
android:key="SORT_MODE"
android:title="@string/pref_sort_title"
android:entries="@array/SortModeNames"
android:defaultValue="0"
android:entryValues="@array/SortModeStringValues"/>
<ListPreference
android:entries="@array/ViewModeNames"
android:entryValues="@array/ViewModeStringValues"
android:defaultValue="@integer/grid_view_integer"
android:title="@string/pref_file_explorer_title"
android:key="EXPLORER_VIEW_TYPE"
android:summary="@string/pref_viewmode_summary" />
<CheckBoxPreference
android:title="@string/pref_show_hidden_files"
android:key="ENABLE_SHOW_HIDDEN_FILES"
android:summary="@string/pref_show_hidden_files_summary"
android:defaultValue="false" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/pref_category_general"
android:key="PREF_CATEGORY_GENERAL">

View File

@@ -1,6 +1,5 @@
package org.libreoffice;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
@@ -11,14 +10,11 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.AssetFileDescriptor;
import android.content.res.AssetManager;
import android.database.Cursor;
import android.graphics.RectF;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.OpenableColumns;
import android.support.design.widget.BottomSheetBehavior;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.DrawerLayout;
@@ -38,8 +34,6 @@ import android.widget.Toast;
import org.libreoffice.overlay.CalcHeadersController;
import org.libreoffice.overlay.DocumentOverlay;
import org.libreoffice.storage.DocumentProviderFactory;
import org.libreoffice.storage.IFile;
import org.libreoffice.ui.FileUtilities;
import org.libreoffice.ui.LibreOfficeUIActivity;
import org.mozilla.gecko.gfx.GeckoLayerClient;
@@ -51,7 +45,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
@@ -80,9 +73,6 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
private static boolean mIsDeveloperMode;
private static boolean mbISReadOnlyMode;
private int providerId;
private URI documentUri;
private DrawerLayout mDrawerLayout;
Toolbar toolbarTop;
@@ -129,7 +119,6 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
private static boolean isDocumentChanged = false;
private boolean isUNOCommandsToolbarOpen = false;
private boolean isNewDocument = false;
private long lastModified = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -212,13 +201,9 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
} else if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_FILE)) {
mInputFile = new File(getIntent().getData().getPath());
mbISReadOnlyMode = true;
Log.d(LOGTAG, "SCHEME_FILE: getPath(): " + getIntent().getData().getPath());
toolbarTop.setTitle(mInputFile.getName());
// Gather data to rebuild IFile object later
providerId = getIntent().getIntExtra(
"org.libreoffice.document_provider_id", 0);
documentUri = (URI) getIntent().getSerializableExtra(
"org.libreoffice.document_uri");
}
} else {
mInputFile = new File(DEFAULT_DOC_PATH);
@@ -234,8 +219,6 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
mDrawerList.setOnItemClickListener(new DocumentPartClickListener());
}
lastModified = mInputFile.lastModified();
mToolbarController.setupToolbars();
TabHost host = findViewById(R.id.toolbarTabHost);
@@ -363,14 +346,12 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
}
/**
* Save the document and invoke save on document provider to upload the file
* to the cloud if necessary.
* Save the document.
*/
public void saveDocument() {
if (!mInputFile.exists()) {
// Needed for handling null in case new document is not created.
mInputFile = new File(DEFAULT_DOC_PATH);
lastModified = mInputFile.lastModified();
}
Toast.makeText(this, R.string.message_saving, Toast.LENGTH_SHORT).show();
// local save
@@ -378,98 +359,51 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
}
public void saveFileToOriginalSource() {
if (documentUri != null) {
// case where file was opened using IDocumentProvider from within LO app
saveFilesToCloud();
} else {
// case where file URI was passed via Intent
if (isReadOnlyMode() || mInputFile == null || getIntent().getData() == null || !getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT))
return;
if (isReadOnlyMode() || mInputFile == null || getIntent().getData() == null || !getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT))
return;
Uri uri = getIntent().getData();
FileInputStream inputStream = null;
OutputStream outputStream = null;
Uri uri = getIntent().getData();
FileInputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = new FileInputStream(mInputFile);
// OutputStream for the actual (original) location
outputStream = getContentResolver().openOutputStream(uri);
try {
inputStream = new FileInputStream(mInputFile);
// OutputStream for the actual (original) location
outputStream = getContentResolver().openOutputStream(uri);
byte[] buffer = new byte[4096];
int readBytes = inputStream.read(buffer);
while (readBytes != -1) {
outputStream.write(buffer, 0, readBytes);
readBytes = inputStream.read(buffer);
}
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LibreOfficeMainActivity.this, R.string.message_saved,
Toast.LENGTH_SHORT).show();
}
});
setDocumentChanged(false);
} catch (Exception e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LibreOfficeMainActivity.this, R.string.message_saving_failed,
Toast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
if (outputStream != null)
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void saveFilesToCloud(){
final Activity activity = LibreOfficeMainActivity.this;
final AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
// rebuild the IFile object from the data passed in the Intent
IFile mStorageFile = DocumentProviderFactory.getInstance()
.getProvider(providerId).createFromUri(LibreOfficeMainActivity.this, documentUri);
// call document provider save operation
mStorageFile.saveDocument(mInputFile);
}
catch (final RuntimeException e) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, e.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
Log.e(LOGTAG, e.getMessage(), e.getCause());
}
return null;
byte[] buffer = new byte[4096];
int readBytes = inputStream.read(buffer);
while (readBytes != -1) {
outputStream.write(buffer, 0, readBytes);
readBytes = inputStream.read(buffer);
}
@Override
protected void onPostExecute(Void param) {
Toast.makeText(activity, R.string.message_saved,
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LibreOfficeMainActivity.this, R.string.message_saved,
Toast.LENGTH_SHORT).show();
setDocumentChanged(false);
}
});
setDocumentChanged(false);
} catch (Exception e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(LibreOfficeMainActivity.this, R.string.message_saving_failed,
Toast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
} finally {
try {
if (inputStream != null)
inputStream.close();
if (outputStream != null)
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
};
if (lastModified < mInputFile.lastModified()) {
task.execute();
lastModified = mInputFile.lastModified();
} else {
Toast.makeText(activity, R.string.message_save_incomplete, Toast.LENGTH_LONG).show();
}
}

View File

@@ -1,125 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage;
import java.util.HashSet;
import java.util.Set;
import org.libreoffice.storage.external.ExtsdDocumentsProvider;
import org.libreoffice.storage.external.OTGDocumentsProvider;
import org.libreoffice.storage.local.LocalDocumentsDirectoryProvider;
import org.libreoffice.storage.local.LocalDocumentsProvider;
import android.content.Context;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
/**
* Keeps the instances of the available IDocumentProviders in the system.
* Instances are maintained in a sorted list and providers have to be
* accessed from their position.
*
* The factory follows the Singleton pattern, there is only one instance of it
* in the application and it must be retrieved with
* DocumentProviderFactory.getInstance().
*/
public final class DocumentProviderFactory {
public static int EXTSD_PROVIDER_INDEX = 2;
public static int OTG_PROVIDER_INDEX = 3;
/**
* Private factory instance for the Singleton pattern.
*/
private static DocumentProviderFactory instance = null;
private IDocumentProvider[] providers;
private String[] providerNames;
private DocumentProviderFactory() {
// private to prevent external instances of the factory
}
/**
* Initializes the factory with some context. If this method is called for
* twice or more times those calls will have no effect.
*
* @param context
* Application context for the factory.
*/
public static void initialize(Context context) {
if (instance == null) {
// initialize instance
instance = new DocumentProviderFactory();
// initialize document providers list
instance.providers = new IDocumentProvider[4];
instance.providers[0] = new LocalDocumentsDirectoryProvider(0);
instance.providers[1] = new LocalDocumentsProvider(1);
instance.providers[OTG_PROVIDER_INDEX] = new OTGDocumentsProvider(OTG_PROVIDER_INDEX, context);
instance.providers[EXTSD_PROVIDER_INDEX] = new ExtsdDocumentsProvider(EXTSD_PROVIDER_INDEX, context);
// initialize document provider names list
instance.providerNames = new String[instance.providers.length];
for (int i = 0; i < instance.providers.length; i++) {
instance.providerNames[i] = context.getString(instance
.getProvider(i).getNameResource());
}
}
}
/**
* Retrieve the unique instance of the factory.
*
* @return the unique factory object or null if it is not yet initialized.
*/
public static DocumentProviderFactory getInstance() {
return instance;
}
/**
* Retrieve the provider associated to a certain id.
*
* @param id
* @return document provider with that id.
*/
public IDocumentProvider getProvider(int id) {
// as for now, id == position in providers array
return providers[id];
}
/**
* Returns a sorted list of the names of the providers. Order is meaningful
* to retrieve the actual provider object with getProvider().
*
* @return Array with the names of the available providers.
*/
public String[] getNames() {
return providerNames;
}
/**
* Returns the default provider.
*
* @return default provider.
*/
public IDocumentProvider getDefaultProvider() {
return providers[0];
}
public Set<OnSharedPreferenceChangeListener> getChangeListeners() {
Set<OnSharedPreferenceChangeListener> listeners =
new HashSet<OnSharedPreferenceChangeListener>();
for (IDocumentProvider provider : providers) {
if (provider instanceof OnSharedPreferenceChangeListener)
listeners.add((OnSharedPreferenceChangeListener) provider);
}
return listeners;
}
}

View File

@@ -1,99 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage;
import java.util.Set;
import org.libreoffice.R;
import org.libreoffice.storage.external.BrowserSelectorActivity;
import android.content.Intent;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.support.v7.app.AppCompatActivity;
public class DocumentProviderSettingsActivity extends AppCompatActivity {
public static final String KEY_PREF_EXTERNAL_SD_PATH_URI = "pref_extsd_path_uri";
public static final String KEY_PREF_OTG_PATH_URI = "pref_otg_path_uri";
private Set<OnSharedPreferenceChangeListener> listeners;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment()).commit();
}
@Override
protected void onResume() {
super.onResume();
listeners = DocumentProviderFactory.getInstance().getChangeListeners();
for (OnSharedPreferenceChangeListener listener : listeners) {
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(listener);
}
}
@Override
protected void onPause() {
super.onPause();
for (OnSharedPreferenceChangeListener listener : listeners) {
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(listener);
}
}
public static class SettingsFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.documentprovider_preferences);
PreferenceScreen extSDPreference =
(PreferenceScreen)findPreference(KEY_PREF_EXTERNAL_SD_PATH_URI);
PreferenceScreen otgPreference =
(PreferenceScreen)findPreference(KEY_PREF_OTG_PATH_URI);
extSDPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startBrowserSelectorActivity(KEY_PREF_EXTERNAL_SD_PATH_URI,
BrowserSelectorActivity.MODE_EXT_SD);
return true;
}
});
otgPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
startBrowserSelectorActivity(KEY_PREF_OTG_PATH_URI,
BrowserSelectorActivity.MODE_OTG);
return true;
}
});
}
private void startBrowserSelectorActivity(String prefKey, String mode) {
Intent i = new Intent(getActivity(), BrowserSelectorActivity.class);
i.putExtra(BrowserSelectorActivity.PREFERENCE_KEY_EXTRA, prefKey);
i.putExtra(BrowserSelectorActivity.MODE_EXTRA, mode);
startActivity(i);
}
}
}

View File

@@ -1,69 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage;
import android.content.Context;
import java.net.URI;
/**
* Represents a Document Provider, an object able to provide documents from a
* certain source (e.g. local documents, DropBox, Google Docs).
*/
public interface IDocumentProvider {
/**
* Provides the content root element for the Document Provider.
*
* @return Content root element.
* @throws RuntimeException in case of error.
* @param context
*/
IFile getRootDirectory(Context context);
/**
* Transforms some URI into the IFile object that represents that content.
*
*
* @param context
* @param uri
* URI pointing to some content object that has been previously
* retrieved with IFile.getUri().
* @return IFile object pointing to the content represented by uri.
* @throws RuntimeException in case of error.
*/
IFile createFromUri(Context context, URI uri);
/**
* Get internationalized name for this provider. This name is intended to be
* shown in the UI.
*
* @return string resource pointing to the provider name.
*/
int getNameResource();
/**
* Provides the unique ID for a document provider instance in a program.
*
* This ID should be set when the instance is built. It could be used to
* tell two instances of the same document provider apart.
*
* @return Unique ID for a document provider instance.
*/
int getId();
/**
* Checks if the Document Provider is available or not.
*
* @return A boolean value based on provider availability.
* @param context
*/
boolean checkProviderAvailability(Context context);
}

View File

@@ -1,116 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage;
import android.content.Context;
import java.io.File;
import java.io.FileFilter;
import java.net.URI;
import java.util.Date;
import java.util.List;
/**
* An abstraction of the File class, intended to be implemented by different
* Document Providers.
*
* It represents a file or a directory in the context of a certain Document
* Provider. It wraps the file-related operations and provides access to the
* final document as a local File, downloading it if necessary.
*/
public interface IFile {
/**
* Provides a URI that represents this IFile object.
*
* @return URI that represents this IFile object in the context of the
* Document Provider that created it. The URI can be transformed
* back into an IFile object with IDocumentProvider.createFromUri().
*/
URI getUri();
/**
* Returns the name of the file or directory represented by this file.
*
* @return This file's name.
*/
String getName();
/**
* Indicates if this file represents a directory in the context of the
* Document Provider which originated it.
*
* @return true if this file is a directory, false otherwise.
*/
boolean isDirectory();
/**
* Returns the file size in bytes.
*
* @return file size in bytes, 0 if the file does not exist.
*/
long getSize();
/**
* Returns the time when this file was last modified, measured in
* milliseconds since January 1st, 1970, midnight.
*
* @return time when this file was last modified, or 0 if the file does not
* exist.
*/
Date getLastModified();
/**
* Returns a list containing the files in the directory represented by this
* file.
*
* @return list of files contained by this directory, or an empty list if
* this is not a directory.
* @throws RuntimeException in case of error.
*/
List<IFile> listFiles();
/**
* Gets the list of files contained in the directory represented by this
* file, and filters it through some FilenameFilter.
*
* @param filter
* the filter to match names against.
* @return filtered list of files contained by this directory, or an empty
* list if this is not a directory.
* @throws RuntimeException in case of error.
*/
List<IFile> listFiles(FileFilter filter);
/**
* Returns the pparent of this file.
*
* @return this file's parent or null if it does not have it.
* @param context
*/
IFile getParent(Context context);
/**
* Returns the document wrapped by this IFile as a local file. The result
* for a directory is not defined.
*
* @return local file containing the document wrapped by this object.
* @throws RuntimeException in case of error.
*/
File getDocument();
/**
* Replaces the wrapped document with a new version of it.
*
* @param file
* A local file pointing to the new version of the document.
*/
void saveDocument(File file);
}

View File

@@ -1,56 +0,0 @@
package org.libreoffice.storage;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
/**
* File IO related methods.
*/
public class IOUtils {
private static final int BUFFER_SIZE = 1024 * 8;
private static final String LOGTAG = IOUtils.class.getSimpleName();
public static File getFileFromURIString(String URIpath) throws IllegalArgumentException{
try{
return new File(new URI(URIpath));
} catch (URISyntaxException e) {
//should not happen as all URIs are system generated
Log.wtf(LOGTAG, e.getReason());
return null;
}
}
public static boolean isInvalidFile(File f) {
return f == null || !f.exists() || f.getTotalSpace() == 0
|| !f.canRead() || !f.canWrite();
}
public static int copy(InputStream input, OutputStream output) throws Exception {
byte[] buffer = new byte[BUFFER_SIZE];
BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
BufferedOutputStream out = new BufferedOutputStream(output, BUFFER_SIZE);
int count = 0, n = 0;
try {
while ((n = in.read(buffer, 0, BUFFER_SIZE)) != -1) {
out.write(buffer, 0, n);
count += n;
}
out.flush();
} finally {
if (out != null) out.close();
if (in != null) in.close();
}
return count;
}
}

View File

@@ -1,153 +0,0 @@
package org.libreoffice.storage.external;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.UriPermission;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import org.libreoffice.R;
import org.libreoffice.storage.DocumentProviderFactory;
import java.util.Set;
/**
* Activity to select which directory browser to use.
* Android 5+ will use the DocumentTree intent to locate a browser.
* Android 4+ & OTG will use the internal directory browser.
*/
public class BrowserSelectorActivity extends AppCompatActivity {
public static final String PREFERENCE_KEY_EXTRA = "org.libreoffice.pref_key_extra";
public static final String MODE_EXTRA = "org.libreoffice.mode_extra";
public static final String MODE_OTG = "OTG";
public static final String MODE_EXT_SD = "EXT_SD";
private static final String LOGTAG = BrowserSelectorActivity.class.getSimpleName();
private static final int REQUEST_DOCUMENT_TREE = 1;
private static final int REQUEST_INTERNAL_BROWSER = 2;
private Set<SharedPreferences.OnSharedPreferenceChangeListener> listeners;
private String preferenceKey;
private SharedPreferences preferences;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
preferenceKey = getIntent().getStringExtra(PREFERENCE_KEY_EXTRA);
preferences = PreferenceManager.getDefaultSharedPreferences(this);
String mode = getIntent().getStringExtra(MODE_EXTRA);
if(mode.equals(MODE_EXT_SD)) {
findSDCard();
} else if (mode.equals(MODE_OTG)) {
findOTGDevice();
}
}
private void findOTGDevice() {
useInternalBrowser(DocumentProviderFactory.OTG_PROVIDER_INDEX);
}
private void findSDCard() {
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
useDocumentTreeBrowser();
} else {
useInternalBrowser(DocumentProviderFactory.EXTSD_PROVIDER_INDEX);
}
}
private void useInternalBrowser(int providerIndex) {
IExternalDocumentProvider provider =
(IExternalDocumentProvider) DocumentProviderFactory.getInstance()
.getProvider(providerIndex);
String previousDirectoryPath = preferences.getString(preferenceKey, provider.guessRootURI(this));
Intent i = new Intent(this, DirectoryBrowserActivity.class);
i.putExtra(DirectoryBrowserActivity.DIRECTORY_PATH_EXTRA, previousDirectoryPath);
startActivityForResult(i, REQUEST_INTERNAL_BROWSER);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void useDocumentTreeBrowser() {
Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(i, REQUEST_DOCUMENT_TREE);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//listeners are registered here as onActivityResult is called before onResume
super.onActivityResult(requestCode, resultCode, data);
registerListeners();
if(resultCode == RESULT_OK) {
switch(requestCode) {
case REQUEST_DOCUMENT_TREE:
Uri treeUri = data.getData();
preferences.edit()
.putString(preferenceKey, treeUri.toString())
.apply();
updatePersistedUriPermission(treeUri);
getContentResolver().takePersistableUriPermission(treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
break;
case REQUEST_INTERNAL_BROWSER:
Uri fileUri = data.getData();
preferences.edit()
.putString(preferenceKey, fileUri.toString())
.apply();
break;
default:
}
}
unregisterListeners();
Log.d(LOGTAG, "Preference saved: " +
preferences.getString(preferenceKey, getString(R.string.directory_not_saved)));
finish();
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updatePersistedUriPermission(Uri treeUri) {
freePreviousUriPermissions();
//TODO: Use non-emulator Android 5+ device to check if needed
/*this.grantUriPermission(this.getPackageName(),
treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION); */
getContentResolver().takePersistableUriPermission(treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION |
Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void freePreviousUriPermissions() {
ContentResolver cr = getContentResolver();
for (UriPermission uriPermission : cr.getPersistedUriPermissions()) {
cr.releasePersistableUriPermission(uriPermission.getUri(), 0);
}
}
private void registerListeners() {
listeners = DocumentProviderFactory.getInstance().getChangeListeners();
for (SharedPreferences.OnSharedPreferenceChangeListener listener : listeners) {
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(listener);
}
}
private void unregisterListeners() {
for (SharedPreferences.OnSharedPreferenceChangeListener listener : listeners) {
PreferenceManager.getDefaultSharedPreferences(this)
.unregisterOnSharedPreferenceChangeListener(listener);
}
}
}

View File

@@ -1,42 +0,0 @@
package org.libreoffice.storage.external;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import org.libreoffice.R;
/**
* Container for DirectoryBrowserFragment
*/
public class DirectoryBrowserActivity extends AppCompatActivity {
public static final String DIRECTORY_PATH_EXTRA = "org.libreoffice.directory_path_extra";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent data = getIntent();
String initialPath = data.getStringExtra(DIRECTORY_PATH_EXTRA);
setContentView(R.layout.activity_directory_browser);
FragmentManager fm = getFragmentManager();
Fragment fragment = DirectoryBrowserFragment.newInstance(initialPath);
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
@Override
public void onBackPressed() {
FragmentManager fm = getFragmentManager();
if(fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
} else {
super.onBackPressed();
}
}
}

View File

@@ -1,199 +0,0 @@
package org.libreoffice.storage.external;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import org.libreoffice.R;
import org.libreoffice.storage.IOUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.Comparator;
/**
* A simple directory browser.
*/
public class DirectoryBrowserFragment extends Fragment {
private static final String LOGTAG = DirectoryBrowserFragment.class.getSimpleName();
private static final String INITIAL_PATH_URI_KEY = "initial_path";
private File currentDirectory;
private FileArrayAdapter directoryAdapter;
public static DirectoryBrowserFragment newInstance(String initialPathURI) {
Bundle args = new Bundle();
args.putString(INITIAL_PATH_URI_KEY, initialPathURI);
DirectoryBrowserFragment fragment = new DirectoryBrowserFragment();
fragment.setArguments(args);
Log.d(LOGTAG, "Saved path: " + initialPathURI);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String initialPathURI = getArguments().getString(INITIAL_PATH_URI_KEY);
setupCurrentDirectory(initialPathURI);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_directory_browser, container, false);
final EditText directoryHeader = v.findViewById(R.id.directory_header);
Button directorySearchButton = v.findViewById(R.id.directory_search_button);
Button positiveButton = v.findViewById(R.id.confirm_button);
Button negativeButton = v.findViewById(R.id.cancel_button);
ImageView upImage = v.findViewById(R.id.up_image);
ListView directoryListView = v.findViewById(R.id.directory_list);
directoryHeader.setText(currentDirectory.getPath());
directorySearchButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String currentPath = currentDirectory.getAbsolutePath();
String enteredPath = directoryHeader.getText().toString();
File testDirectory = new File(enteredPath);
if(enteredPath.equals(currentPath)) ;
else if (isInvalidFileDirectory(testDirectory)) {
Toast.makeText(getActivity(), R.string.bad_directory, Toast.LENGTH_SHORT)
.show();
}
else {
changeDirectory(testDirectory);
}
}
});
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent data = new Intent();
data.setData(Uri.fromFile(currentDirectory));
getActivity().setResult(Activity.RESULT_OK, data);
getActivity().finish();
}
});
negativeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getActivity().setResult(Activity.RESULT_CANCELED, null);
getActivity().finish();
}
});
upImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeDirectory(currentDirectory.getParentFile());
}
});
directoryAdapter = new FileArrayAdapter(getActivity(), new ArrayList<File>());
directoryAdapter.populateFileList(currentDirectory);
directoryListView.setAdapter(directoryAdapter);
directoryListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
changeDirectory(directoryAdapter.getItem(position));
}
});
return v;
}
private void changeDirectory(File destination) {
if(destination == null) {
Toast.makeText(getActivity(), R.string.unable_to_go_further, Toast.LENGTH_SHORT)
.show();
} else {
Fragment fragment = DirectoryBrowserFragment.newInstance(destination.toURI().toString());
getActivity().getFragmentManager().beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
}
}
private void setupCurrentDirectory(String initialPathURI) {
File initialDirectory = null;
if(initialPathURI != null && !initialPathURI.isEmpty()) {
initialDirectory = IOUtils.getFileFromURIString(initialPathURI);
}
if(isInvalidFileDirectory(initialDirectory)) {
initialDirectory = Environment.getExternalStorageDirectory();
}
currentDirectory = initialDirectory;
}
private boolean isInvalidFileDirectory(File f) {
return f == null || !f.exists() || !f.isDirectory() ||!f.canRead();
}
private class FileArrayAdapter extends ArrayAdapter<File> {
private Comparator<File> caseInsensitiveNaturalOrderComparator;
public FileArrayAdapter(Context context, ArrayList<File> files) {
super(context, 0, files);
caseInsensitiveNaturalOrderComparator = new AlphabeticalFileComparator();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = getActivity().getLayoutInflater()
.inflate(android.R.layout.simple_list_item_1, null);
}
File f = this.getItem(position);
TextView tv = convertView.findViewById(android.R.id.text1);
tv.setText(f.getName());
return convertView;
}
public void sortAlphabetically() {
this.sort(caseInsensitiveNaturalOrderComparator);
}
public void populateFileList(File directory) {
for(File f : directory.listFiles()){
if(f.isDirectory()){
directoryAdapter.add(f);
}
}
directoryAdapter.sortAlphabetically();
}
}
private class AlphabeticalFileComparator implements Comparator<File> {
@Override
public int compare(File lhs, File rhs) {
String lhsName = lhs.getName();
String rhsName = rhs.getName();
return lhsName.compareToIgnoreCase(rhsName);
}
}
}

View File

@@ -1,163 +0,0 @@
package org.libreoffice.storage.external;
import android.content.Context;
import android.support.v4.provider.DocumentFile;
import android.util.Log;
import org.libreoffice.storage.IFile;
import org.libreoffice.storage.IOUtils;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Implementation of IFile for the external file system, for Android 4.4+
*
* Uses the DocumentFile class.
*
* The DocumentFile class obfuscates the path of the files it wraps,
* preventing usage of LOK's documentLoad method. A copy of the DocumentFile's contents
* will be created in the cache when files are opened, allowing use of documentLoad.
*/
public class ExternalFile implements IFile{
private final static String LOGTAG = "ExternalFile";
private ExtsdDocumentsProvider provider;
private DocumentFile docFile;
private File duplicateFile;
private Context context;
public ExternalFile(ExtsdDocumentsProvider provider, DocumentFile docFile, Context context) {
this.provider = provider;
this.context = context;
this.docFile = docFile;
}
@Override
public URI getUri() {
try{
return new URI(docFile.toString());
} catch (URISyntaxException e) {
Log.e(LOGTAG, e.getMessage(), e.getCause());
return null;
}
}
@Override
public String getName() {
return docFile.getName();
}
@Override
public boolean isDirectory() {
return docFile.isDirectory();
}
@Override
public long getSize() {
return docFile.length();
}
@Override
public Date getLastModified() {
return new Date(docFile.lastModified());
}
@Override
public List<IFile> listFiles() {
List<IFile> children = new ArrayList<IFile>();
for (DocumentFile child : docFile.listFiles()) {
children.add(new ExternalFile(provider, child, context));
}
return children;
}
@Override
public List<IFile> listFiles(FileFilter filter) {
File file;
try{
List<IFile> children = new ArrayList<IFile>();
for (DocumentFile child : docFile.listFiles()) {
file = new File(new URI(child.getUri().toString()));
if(filter.accept(file))
children.add(new ExternalFile(provider, child, context));
}
return children;
}catch (Exception e){
e.printStackTrace();
}
/* if something goes wrong */
return listFiles();
}
@Override
public IFile getParent(Context context) {
// this is the root node
if(docFile.getParentFile() == null) return null;
return new ExternalFile(provider, docFile.getParentFile(), this.context);
}
@Override
public File getDocument() {
if(isDirectory()) {
return null;
} else {
duplicateFile = duplicateInCache();
return duplicateFile;
}
}
private File duplicateInCache() {
try{
InputStream istream = context.getContentResolver().
openInputStream(docFile.getUri());
File storageFolder = provider.getCacheDir();
File fileCopy = new File(storageFolder, docFile.getName());
OutputStream ostream = new FileOutputStream(fileCopy);
IOUtils.copy(istream, ostream);
return fileCopy;
} catch (Exception e) {
Log.e(LOGTAG, e.getMessage(), e.getCause());
return null;
}
}
@Override
public void saveDocument(File file) {
try{
OutputStream ostream = context.getContentResolver().
openOutputStream(docFile.getUri());
InputStream istream = new FileInputStream(file);
IOUtils.copy(istream, ostream);
} catch (Exception e) {
Log.e(LOGTAG, e.getMessage(), e.getCause());
}
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (!(object instanceof ExternalFile))
return false;
ExternalFile file = (ExternalFile) object;
return file.getUri().equals(getUri());
}
}

View File

@@ -1,175 +0,0 @@
package org.libreoffice.storage.external;
import android.Manifest;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
import android.support.v4.provider.DocumentFile;
import android.util.Log;
import org.libreoffice.R;
import org.libreoffice.storage.DocumentProviderSettingsActivity;
import org.libreoffice.storage.IFile;
import java.io.File;
import java.net.URI;
/**
* Implementation of IDocumentProvider for the external file system, for android 4.4+
*
* The DocumentFile class is required when accessing files in external storage
* for Android 4.4+. The ExternalFile class is used to handle this.
*
* Android 4.4 & 5+ use different types of root directory paths,
* 5 using a DirectoryTree Uri and 4.4 using a normal File path.
* As such, different methods are required to obtain the rootDirectory IFile.
* 4.4 has to guess the location of the rootDirectory as well.
*/
public class ExtsdDocumentsProvider implements IExternalDocumentProvider,
OnSharedPreferenceChangeListener{
private static final String LOGTAG = ExtsdDocumentsProvider.class.getSimpleName();
private int id;
private File cacheDir;
private String rootPathURI;
public ExtsdDocumentsProvider(int id, Context context) {
this.id = id;
setupRootPathUri(context);
setupCache(context);
}
private void setupRootPathUri(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
rootPathURI = preferences.getString(
DocumentProviderSettingsActivity.KEY_PREF_EXTERNAL_SD_PATH_URI, guessRootURI(context));
}
public String guessRootURI(Context context) {
// TODO: unfortunately the getExternalFilesDirs function relies on devices to actually
// follow guidelines re external storage. Of course device manufacturers don't and as such
// you cannot rely on it returning the actual paths (neither the compat, nor the native variant)
File[] possibleRemovables = ContextCompat.getExternalFilesDirs(context,null);
// the primary dir that is already covered by the "LocalDocumentsProvider"
// might be emulated/part of internal memory or actual SD card
// TODO: change to not confuse android's "external storage" with "expandable storage"
String primaryExternal = Environment.getExternalStorageDirectory().getAbsolutePath();
for (File option: possibleRemovables) {
// Returned paths may be null if a storage device is unavailable.
if (null == option) {
Log.w(LOGTAG,"path was a null option :-/"); continue; }
String optionPath = option.getAbsolutePath();
if(optionPath.contains(primaryExternal)) {
Log.v(LOGTAG, "did get file path - but is same as primary storage ("+ primaryExternal +")");
continue;
}
return option.toURI().toString();
}
// TODO: do some manual probing of possible directories (/storage/sdcard1 and similar)
Log.i(LOGTAG, "no secondary storage reported");
return null;
}
private void setupCache(Context context) {
// TODO: probably we should do smarter cache management
cacheDir = new File(context.getExternalCacheDir(), "externalFiles");
if (cacheDir.exists()) {
deleteRecursive(cacheDir);
}
cacheDir.mkdirs();
}
private static void deleteRecursive(File file) {
if (file.isDirectory()) {
for (File child : file.listFiles())
deleteRecursive(child);
}
file.delete();
}
public File getCacheDir() {
return cacheDir;
}
@Override
public IFile getRootDirectory(Context context) {
if(android.os.Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
return android4RootDirectory(context);
} else {
return android5RootDirectory(context);
}
}
private ExternalFile android4RootDirectory(Context context) {
try{
File f = new File(new URI(rootPathURI));
return new ExternalFile(this, DocumentFile.fromFile(f), context);
} catch (Exception e) {
//invalid rootPathURI
throw buildRuntimeExceptionForInvalidFileURI(context);
}
}
private ExternalFile android5RootDirectory(Context context) {
try {
return new ExternalFile(this,
DocumentFile.fromTreeUri(context, Uri.parse(rootPathURI)),
context);
} catch (Exception e) {
//invalid rootPathURI
throw buildRuntimeExceptionForInvalidFileURI(context);
}
}
private RuntimeException buildRuntimeExceptionForInvalidFileURI(Context context) {
// ToDo: discarding the original exception / catch-all handling is bad style
return new RuntimeException(context.getString(R.string.ext_document_provider_error));
}
@Override
public IFile createFromUri(Context context, URI javaURI) {
//TODO: refactor when new DocumentFile API exist
//uri must be of a DocumentFile file, not directory.
Uri androidUri = Uri.parse(javaURI.toString());
return new ExternalFile(this,
DocumentFile.fromSingleUri(context, androidUri),
context);
}
@Override
public int getNameResource() {
return R.string.external_sd_file_system;
}
@Override
public int getId() {
return id;
}
@Override
public boolean checkProviderAvailability(Context context) {
// too many devices (or I am just unlucky) don't report the mounted state properly, and other
// devices also consider dedicated part of internal storage to be "mounted" so cannot use
// getExternalStorageState().equals(Environment.MEDIA_MOUNTED) && isExternalStorageRemovable()
// but they refer to the primary external storage anyway, so what currently is covered by the
// "LocalDocumentsProvider"
return rootPathURI!=null && ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key.equals(DocumentProviderSettingsActivity.KEY_PREF_EXTERNAL_SD_PATH_URI)) {
rootPathURI = preferences.getString(key, "");
}
}
}

View File

@@ -1,22 +0,0 @@
package org.libreoffice.storage.external;
import android.content.Context;
import org.libreoffice.storage.IDocumentProvider;
/**
* Interface for external document providers.
*/
public interface IExternalDocumentProvider extends IDocumentProvider {
/**
* Used to obtain the default directory to display when
* browsing using the internal DirectoryBrowser.
*
* @return a guess of the root file's URI.
* @param context
*/
String guessRootURI(Context context);
}

View File

@@ -1,90 +0,0 @@
package org.libreoffice.storage.external;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.preference.PreferenceManager;
import android.util.Log;
import org.libreoffice.R;
import org.libreoffice.storage.DocumentProviderSettingsActivity;
import org.libreoffice.storage.IFile;
import org.libreoffice.storage.IOUtils;
import org.libreoffice.storage.local.LocalFile;
import java.io.File;
import java.net.URI;
/**
* TODO: OTG currently uses LocalFile. Change to an IFile that handles abrupt OTG unmounting
*/
public class OTGDocumentsProvider implements IExternalDocumentProvider,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String LOGTAG = OTGDocumentsProvider.class.getSimpleName();
private String rootPathURI;
private int id;
public OTGDocumentsProvider(int id, Context context) {
this.id = id;
setupRootPath(context);
}
private void setupRootPath(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
rootPathURI = preferences.getString(
DocumentProviderSettingsActivity.KEY_PREF_OTG_PATH_URI, "");
}
@Override
public IFile createFromUri(Context context, URI uri) {
return new LocalFile(uri);
}
@Override
public int getNameResource() {
return R.string.otg_file_system;
}
@Override
public int getId() {
return id;
}
@Override
public IFile getRootDirectory(Context context) {
// TODO: handle this with more fine-grained exceptions
if(rootPathURI.equals("")) {
Log.e(LOGTAG, "rootPathURI is empty");
throw new RuntimeException(context.getString(R.string.ext_document_provider_error));
}
File f = IOUtils.getFileFromURIString(rootPathURI);
if(IOUtils.isInvalidFile(f)) {
Log.e(LOGTAG, "rootPathURI is invalid - missing device?");
throw new RuntimeException(context.getString(R.string.otg_missing_error));
}
return new LocalFile(f);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(DocumentProviderSettingsActivity.KEY_PREF_OTG_PATH_URI)) {
rootPathURI = sharedPreferences.getString(key, "");
}
}
@Override
public String guessRootURI(Context context) {
return "";
}
@Override
public boolean checkProviderAvailability(Context context) {
// check if system supports USB Host
return rootPathURI.length()>0 && context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_USB_HOST);
}
}

View File

@@ -1,73 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage.local;
import java.io.File;
import org.libreoffice.storage.IFile;
import org.libreoffice.R;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
import android.util.Log;
/**
* A convenience IDocumentProvider to browse the /sdcard/Documents directory.
*
* Extends LocalDocumentsProvider to overwrite getRootDirectory and set it to
* /sdcard/Documents. Most documents will probably be stored there so there is
* no need for the user to browse the filesystem from the root every time.
*/
public class LocalDocumentsDirectoryProvider extends LocalDocumentsProvider {
public LocalDocumentsDirectoryProvider(int id) {
super(id);
}
private static File getDocumentsDir() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// DIRECTORY_DOCUMENTS is 19 or later only
return Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
} else {
return new File(Environment.getExternalStorageDirectory() + "/Documents");
}
}
@Override
public IFile getRootDirectory(Context context) {
File documentsDirectory = getDocumentsDir();
if (!documentsDirectory.exists()) {
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
if(!documentsDirectory.mkdirs()) {
// fallback to the toplevel dir - might be due to the dir not mounted/used as USB-Mass-Storage or similar
// TODO: handle unavailability of the storage/failure of the mkdir properly
Log.e("LocalDocumentsProvider", "not sure how we ended up here - if we have read permissions to use it in the first place, we also should have the write-permissions..");
documentsDirectory = Environment.getExternalStorageDirectory();
}
}
}
return new LocalFile(documentsDirectory);
}
@Override
public int getNameResource() {
return R.string.local_documents;
}
@Override
public boolean checkProviderAvailability(Context context) {
File documentsDirectory = getDocumentsDir();
return documentsDirectory.exists() && ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
}

View File

@@ -1,60 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage.local;
import java.net.URI;
import org.libreoffice.storage.IDocumentProvider;
import org.libreoffice.storage.IFile;
import org.libreoffice.R;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Environment;
import android.support.v4.content.ContextCompat;
/**
* Implementation of IDocumentProvider for the local file system.
*/
public class LocalDocumentsProvider implements IDocumentProvider {
private int id;
public LocalDocumentsProvider(int id) {
this.id = id;
}
@Override
public IFile getRootDirectory(Context context) {
return new LocalFile(Environment.getExternalStorageDirectory());
}
@Override
public IFile createFromUri(Context context, URI uri) {
return new LocalFile(uri);
}
@Override
public int getNameResource() {
return R.string.local_file_system;
}
@Override
public int getId() {
return id;
}
@Override
public boolean checkProviderAvailability(Context context) {
return ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
}
}

View File

@@ -1,103 +0,0 @@
/* -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.libreoffice.storage.local;
import android.content.Context;
import java.io.File;
import java.io.FileFilter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.libreoffice.storage.IFile;
/**
* Implementation of IFile for the local file system.
*/
public class LocalFile implements IFile {
private File file;
public LocalFile(File file) {
this.file = file;
}
public LocalFile(URI uri) {
this.file = new File(uri);
}
public URI getUri() {
return file.toURI();
}
public String getName() {
return file.getName();
}
@Override
public boolean isDirectory() {
return file.isDirectory();
}
@Override
public long getSize() {
return file.length();
}
@Override
public Date getLastModified() {
return new Date(file.lastModified());
}
@Override
public List<IFile> listFiles() {
List<IFile> children = new ArrayList<IFile>();
for (File child : file.listFiles()) {
children.add(new LocalFile(child));
}
return children;
}
@Override
public List<IFile> listFiles(FileFilter filter) {
List<IFile> children = new ArrayList<IFile>();
for (File child : file.listFiles(filter)) {
children.add(new LocalFile(child));
}
return children;
}
@Override
public IFile getParent(Context context) {
return new LocalFile(file.getParentFile());
}
@Override
public File getDocument() {
return file;
}
@Override
public boolean equals(Object object) {
if (this == object)
return true;
if (!(object instanceof LocalFile))
return false;
LocalFile file = (LocalFile) object;
return file.getUri().equals(getUri());
}
@Override
public void saveDocument(File file) {
// do nothing; file is local
}
}

View File

@@ -8,17 +8,9 @@
*/
package org.libreoffice.ui;
import org.libreoffice.storage.IFile;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.text.Collator;
import java.util.Map;
import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.Comparator;
import android.content.ContentResolver;
import android.database.Cursor;
@@ -41,17 +33,6 @@ public class FileUtilities {
static final int UNKNOWN = 10;
static final int SORT_AZ = 0;
static final int SORT_ZA = 1;
/** Oldest Files First*/
static final int SORT_OLDEST = 2;
/** Newest Files First*/
static final int SORT_NEWEST = 3;
/** Largest Files First */
static final int SORT_LARGEST = 4;
/** Smallest Files First */
static final int SORT_SMALLEST = 5;
public static final String DEFAULT_WRITER_EXTENSION = ".odt";
public static final String DEFAULT_IMPRESS_EXTENSION = ".odp";
public static final String DEFAULT_SPREADSHEET_EXTENSION = ".ods";
@@ -183,81 +164,6 @@ public class FileUtilities {
return true;
}
static FileFilter getFileFilter(final int mode) {
return new FileFilter() {
public boolean accept(File pathname) {
if (pathname.isDirectory())
return true;
if (lookupExtension(pathname.getName()) == UNKNOWN)
return false;
return doAccept(pathname.getName(), mode, "");
}
};
}
static FilenameFilter getFilenameFilter(final int mode) {
return new FilenameFilter() {
public boolean accept(File dir, String filename) {
if (new File(dir , filename).isDirectory())
return true;
return doAccept(filename, mode, "");
}
};
}
static void sortFiles(List<IFile> files, int sortMode) {
if (files == null)
return;
// Compare filenames in the default locale
final Collator mCollator = Collator.getInstance();
switch (sortMode) {
case SORT_AZ:
Collections.sort(files , new Comparator<IFile>() {
public int compare(IFile lhs, IFile rhs) {
return mCollator.compare(lhs.getName(), rhs.getName());
}
});
break;
case SORT_ZA:
Collections.sort(files , new Comparator<IFile>() {
public int compare(IFile lhs, IFile rhs) {
return mCollator.compare(rhs.getName(), lhs.getName());
}
});
break;
case SORT_OLDEST:
Collections.sort(files , new Comparator<IFile>() {
public int compare(IFile lhs, IFile rhs) {
return lhs.getLastModified().compareTo(rhs.getLastModified());
}
});
break;
case SORT_NEWEST:
Collections.sort(files , new Comparator<IFile>() {
public int compare(IFile lhs, IFile rhs) {
return rhs.getLastModified().compareTo(lhs.getLastModified());
}
});
break;
case SORT_LARGEST:
Collections.sort(files , new Comparator<IFile>() {
public int compare(IFile lhs, IFile rhs) {
return Long.valueOf(rhs.getSize()).compareTo(lhs.getSize());
}
});
break;
case SORT_SMALLEST:
Collections.sort(files , new Comparator<IFile>() {
public int compare(IFile lhs, IFile rhs) {
return Long.valueOf(lhs.getSize()).compareTo(rhs.getSize());
}
});
break;
default:
Log.e(LOGTAG, "uncatched sortMode: " + sortMode);
}
}
static boolean isHidden(File file) {
return file.getName().startsWith(".");
}
@@ -266,20 +172,6 @@ public class FileUtilities {
return isHidden(file) && file.getName().endsWith(".png");
}
static boolean hasThumbnail(File file) {
String filename = file.getName();
if (lookupExtension(filename) == DOC) // only do this for docs for now
{
// Will need another method to check if Thumb is up-to-date - or extend this one?
return new File(file.getParent(), getThumbnailName(file)).isFile();
}
return true;
}
static String getThumbnailName(File file) {
return "." + file.getName().split("[.]")[0] + ".png" ;
}
/**
* Tries to retrieve the display (which should be the document name)
* for the given URI using the given resolver.