diff options
17 files changed, 586 insertions, 68 deletions
diff --git a/android/Bootstrap/src/org/libreoffice/kit/Document.java b/android/Bootstrap/src/org/libreoffice/kit/Document.java index a7d3f04938bb..7cf99413644c 100644 --- a/android/Bootstrap/src/org/libreoffice/kit/Document.java +++ b/android/Bootstrap/src/org/libreoffice/kit/Document.java @@ -147,7 +147,7 @@ public class Document { public native void setClientZoom(int nTilePixelWidth, int nTilePixelHeight, int nTileTwipWidth, int nTileTwipHeight); - private native void saveAs(String url, String format, String options); + public native void saveAs(String url, String format, String options); private native void paintTileNative(ByteBuffer buffer, int canvasWidth, int canvasHeight, int tilePositionX, int tilePositionY, int tileWidth, int tileHeight); diff --git a/android/source/build.gradle b/android/source/build.gradle index d5fc5c932be0..302d34a54754 100644 --- a/android/source/build.gradle +++ b/android/source/build.gradle @@ -26,6 +26,7 @@ dependencies { compile files("${liboWorkdir}/UnpackedTarball/owncloud_android_lib/bin/owncloud-android-library.jar") compile 'com.android.support:appcompat-v7:25.1.0' compile 'com.android.support:design:25.1.0' + compile 'com.android.support.constraint:constraint-layout:1.0.2' } android { @@ -64,6 +65,7 @@ android { // ToDo: fix openssl stuff to not block targeting 23 or later targetSdkVersion 22 manifestPlaceholders = [installLocation: "preferExternal"] + vectorDrawables.useSupportLibrary = true } buildTypes { debug { diff --git a/android/source/res/anim/fab_close.xml b/android/source/res/anim/fab_close.xml new file mode 100644 index 000000000000..ba13b0f038be --- /dev/null +++ b/android/source/res/anim/fab_close.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + <scale + android:duration="300" + android:fromXScale="0.8" + android:fromYScale="0.8" + android:interpolator="@android:anim/linear_interpolator" + android:toXScale="0.0" + android:pivotX="50%" + android:pivotY="50%" + android:toYScale="0.0" /> + + <alpha android:fromAlpha="1.0" + android:toAlpha="0.0" + android:interpolator="@android:anim/accelerate_interpolator" + android:duration="300"/> +</set> diff --git a/android/source/res/anim/fab_open.xml b/android/source/res/anim/fab_open.xml new file mode 100644 index 000000000000..9099d8b53c80 --- /dev/null +++ b/android/source/res/anim/fab_open.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android" + android:fillAfter="true"> + <scale + android:duration="300" + android:fromXScale="0.0" + android:fromYScale="0.0" + android:interpolator="@android:anim/linear_interpolator" + android:toXScale="0.8" + android:pivotX="50%" + android:pivotY="50%" + android:toYScale="0.8"/> + + <alpha + android:fromAlpha="0.0" + android:toAlpha="1.0" + android:interpolator="@android:anim/accelerate_interpolator" + android:duration="300"/> +</set> diff --git a/android/source/res/drawable/ic_add_black_24dp.xml b/android/source/res/drawable/ic_add_black_24dp.xml new file mode 100644 index 000000000000..0258249cc482 --- /dev/null +++ b/android/source/res/drawable/ic_add_black_24dp.xml @@ -0,0 +1,9 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24.0" + android:viewportHeight="24.0"> + <path + android:fillColor="#FF000000" + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> +</vector> diff --git a/android/source/res/layout/activity_document_browser.xml b/android/source/res/layout/activity_document_browser.xml index 20ca14125003..fab1b095035e 100644 --- a/android/source/res/layout/activity_document_browser.xml +++ b/android/source/res/layout/activity_document_browser.xml @@ -6,32 +6,45 @@ 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.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" > + android:orientation="vertical"> <!-- The toolbar --> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" - android:layout_width="match_parent" + android:layout_width="0dp" android:layout_height="wrap_content" android:elevation="3dp" android:background="@color/toolbar_background" app:theme="@style/LibreOfficeTheme.Toolbar" tools:theme="@style/LibreOfficeTheme.Toolbar" - app:popupTheme="@style/LibreOfficeTheme"> + app:popupTheme="@style/LibreOfficeTheme" + tools:layout_constraintTop_creator="1" + tools:layout_constraintRight_creator="1" + app:layout_constraintRight_toRightOf="parent" + tools:layout_constraintLeft_creator="1" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintTop_toTopOf="parent"> </android.support.v7.widget.Toolbar> <android.support.v4.widget.DrawerLayout android:id="@+id/drawer_layout" - android:layout_width="match_parent" - android:layout_height="match_parent"> + android:layout_width="0dp" + android:layout_height="0dp" + tools:layout_constraintTop_creator="1" + tools:layout_constraintRight_creator="1" + tools:layout_constraintBottom_creator="1" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toBottomOf="@+id/toolbar" + tools:layout_constraintLeft_creator="1" + app:layout_constraintLeft_toLeftOf="parent"> <!-- The content --> <ScrollView @@ -53,7 +66,7 @@ android:gravity="center_vertical" android:textSize="14sp" android:padding="16dp" - android:textStyle="bold"/> + android:textStyle="bold" /> <!--Recent files--> <android.support.v7.widget.RecyclerView @@ -61,7 +74,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" - android:layout_marginBottom="8dp"/> + android:layout_marginBottom="8dp" /> <TextView android:layout_width="match_parent" @@ -82,19 +95,177 @@ android:background="@color/background_normal" android:orientation="vertical" /> - </LinearLayout> + </LinearLayout> - </ScrollView> + </ScrollView> - <!-- 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" /> + <!-- 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> -</LinearLayout> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/editFAB" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginBottom="16dp" + android:layout_marginEnd="16dp" + android:layout_marginRight="16dp" + android:clickable="true" + app:fabSize="normal" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:backgroundTint="@color/background_normal" + app:srcCompat="@drawable/ic_add_black_24dp" /> + + <LinearLayout + android:id="@+id/writerLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="@+id/editFAB" + app:layout_constraintRight_toRightOf="parent"> + + <TextView + android:id="@+id/newWriterTextView" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:elevation="2dp" + android:gravity="center_horizontal" + android:text="@string/new_write" + android:textSize="18sp" + android:textStyle="bold" + android:background="@color/background_normal" + android:typeface="normal" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/newWriterFAB" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + app:fabSize="normal" + app:backgroundTint="@color/background_normal" + app:srcCompat="@drawable/writer" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/impressLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="@+id/writerLayout" + app:layout_constraintLeft_toLeftOf="@+id/writerLayout" + app:layout_constraintRight_toRightOf="parent"> + + <TextView + android:id="@+id/newImpressTextView" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:elevation="2dp" + android:gravity="center_horizontal" + android:text="@string/new_impress" + android:textSize="18sp" + android:textStyle="bold" + android:background="@color/background_normal" + android:typeface="normal" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/newImpressFAB" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + app:fabSize="normal" + app:backgroundTint="@color/background_normal" + app:srcCompat="@drawable/impress" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/calcLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="@+id/impressLayout" + app:layout_constraintLeft_toLeftOf="@+id/impressLayout" + app:layout_constraintRight_toRightOf="parent"> + + <TextView + android:id="@+id/newCalcTextView" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:elevation="2dp" + android:gravity="center_horizontal" + android:text="@string/new_spreadsheet" + android:background="@color/background_normal" + android:textSize="18sp" + android:textStyle="bold" + android:typeface="normal" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/newCalcFAB" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + app:fabSize="normal" + app:backgroundTint="@color/background_normal" + app:srcCompat="@drawable/calc" /> + + </LinearLayout> + + <LinearLayout + android:id="@+id/drawLayout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal" + android:visibility="invisible" + app:layout_constraintBottom_toTopOf="@+id/calcLayout" + app:layout_constraintLeft_toLeftOf="@+id/calcLayout" + app:layout_constraintRight_toRightOf="parent"> + + <TextView + android:id="@+id/newDrawTextView" + android:layout_width="120dp" + android:layout_height="wrap_content" + android:layout_marginEnd="8dp" + android:layout_marginRight="8dp" + android:elevation="2dp" + android:gravity="center_horizontal" + android:text="@string/new_draw" + android:background="@color/background_normal" + android:textSize="18sp" + android:textStyle="bold" + android:typeface="normal" /> + + <android.support.design.widget.FloatingActionButton + android:id="@+id/newDrawFAB" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:clickable="true" + app:fabSize="normal" + app:backgroundTint="@color/background_normal" + app:srcCompat="@drawable/draw" /> + + </LinearLayout> + +</android.support.constraint.ConstraintLayout> diff --git a/android/source/res/values/strings.xml b/android/source/res/values/strings.xml index 9832d8b72eb2..ce3754ee3d15 100644 --- a/android/source/res/values/strings.xml +++ b/android/source/res/values/strings.xml @@ -16,6 +16,10 @@ <string name="about_moreinfo">More Info</string> <string name="back_again_to_quit">Press back again to quit</string> + <string name="new_impress">New Impress</string> + <string name="new_spreadsheet">New Sheet</string> + <string name="new_draw">New Draw</string> + <string name="browser_app_name">LibreOffice Browser</string> <string name="menu_search">Search</string> <string name="list_view">List</string> @@ -89,6 +93,7 @@ <string name="message_saved">Save complete</string> <string name="message_saving">Saving the document…</string> <string name="message_save_incomplete">Save incomplete. Were there any changes?</string> + <string name="new_file_created">File created in Documents folder</string> <!-- Document provider settings --> <string name="storage_provider_settings">Storage provider settings</string> @@ -117,6 +122,10 @@ <string name="save_document">SAVE</string> <string name="cancel_save_document">CANCEL</string> <string name="no_save_document">NO</string> + <string name="new_write">New Write</string> + + <string name="save_as_success">Saved file -</string> + <string name="save_as_error">Unable to create new file, please check entered file name.</string> </resources> diff --git a/android/source/src/java/org/libreoffice/LOEvent.java b/android/source/src/java/org/libreoffice/LOEvent.java index 23109e8d7f6a..3e058c8c035e 100644 --- a/android/source/src/java/org/libreoffice/LOEvent.java +++ b/android/source/src/java/org/libreoffice/LOEvent.java @@ -34,6 +34,8 @@ public class LOEvent implements Comparable<LOEvent> { public static final int NAVIGATION_CLICK = 13; public static final int UNO_COMMAND = 14; public static final int RESUME = 15; + public static final int LOAD_NEW = 16; + public static final int SAVE_AS = 17; public final int mType; public int mPriority = 0; @@ -42,6 +44,8 @@ public class LOEvent implements Comparable<LOEvent> { public ThumbnailCreator.ThumbnailCreationTask mTask; public int mPartIndex; public String mString; + public String filePath; + public String fileType; public ComposedTileLayer mComposedTileLayer; public String mTouchType; public PointF mDocumentCoordinate; @@ -81,6 +85,19 @@ public class LOEvent implements Comparable<LOEvent> { mPartIndex = value; } + public LOEvent(String filePath, int type) { + mType = type; + mTypeString = "Load"; + this.filePath = filePath; + } + + public LOEvent(String filePath, String fileType, int type) { + mType = type; + mTypeString = "Load New/Save As"; + this.filePath = filePath; + this.fileType = fileType; + } + public LOEvent(int type, int partIndex) { mType = type; mPartIndex = partIndex; diff --git a/android/source/src/java/org/libreoffice/LOKitShell.java b/android/source/src/java/org/libreoffice/LOKitShell.java index 35a8fd009dc2..7b7525722250 100644 --- a/android/source/src/java/org/libreoffice/LOKitShell.java +++ b/android/source/src/java/org/libreoffice/LOKitShell.java @@ -106,8 +106,16 @@ public class LOKitShell { LOKitShell.sendEvent(new LOEvent(LOEvent.CHANGE_PART, part)); } - public static void sendLoadEvent(String inputFile) { - LOKitShell.sendEvent(new LOEvent(LOEvent.LOAD, inputFile)); + public static void sendLoadEvent(String inputFilePath) { + LOKitShell.sendEvent(new LOEvent(inputFilePath, LOEvent.LOAD)); + } + + public static void sendNewDocumentLoadEvent(String newDocumentPath, String newDocumentType) { + LOKitShell.sendEvent(new LOEvent(newDocumentPath, newDocumentType, LOEvent.LOAD_NEW)); + } + + public static void sendSaveAsEvent(String filePath, String fileFormat) { + LOKitShell.sendEvent(new LOEvent(filePath, fileFormat, LOEvent.SAVE_AS)); } public static void sendResumeEvent(String inputFile, int partIndex) { diff --git a/android/source/src/java/org/libreoffice/LOKitThread.java b/android/source/src/java/org/libreoffice/LOKitThread.java index 742b1cb6e34c..31d3b96440ed 100644 --- a/android/source/src/java/org/libreoffice/LOKitThread.java +++ b/android/source/src/java/org/libreoffice/LOKitThread.java @@ -3,9 +3,11 @@ package org.libreoffice; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.RectF; +import android.util.Log; import android.view.KeyEvent; import org.libreoffice.canvas.SelectionHandle; +import org.libreoffice.ui.LibreOfficeUIActivity; import org.mozilla.gecko.gfx.CairoImage; import org.mozilla.gecko.gfx.ComposedTileLayer; import org.mozilla.gecko.gfx.GeckoLayerClient; @@ -171,8 +173,6 @@ class LOKitThread extends Thread { } else { closeDocument(); } - - } /** @@ -189,25 +189,65 @@ class LOKitThread extends Thread { /** * Handle load document event. - * @param filename - filename where the document is located + * @param filePath - filePath to where the document is located */ - private void loadDocument(String filename) { + private void loadDocument(String filePath) { + mLayerClient = mContext.getLayerClient(); + mInvalidationHandler = new InvalidationHandler(mContext); + mTileProvider = TileProviderFactory.create(mContext, mInvalidationHandler, filePath); + + if (mTileProvider.isReady()) { + LOKitShell.showProgressSpinner(mContext); + refresh(); + LOKitShell.hideProgressSpinner(mContext); + } else { + closeDocument(); + } + } + + /** + * Handle load new document event. + * @param filePath - filePath to where new document is to be created + * @param fileType - fileType what type of new document is to be loaded + */ + private void loadNewDocument(String filePath, String fileType) { mLayerClient = mContext.getLayerClient(); mInvalidationHandler = new InvalidationHandler(mContext); - mTileProvider = TileProviderFactory.create(mContext, mInvalidationHandler, filename); + mTileProvider = TileProviderFactory.create(mContext, mInvalidationHandler, fileType); if (mTileProvider.isReady()) { LOKitShell.showProgressSpinner(mContext); refresh(); LOKitShell.hideProgressSpinner(mContext); + + if (fileType.matches(LibreOfficeUIActivity.NEW_WRITER_STRING_KEY)) + mTileProvider.saveDocumentAs(filePath, "odt"); + else if (fileType.matches(LibreOfficeUIActivity.NEW_CALC_STRING_KEY)) + mTileProvider.saveDocumentAs(filePath, "ods"); + else if (fileType.matches(LibreOfficeUIActivity.NEW_IMPRESS_STRING_KEY)) + mTileProvider.saveDocumentAs(filePath, "odp"); + else + mTileProvider.saveDocumentAs(filePath, "odg"); + } else { closeDocument(); } } /** + * Save the currently loaded document. + */ + private void saveDocumentAs(String filePath, String fileType) { + if (mTileProvider == null) { + Log.e(LOGTAG, "Error in saving, Tile Provider instance is null"); + } else { + mTileProvider.saveDocumentAs(filePath, fileType); + } + } + + /** * Close the currently loaded document. */ private void closeDocument() { @@ -223,7 +263,13 @@ class LOKitThread extends Thread { private void processEvent(LOEvent event) { switch (event.mType) { case LOEvent.LOAD: - loadDocument(event.mString); + loadDocument(event.filePath); + break; + case LOEvent.LOAD_NEW: + loadNewDocument(event.filePath, event.fileType); + break; + case LOEvent.SAVE_AS: + saveDocumentAs(event.filePath, event.fileType); break; case LOEvent.RESUME: resumeDocument(event.mString, event.mPartIndex); diff --git a/android/source/src/java/org/libreoffice/LOKitTileProvider.java b/android/source/src/java/org/libreoffice/LOKitTileProvider.java index d0fbe9431de7..6ed73ee32805 100644 --- a/android/source/src/java/org/libreoffice/LOKitTileProvider.java +++ b/android/source/src/java/org/libreoffice/LOKitTileProvider.java @@ -131,6 +131,30 @@ class LOKitTileProvider implements TileProvider { }); } + @Override + public void saveDocumentAs(String filePath, String format) { + String newFilePath = "file://" + filePath; + Log.d("saveFilePathURL", newFilePath); + mDocument.saveAs(newFilePath, format, ""); + if (!mOffice.getError().isEmpty()){ + Log.e("Save Error", mOffice.getError()); + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + // There was some error + mContext.showSaveStatusToast(true); + } + }); + } + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + // There was no error + mContext.showSaveStatusToast(false); + } + }); + } + private void setupDocumentFonts() { String values = mDocument.getCommandValues(".uno:CharFontName"); if (values == null || values.isEmpty()) diff --git a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java index 02129e50a7e8..fb41762af342 100755 --- a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java @@ -10,8 +10,10 @@ import android.content.SharedPreferences; import android.content.res.AssetFileDescriptor; import android.content.res.AssetManager; import android.graphics.RectF; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.preference.PreferenceManager; import android.support.v4.widget.DrawerLayout; @@ -23,12 +25,13 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; import android.widget.ListView; -import android.widget.TabHost; import android.widget.Toast; 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.ZoomConstraints; import org.mozilla.gecko.gfx.GeckoLayerClient; import org.mozilla.gecko.gfx.LayerView; @@ -66,19 +69,23 @@ public class LibreOfficeMainActivity extends AppCompatActivity { private URI documentUri; private DrawerLayout mDrawerLayout; + Toolbar toolbarTop; private ListView mDrawerList; private List<DocumentPartView> mDocumentPartView = new ArrayList<DocumentPartView>(); private DocumentPartViewListAdapter mDocumentPartViewListAdapter; private int partIndex=-1; private File mInputFile; + private String newFileName = "untitled"; + private String newFilePath = "/storage/emulated/0/Documents/"; private DocumentOverlay mDocumentOverlay; private File mTempFile = null; - + private String newDocumentType = null; private FormattingController mFormattingController; private ToolbarController mToolbarController; private FontController mFontController; private SearchController mSearchController; + private static final int PICKFOLDER_RESULT_CODE = 1; public GeckoLayerClient getLayerClient() { return mLayerClient; @@ -96,7 +103,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity { private boolean isFormattingToolbarOpen = false; private boolean isSearchToolbarOpen = false; private boolean isDocumentChanged = false; - + public boolean isNewDocument = false; @Override public void onCreate(Bundle savedInstanceState) { Log.w(LOGTAG, "onCreate.."); @@ -112,7 +119,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity { } setContentView(R.layout.activity_main); - Toolbar toolbarTop = (Toolbar) findViewById(R.id.toolbar); + toolbarTop = (Toolbar) findViewById(R.id.toolbar); hideBottomToolbar(); mToolbarController = new ToolbarController(this, toolbarTop); @@ -127,32 +134,48 @@ public class LibreOfficeMainActivity extends AppCompatActivity { mFontController = new FontController(this); mSearchController = new SearchController(this); + // New document type string is not null, it means we want to open a new document + if (getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY) != null) { + newDocumentType = getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY); + if (newDocumentType.matches(LibreOfficeUIActivity.NEW_WRITER_STRING_KEY)) + newFileName = newFileName + ".odt"; + else if (newDocumentType.matches(LibreOfficeUIActivity.NEW_IMPRESS_STRING_KEY)) + newFileName = newFileName + ".odp"; + else if (newDocumentType.matches(LibreOfficeUIActivity.NEW_CALC_STRING_KEY)) + newFileName = newFileName + ".ods"; + else + newFileName = newFileName + ".odg"; + // We want to create a new Document, create an alert dialogue to name the new file. + openSelectPathIntent(); + isNewDocument = true; + } + if (getIntent().getData() != null) { if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_CONTENT)) { if (copyFileToTemp() && mTempFile != null) { mInputFile = mTempFile; - Log.d(LOGTAG, "SCHEME_CONTENT: getPath(): " + getIntent().getData().getPath()); + Log.e(LOGTAG, "SCHEME_CONTENT: getPath(): " + getIntent().getData().getPath()); + toolbarTop.setTitle(mInputFile.getName()); } else { // TODO: can't open the file Log.e(LOGTAG, "couldn't create temporary file from " + getIntent().getData()); } } else if (getIntent().getData().getScheme().equals(ContentResolver.SCHEME_FILE)) { mInputFile = new File(getIntent().getData().getPath()); - Log.d(LOGTAG, "SCHEME_FILE: getPath(): " + getIntent().getData().getPath()); - + Log.e(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 if (isNewDocument){ + toolbarTop.setTitle(newFileName); } else { mInputFile = new File(DEFAULT_DOC_PATH); } - toolbarTop.setTitle(mInputFile.getName()); - - mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); if (mDocumentPartViewListAdapter == null) { @@ -187,24 +210,42 @@ public class LibreOfficeMainActivity extends AppCompatActivity { mDocumentOverlay = new DocumentOverlay(this, layerView); mToolbarController.setupToolbars(); + } - TabHost host = (TabHost) findViewById(R.id.toolbarTabHost); - host.setup(); - - TabHost.TabSpec spec = host.newTabSpec("Character"); - spec.setContent(R.id.tab_character); - spec.setIndicator("Character"); - host.addTab(spec); - - spec = host.newTabSpec("Paragraph"); - spec.setContent(R.id.tab_paragraph); - spec.setIndicator("Paragraph"); - host.addTab(spec); + private void openSelectPathIntent() { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType("*/*") + .putExtra(Intent.EXTRA_TITLE, newFileName); + startActivityForResult(intent, PICKFOLDER_RESULT_CODE); + } - spec = host.newTabSpec("Insert"); - spec.setContent(R.id.tab_insert); - spec.setIndicator("Insert"); - host.addTab(spec); + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + switch(requestCode){ + case PICKFOLDER_RESULT_CODE: + if(resultCode==RESULT_OK){ + Uri fileURI = data.getData(); + newFilePath = fileURI.getPath(); + if (newFilePath.contains("primary")) { + /* When file is being saved in primary storage folder other than Documents home. + * newFilePath content is similar to this - /document/primary:Downloads/untitled1.odt */ + newFilePath = Environment.getExternalStorageDirectory().getPath() + "/" + newFilePath.split(":")[1]; + } else if (newFilePath.contains("home")) { + /* When file is being saved in documents folder itself i.e. Document home. + * newFilePath content is similar to this - /document/home:untitled1.odt */ + newFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/" + newFilePath.split(":")[1]; + } else { + newFilePath = Environment.getExternalStorageDirectory().getPath() + "/"; + } + Log.d("newFilePath", newFilePath); + } + // Now set the input file variable to new file created + mInputFile = new File(newFilePath); + // and load the new document + LOKitShell.sendNewDocumentLoadEvent(newFilePath, newDocumentType); + break; + } } public RectF getCurrentCursorPosition() { @@ -252,10 +293,21 @@ public class LibreOfficeMainActivity extends AppCompatActivity { } /** + * Save a new document + * */ + public void saveAs(){ + LOKitShell.sendSaveAsEvent(mInputFile.getPath(), FileUtilities.getExtension(mInputFile.getPath()).substring(1)); + } + + /** * Save the document and invoke save on document provider to upload the file * to the cloud if necessary. */ public void saveDocument() { + if (!mInputFile.exists()) { + // Needed for handling null in case new document is not created. + mInputFile = new File(DEFAULT_DOC_PATH); + } final long lastModified = mInputFile.lastModified(); final Activity activity = LibreOfficeMainActivity.this; Toast.makeText(this, R.string.message_saving, Toast.LENGTH_SHORT).show(); @@ -341,10 +393,12 @@ public class LibreOfficeMainActivity extends AppCompatActivity { protected void onStart() { Log.i(LOGTAG, "onStart.."); super.onStart(); - if(partIndex == -1) - LOKitShell.sendLoadEvent(mInputFile.getPath()); - else - LOKitShell.sendResumeEvent(mInputFile.getPath(), partIndex); + if (!isNewDocument){ + if (partIndex == -1) + LOKitShell.sendLoadEvent(mInputFile.getPath()); + else + LOKitShell.sendResumeEvent(mInputFile.getPath(), partIndex); + } } @Override @@ -381,12 +435,14 @@ public class LibreOfficeMainActivity extends AppCompatActivity { public void onClick(DialogInterface dialog, int which) { switch (which){ case DialogInterface.BUTTON_POSITIVE: - //SAVE - saveDocument(); + if (isNewDocument) { + saveAs(); + } else { + saveDocument(); + } isDocumentChanged=false; onBackPressed(); break; - case DialogInterface.BUTTON_NEGATIVE: //CANCEL break; @@ -640,7 +696,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity { } private static boolean copyFromAssets(AssetManager assetManager, - String fromAssetPath, String targetDir) { + String fromAssetPath, String targetDir) { try { String[] files = assetManager.list(fromAssetPath); @@ -695,6 +751,14 @@ public class LibreOfficeMainActivity extends AppCompatActivity { return false; } } + + // This method is used in LOKitTileProvider.java to show status of new file creation. + public void showSaveStatusToast(boolean error) { + if (!error) + Toast.makeText(this, getString(R.string.save_as_success) + newFilePath, Toast.LENGTH_SHORT).show(); + else + Toast.makeText(this, R.string.save_as_error, Toast.LENGTH_SHORT).show(); + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/android/source/src/java/org/libreoffice/TileProvider.java b/android/source/src/java/org/libreoffice/TileProvider.java index 0ab5a1f641c9..e84127cb0715 100644 --- a/android/source/src/java/org/libreoffice/TileProvider.java +++ b/android/source/src/java/org/libreoffice/TileProvider.java @@ -20,6 +20,12 @@ import org.mozilla.gecko.gfx.IntSize; * Provides the tiles and other document information. */ public interface TileProvider { + + /** + * Save the current document. + */ + void saveDocumentAs(String filePath, String format); + /** * Returns the page width in pixels. */ diff --git a/android/source/src/java/org/libreoffice/ToolbarController.java b/android/source/src/java/org/libreoffice/ToolbarController.java index e4a55dd424b6..0012fdbeae06 100644 --- a/android/source/src/java/org/libreoffice/ToolbarController.java +++ b/android/source/src/java/org/libreoffice/ToolbarController.java @@ -98,7 +98,11 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener { mContext.showAbout(); return true; case R.id.action_save: - mContext.saveDocument(); + if (mContext.isNewDocument) { + mContext.saveAs(); + } else { + mContext.saveDocument(); + } return true; case R.id.action_parts: mContext.openDrawer(); diff --git a/android/source/src/java/org/libreoffice/ui/FileUtilities.java b/android/source/src/java/org/libreoffice/ui/FileUtilities.java index 811fc4562dc7..aff6e0bfd7f3 100644 --- a/android/source/src/java/org/libreoffice/ui/FileUtilities.java +++ b/android/source/src/java/org/libreoffice/ui/FileUtilities.java @@ -121,7 +121,7 @@ public class FileUtilities { extensionToMimeTypeMap.put("oth", "application/vnd.oasis.opendocument.text-web"); } - private static final String getExtension(String filename) { + public static final String getExtension(String filename) { if (filename == null) return ""; int nExt = filename.lastIndexOf('.'); diff --git a/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java b/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java index f2f408a7c331..01585b1077be 100644 --- a/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java +++ b/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java @@ -23,11 +23,14 @@ import android.hardware.usb.UsbManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.preference.PreferenceManager; import android.support.annotation.NonNull; +import android.support.design.widget.FloatingActionButton; import android.support.design.widget.NavigationView; import android.support.v4.content.ContextCompat; +import android.support.v4.view.ViewCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBarDrawerToggle; @@ -47,7 +50,11 @@ import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewGroup; +import android.view.animation.Animation; +import android.view.animation.AnimationUtils; +import android.view.animation.OvershootInterpolator; import android.widget.ImageView; +import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -60,10 +67,12 @@ import org.libreoffice.storage.DocumentProviderFactory; import org.libreoffice.storage.DocumentProviderSettingsActivity; import org.libreoffice.storage.IDocumentProvider; import org.libreoffice.storage.IFile; +import org.libreoffice.storage.local.LocalFile; import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.text.SimpleDateFormat; @@ -74,7 +83,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; -public class LibreOfficeUIActivity extends AppCompatActivity implements SettingsListenerModel.OnSettingsPreferenceChangedListener{ +public class LibreOfficeUIActivity extends AppCompatActivity implements SettingsListenerModel.OnSettingsPreferenceChangedListener, View.OnClickListener{ private String LOGTAG = LibreOfficeUIActivity.class.getSimpleName(); private SharedPreferences prefs; private int filterMode = FileUtilities.ALL; @@ -98,6 +107,12 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings public static final String SORT_MODE_KEY = "SORT_MODE"; private static final String RECENT_DOCUMENTS_KEY = "RECENT_DOCUMENTS"; + public static final String NEW_DOC_TYPE_KEY = "NEW_DOC_TYPE_KEY"; + public static final String NEW_WRITER_STRING_KEY = "private:factory/swriter"; + public static final String NEW_IMPRESS_STRING_KEY = "private:factory/simpress"; + public static final String NEW_CALC_STRING_KEY = "private:factory/scalc"; + public static final String NEW_DRAW_STRING_KEY = "private:factory/sdraw"; + public static final int GRID_VIEW = 0; public static final int LIST_VIEW = 1; @@ -110,6 +125,20 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings private boolean canQuit = false; + private Animation fabOpenAnimation; + private Animation fabCloseAnimation; + private boolean isFabMenuOpen = false; + private FloatingActionButton editFAB; + private FloatingActionButton writerFAB; + private FloatingActionButton drawFAB; + private FloatingActionButton impressFAB; + private FloatingActionButton calcFAB; + private LinearLayout drawLayout; + private LinearLayout writerLayout; + private LinearLayout impressLayout; + private LinearLayout calcLayout; + + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -129,6 +158,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings // init UI and populate with contents from the provider switchToDocumentProvider(documentProviderFactory.getDefaultProvider()); createUI(); + getAnimations(); } public void createUI() { @@ -143,6 +173,21 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings actionBar.setDisplayHomeAsUpEnabled(true); } + editFAB = (FloatingActionButton) findViewById(R.id.editFAB); + editFAB.setOnClickListener(this); + impressFAB = (FloatingActionButton) findViewById(R.id.newImpressFAB); + impressFAB.setOnClickListener(this); + writerFAB = (FloatingActionButton) findViewById(R.id.newWriterFAB); + writerFAB.setOnClickListener(this); + calcFAB = (FloatingActionButton) findViewById(R.id.newCalcFAB); + calcFAB.setOnClickListener(this); + drawFAB = (FloatingActionButton) findViewById(R.id.newDrawFAB); + drawFAB.setOnClickListener(this); + writerLayout = (LinearLayout) findViewById(R.id.writerLayout); + impressLayout = (LinearLayout) findViewById(R.id.impressLayout); + calcLayout = (LinearLayout) findViewById(R.id.calcLayout); + drawLayout = (LinearLayout) findViewById(R.id.drawLayout); + recentRecyclerView = (RecyclerView) findViewById(R.id.list_recent); Set<String> recentFileStrings = prefs.getStringSet(RECENT_DOCUMENTS_KEY, new HashSet<String>()); @@ -235,6 +280,9 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings super.onDrawerOpened(drawerView); supportInvalidateOptionsMenu(); navigationDrawer.requestFocus(); // Make keypad navigation easier + if (isFabMenuOpen) { + collapseFabMenu(); //Collapse FAB Menu when drawer is opened + } } @Override @@ -248,6 +296,37 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings drawerToggle.syncState(); } + private void getAnimations() { + fabOpenAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_open); + fabCloseAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_close); + } + + private void expandFabMenu() { + ViewCompat.animate(editFAB).rotation(45.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start(); + drawLayout.startAnimation(fabOpenAnimation); + impressLayout.startAnimation(fabOpenAnimation); + writerLayout.startAnimation(fabOpenAnimation); + calcLayout.startAnimation(fabOpenAnimation); + writerFAB.setClickable(true); + impressFAB.setClickable(true); + drawFAB.setClickable(true); + calcFAB.setClickable(true); + isFabMenuOpen = true; + } + + private void collapseFabMenu() { + ViewCompat.animate(editFAB).rotation(0.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start(); + writerLayout.startAnimation(fabCloseAnimation); + impressLayout.startAnimation(fabCloseAnimation); + drawLayout.startAnimation(fabCloseAnimation); + calcLayout.startAnimation(fabCloseAnimation); + writerFAB.setClickable(false); + impressFAB.setClickable(false); + drawFAB.setClickable(false); + calcFAB.setClickable(false); + isFabMenuOpen = false; + } + private boolean checkDocumentProviderAvailability(IDocumentProvider provider) { return provider.checkProviderAvailability(); } @@ -273,15 +352,23 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings fileRecyclerView.setAdapter(new ExplorerItemAdapter(this, filePaths)); // close drawer if it was open drawerLayout.closeDrawer(navigationDrawer); + if (isFabMenuOpen) { + collapseFabMenu(); + } } @Override public void onBackPressed() { if (drawerLayout.isDrawerOpen(navigationDrawer)) { drawerLayout.closeDrawer(navigationDrawer); + if (isFabMenuOpen) { + collapseFabMenu(); + } } else if (!currentDirectory.equals(homeDirectory)) { // navigate upwards in directory hierarchy openParentDirectory(); + } else if (isFabMenuOpen) { + collapseFabMenu(); } else { // only exit if warning has been shown if (canQuit) { @@ -460,6 +547,13 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings }.execute(document); } + // For opening a new Document + private void open(String newDocumentType) { + Intent intent = new Intent(this, LibreOfficeMainActivity.class); + intent.putExtra(NEW_DOC_TYPE_KEY, newDocumentType); + startActivity(intent); + } + private void open(int position) { IFile file = filePaths.get(position); if (!file.isDirectory()) { @@ -884,6 +978,33 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings } } + @Override + public void onClick(View v) { + int id = v.getId(); + switch (id){ + case R.id.editFAB: + if (isFabMenuOpen) { + collapseFabMenu(); + } else { + expandFabMenu(); + } + break; + case R.id.newWriterFAB: + open(NEW_WRITER_STRING_KEY); + break; + case R.id.newImpressFAB: + open(NEW_IMPRESS_STRING_KEY); + break; + case R.id.newCalcFAB: + open(NEW_CALC_STRING_KEY); + break; + case R.id.newDrawFAB: + open(NEW_DRAW_STRING_KEY); + break; + } + } + + class ExplorerItemAdapter extends RecyclerView.Adapter<ExplorerItemAdapter.ViewHolder> { private Activity mActivity; diff --git a/desktop/source/lib/lokandroid.cxx b/desktop/source/lib/lokandroid.cxx index af3604aed6b6..0f807d061590 100644 --- a/desktop/source/lib/lokandroid.cxx +++ b/desktop/source/lib/lokandroid.cxx @@ -257,7 +257,7 @@ extern "C" SAL_JNI_EXPORT void JNICALL Java_org_libreoffice_kit_Document_initial pDocument->pClass->initializeForRendering(pDocument, NULL); } -extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Office_saveAs +extern "C" SAL_JNI_EXPORT jint JNICALL Java_org_libreoffice_kit_Document_saveAs (JNIEnv* pEnv, jobject aObject, jstring sUrl, jstring sFormat, jstring sOptions) { LibreOfficeKitDocument* pDocument = getHandle<LibreOfficeKitDocument>(pEnv, aObject); |