diff options
author | Ximeng Zu <uznomis@yahoo.com> | 2017-05-24 17:02:17 -0500 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2017-07-11 10:52:19 +0200 |
commit | 6b873439042960bfc72a5e341c5ed61eeb40f53e (patch) | |
tree | 0415afc622d0576b63f41cc0e56d0e96a8996685 /android | |
parent | ff10bc47abe0b04480c9fb5db025afbb5e402b4b (diff) |
Presentation Mode
Adding fullscreen presentation mode.
Change-Id: Id07416ce204d1d7dd917fbd33a4d9f5072ac1703
Reviewed-on: https://gerrit.libreoffice.org/38006
Tested-by: Jenkins <ci@libreoffice.org>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'android')
8 files changed, 329 insertions, 16 deletions
diff --git a/android/source/AndroidManifest.xml b/android/source/AndroidManifest.xml index 0b384a3becc2..a9c3a3a1609d 100644 --- a/android/source/AndroidManifest.xml +++ b/android/source/AndroidManifest.xml @@ -125,6 +125,13 @@ android:windowSoftInputMode="stateHidden"> </activity> + <activity android:name=".PresentationActivity" + android:screenOrientation="landscape" > + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value=".LibreOfficeMainActivity" /> + </activity> + </application> </manifest> diff --git a/android/source/res/layout/presentation_mode.xml b/android/source/res/layout/presentation_mode.xml new file mode 100644 index 000000000000..ec1845ab68ab --- /dev/null +++ b/android/source/res/layout/presentation_mode.xml @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" android:layout_height="match_parent"> + + <WebView + android:id="@+id/presentation_view" + android:layout_width="match_parent" + android:layout_height="match_parent" /> + + <RelativeLayout + android:id="@+id/presentation_gesture_view" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animateLayoutChanges="true"> + + <ImageButton + android:id="@+id/slide_show_nav_prev" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:layout_alignParentTop="true" + android:src="@drawable/ic_search_direction_up" /> + + <ImageButton + android:id="@+id/slide_show_nav_next" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentBottom="true" + android:layout_alignParentEnd="true" + android:layout_alignParentRight="true" + android:src="@drawable/ic_search_direction_down" /> + + <Button + android:id="@+id/slide_show_nav_back" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_alignParentTop="true" + android:text="@string/slideshow_action_back" /> + + </RelativeLayout> + +</RelativeLayout>
\ No newline at end of file diff --git a/android/source/res/menu/main.xml b/android/source/res/menu/main.xml index 3ec0dadc5b63..0b34d8b5ffac 100644 --- a/android/source/res/menu/main.xml +++ b/android/source/res/menu/main.xml @@ -42,6 +42,11 @@ android:orderInCategory="100" /> </group> + <item android:id="@+id/action_presentation" + android:title="@string/action_presentation" + android:orderInCategory="100" + android:enabled="false" /> + <item android:id="@+id/action_parts" android:title="@string/action_parts" android:orderInCategory="100" /> diff --git a/android/source/res/values/strings.xml b/android/source/res/values/strings.xml index 4495ca584927..ad4d2699684a 100644 --- a/android/source/res/values/strings.xml +++ b/android/source/res/values/strings.xml @@ -130,5 +130,9 @@ <!-- Create New Document Dialog Strings --> <string name="create_new_document_title">Enter file name</string> <string name="action_create">CREATE</string> + <string name="action_presentation">Slide show</string> + <string name="alert_copy_svg_slide_show_to_clipboard">Your Android device doesn\'t support in-app svg slideshow. We copied the slideshow link to clipboard. Please press home button, open a modern web browser, paste in the address bar, and go.</string> + <string name="alert_copy_svg_slide_show_to_clipboard_dismiss">OK</string> + <string name="slideshow_action_back">Back</string> </resources> diff --git a/android/source/src/java/org/libreoffice/LOKitTileProvider.java b/android/source/src/java/org/libreoffice/LOKitTileProvider.java index e17893985a75..6fb8a9b80671 100644 --- a/android/source/src/java/org/libreoffice/LOKitTileProvider.java +++ b/android/source/src/java/org/libreoffice/LOKitTileProvider.java @@ -101,6 +101,10 @@ class LOKitTileProvider implements TileProvider { Log.i(LOGTAG, "Document parts: " + parts); mContext.getDocumentPartView().clear(); + if (mDocument.getDocumentType() == Document.DOCTYPE_PRESENTATION) { + mContext.getToolbarController().disableMenuItem(R.id.action_presentation, false); + } + // Writer documents always have one part, so hide the navigation drawer. if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) { for (int i = 0; i < parts; i++) { @@ -134,26 +138,44 @@ class LOKitTileProvider implements TileProvider { @Override public void saveDocumentAs(String filePath, String format) { - String newFilePath = "file://" + filePath; + final String newFilePath = "file://" + filePath; Log.d("saveFilePathURL", newFilePath); + LOKitShell.showProgressSpinner(mContext); 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.showSaveStatusMessage(true); - } - }); - } - LOKitShell.getMainHandler().post(new Runnable() { - @Override - public void run() { - // There was no error - mContext.showSaveStatusMessage(false); + if (format.equals("svg")) { + // error in creating temp slideshow svg file + Log.d(LOGTAG, "Error in creating temp slideshow svg file"); + } else { + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + // There was some error + mContext.showSaveStatusMessage(true); + } + }); } - }); + } else { + if (format.equals("svg")) { + // successfully created temp slideshow svg file + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + mContext.startPresentation(newFilePath); + } + }); + } else { + LOKitShell.getMainHandler().post(new Runnable() { + @Override + public void run() { + // There was no error + mContext.showSaveStatusMessage(false); + } + }); + } + } + LOKitShell.hideProgressSpinner(mContext); } private void setupDocumentFonts() { diff --git a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java index b4f8dc77f60b..8aeb1f5ff816 100755 --- a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java +++ b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java @@ -2,6 +2,8 @@ package org.libreoffice; import android.app.Activity; import android.app.AlertDialog; +import android.content.ClipData; +import android.content.ClipboardManager; import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; @@ -10,12 +12,14 @@ 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.Build; import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.support.design.widget.Snackbar; import android.support.design.widget.BottomSheetBehavior; +import android.support.design.widget.Snackbar; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -79,6 +83,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin private File mInputFile; private DocumentOverlay mDocumentOverlay; private File mTempFile = null; + private File mTempSlideShowFile = null; private String newDocumentType = null; BottomSheetBehavior bottomToolbarSheetBehavior; @@ -407,6 +412,10 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin // noinspection ResultOfMethodCallIgnored mTempFile.delete(); } + if (mTempSlideShowFile.exists()) { + // noinspection ResultOfMethodCallIgnored + mTempSlideShowFile.delete(); + } } } @Override @@ -749,6 +758,36 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin Snackbar.make(mDrawerLayout, getString(R.string.create_new_file_success) + mInputFile.getName(), Snackbar.LENGTH_LONG).show(); else Snackbar.make(mDrawerLayout, getString(R.string.create_new_file_error) + mInputFile.getName(), Snackbar.LENGTH_LONG).show(); } + + public void preparePresentation() { + if (getExternalCacheDir() != null) { + String tempPath = getExternalCacheDir().getPath() + "/" + mInputFile.getName() + ".svg"; + mTempSlideShowFile = new File(tempPath); + if (mTempSlideShowFile.exists() && !isDocumentChanged) { + startPresentation("file://" + tempPath); + } else { + LOKitShell.sendSaveAsEvent(tempPath, "svg"); + } + } + } + + public void startPresentation(String tempPath) { + // pre-KitKat android doesn't have chrome-based WebView, which is needed to show svg slideshow + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + Intent intent = new Intent(this, PresentationActivity.class); + intent.setData(Uri.parse(tempPath)); + startActivity(intent); + } else { + // copy the svg file path to clipboard for the user to paste in a browser + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("temp svg file path", tempPath); + clipboard.setPrimaryClip(clip); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(R.string.alert_copy_svg_slide_show_to_clipboard) + .setPositiveButton(R.string.alert_copy_svg_slide_show_to_clipboard_dismiss, null).show(); + } + } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/android/source/src/java/org/libreoffice/PresentationActivity.java b/android/source/src/java/org/libreoffice/PresentationActivity.java new file mode 100644 index 000000000000..1eea5e3836fb --- /dev/null +++ b/android/source/src/java/org/libreoffice/PresentationActivity.java @@ -0,0 +1,188 @@ +package org.libreoffice; + +import android.content.Intent; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v4.view.GestureDetectorCompat; +import android.support.v7.app.AppCompatActivity; +import android.view.GestureDetector; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.view.WindowManager; +import android.webkit.WebView; +import android.widget.Button; +import android.widget.ImageButton; + +public class PresentationActivity extends AppCompatActivity { + + private static final String LOGTAG = PresentationActivity.class.getSimpleName(); + WebView mWebView; + View mGestureView; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // First we hide the status bar + if (Build.VERSION.SDK_INT < 16) { + // If the Android version is lower than Jellybean, use this call to hide + // the status bar. + getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN); + } else { + // If higher than Jellybean + View decorView = getWindow().getDecorView(); + // Hide the status bar. + int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN; + decorView.setSystemUiVisibility(uiOptions); + } + + setContentView(R.layout.presentation_mode); + + // get intent and url + Intent intent = getIntent(); + String filePath = intent.getDataString(); + + // set up WebView + mWebView = (WebView) findViewById(R.id.presentation_view); + mWebView.getSettings().setJavaScriptEnabled(true); + mWebView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return true; + } + }); + + // set up buttons within presentation_gesture_view + ImageButton prevButton = (ImageButton) findViewById(R.id.slide_show_nav_prev); + ImageButton nextButton = (ImageButton) findViewById(R.id.slide_show_nav_next); + Button backButton = (Button) findViewById(R.id.slide_show_nav_back); + + prevButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + pageLeft(); + } + }); + nextButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + pageRight(); + } + }); + backButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + onBackPressed(); + } + }); + + // set up presentation_gesture_view + mGestureView = findViewById(R.id.presentation_gesture_view); + final GestureDetectorCompat gestureDetector = + new GestureDetectorCompat(this, new presentationGestureViewListener()); + mGestureView.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + return gestureDetector.onTouchEvent(event); + } + }); + + // load url + mWebView.loadUrl(filePath); + } + + private class presentationGestureViewListener extends GestureDetector.SimpleOnGestureListener { + private static final int SWIPE_VELOCITY_THRESHOLD = 100; + private static final int SCROLL_THRESHOLD = 10; // if scrollCounter is larger than this, a page switch is triggered + private int scrollCounter = 0; // a counter for measuring scrolling distance + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + boolean result = false; + try { + float diffY = e2.getY() - e1.getY(); + float diffX = e2.getX() - e1.getX(); + if (Math.abs(diffX) > Math.abs(diffY)) { + if (Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { + if (diffX > 0) { + pageRight(); + } else { + pageLeft(); + } + result = true; + } + } + } catch (Exception exception) { + exception.printStackTrace(); + } + return result; + } + + @Override + public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { + boolean result = false; + try { + float diffY = e2.getY() - e1.getY(); + float diffX = e2.getX() - e1.getX(); + if (Math.abs(diffX) < Math.abs(diffY)) { + if (distanceY > 0) { + scrollCounter++; + if (scrollCounter >= SCROLL_THRESHOLD) { + pageRight(); + scrollCounter = 0; + } + } else { + scrollCounter--; + if (scrollCounter <= -SCROLL_THRESHOLD) { + pageLeft(); + scrollCounter = 0; + } + } + result = true; + } + } catch (Exception exception) { + exception.printStackTrace(); + } + return result; + } + + @Override + public boolean onSingleTapUp(MotionEvent e) { + if (e.getX() < mGestureView.getWidth()/3) { + pageLeft(); + } else if (e.getX() < mGestureView.getWidth()*2/3) { + hideControlButtons(); + } else { + pageRight(); + } + return true; + } + } + + private void hideControlButtons() { + View[] views= {findViewById(R.id.slide_show_nav_prev),findViewById(R.id.slide_show_nav_next),findViewById(R.id.slide_show_nav_back)} ; + for (View view : views) { + if (view.getVisibility() == View.GONE) { + view.setVisibility(View.VISIBLE); + } else if (view.getVisibility() == View.VISIBLE) { + view.setVisibility(View.GONE); + } + } + } + + private void pageLeft() { + mWebView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT)); + } + + private void pageRight() { + mWebView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT)); + } +}
\ No newline at end of file diff --git a/android/source/src/java/org/libreoffice/ToolbarController.java b/android/source/src/java/org/libreoffice/ToolbarController.java index d7f1ac18c95e..9ef994d271a9 100644 --- a/android/source/src/java/org/libreoffice/ToolbarController.java +++ b/android/source/src/java/org/libreoffice/ToolbarController.java @@ -132,6 +132,9 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener { case R.id.action_redo: LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Redo")); return true; + case R.id.action_presentation: + mContext.preparePresentation(); + return true; } return false; } |