summaryrefslogtreecommitdiff
path: root/android/source/src
diff options
context:
space:
mode:
Diffstat (limited to 'android/source/src')
-rw-r--r--android/source/src/java/org/libreoffice/AboutDialogFragment.java67
-rw-r--r--android/source/src/java/org/libreoffice/ColorPaletteAdapter.java20
-rw-r--r--android/source/src/java/org/libreoffice/ColorPickerAdapter.java86
-rw-r--r--android/source/src/java/org/libreoffice/DocumentPartViewListAdapter.java1
-rw-r--r--android/source/src/java/org/libreoffice/FontController.java91
-rw-r--r--android/source/src/java/org/libreoffice/FormattingController.java153
-rw-r--r--android/source/src/java/org/libreoffice/InvalidationHandler.java66
-rw-r--r--android/source/src/java/org/libreoffice/LOEvent.java9
-rw-r--r--android/source/src/java/org/libreoffice/LOKitInputConnectionHandler.java2
-rw-r--r--android/source/src/java/org/libreoffice/LOKitShell.java12
-rw-r--r--android/source/src/java/org/libreoffice/LOKitThread.java106
-rw-r--r--android/source/src/java/org/libreoffice/LOKitTileProvider.java217
-rw-r--r--android/source/src/java/org/libreoffice/LibreOfficeApplication.java4
-rw-r--r--android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java517
-rw-r--r--android/source/src/java/org/libreoffice/LocaleHelper.java3
-rw-r--r--android/source/src/java/org/libreoffice/PasswordDialogFragment.java6
-rw-r--r--android/source/src/java/org/libreoffice/PresentationActivity.java27
-rw-r--r--android/source/src/java/org/libreoffice/SearchController.java13
-rw-r--r--android/source/src/java/org/libreoffice/ThumbnailCreator.java2
-rw-r--r--android/source/src/java/org/libreoffice/TileProvider.java24
-rw-r--r--android/source/src/java/org/libreoffice/ToolbarController.java174
-rw-r--r--android/source/src/java/org/libreoffice/UNOCommandsController.java8
-rw-r--r--android/source/src/java/org/libreoffice/canvas/BitmapHandle.java2
-rw-r--r--android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java38
-rw-r--r--android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java2
-rw-r--r--android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java18
-rw-r--r--android/source/src/java/org/libreoffice/storage/DocumentProviderFactory.java128
-rw-r--r--android/source/src/java/org/libreoffice/storage/DocumentProviderSettingsActivity.java102
-rw-r--r--android/source/src/java/org/libreoffice/storage/IDocumentProvider.java70
-rw-r--r--android/source/src/java/org/libreoffice/storage/IFile.java116
-rw-r--r--android/source/src/java/org/libreoffice/storage/IOUtils.java56
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/BrowserSelectorActivity.java153
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserActivity.java42
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserFragment.java199
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/ExternalFile.java163
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/ExtsdDocumentsProvider.java175
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/IExternalDocumentProvider.java22
-rw-r--r--android/source/src/java/org/libreoffice/storage/external/OTGDocumentsProvider.java90
-rw-r--r--android/source/src/java/org/libreoffice/storage/local/LocalDocumentsDirectoryProvider.java73
-rw-r--r--android/source/src/java/org/libreoffice/storage/local/LocalDocumentsProvider.java60
-rw-r--r--android/source/src/java/org/libreoffice/storage/local/LocalFile.java103
-rw-r--r--android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudFile.java178
-rw-r--r--android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudProvider.java192
-rw-r--r--android/source/src/java/org/libreoffice/ui/FileUtilities.java206
-rw-r--r--android/source/src/java/org/libreoffice/ui/FolderIconView.java204
-rw-r--r--android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java1139
-rw-r--r--android/source/src/java/org/libreoffice/ui/PageView.java18
-rw-r--r--android/source/src/java/org/libreoffice/ui/RecentFile.java25
-rw-r--r--android/source/src/java/org/libreoffice/ui/RecentFilesAdapter.java18
-rw-r--r--android/source/src/java/org/mozilla/gecko/ZoomConstraints.java14
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/GLController.java78
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java16
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java34
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/LayerRenderer.java83
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/LayerView.java128
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/PointUtils.java4
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java9
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java2
-rw-r--r--android/source/src/java/org/mozilla/gecko/gfx/SubTile.java14
59 files changed, 1174 insertions, 4408 deletions
diff --git a/android/source/src/java/org/libreoffice/AboutDialogFragment.java b/android/source/src/java/org/libreoffice/AboutDialogFragment.java
index 6c944bae7ef1..0d9fc45856ef 100644
--- a/android/source/src/java/org/libreoffice/AboutDialogFragment.java
+++ b/android/source/src/java/org/libreoffice/AboutDialogFragment.java
@@ -18,21 +18,16 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
import android.text.Html;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.TextView;
-import java.io.File;
-
public class AboutDialogFragment extends DialogFragment {
- private static final String DEFAULT_DOC_PATH = "/assets/example.odt";
-
-
@NonNull @Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
@@ -45,66 +40,60 @@ public class AboutDialogFragment extends DialogFragment {
int defaultColor = textView.getTextColors().getDefaultColor();
textView.setTextColor(defaultColor);
- // Take care of placeholders in the version and vendor text views.
- TextView versionView = messageView.findViewById(R.id.about_version);
- TextView vendorView = messageView.findViewById(R.id.about_vendor);
+ // Take care of placeholders and set text in version and vendor text views.
try
{
String versionName = getActivity().getPackageManager()
.getPackageInfo(getActivity().getPackageName(), 0).versionName;
- String[] tokens = versionName.split("/");
- if (tokens.length == 3)
- {
- String version = String.format(versionView.getText().toString().replace("\n", "<br/>"),
- tokens[0], "<a href=\"https://hub.libreoffice.org/git-core/" + tokens[1] + "\">" + tokens[1] + "</a>");
- @SuppressWarnings("deprecation") // since 24 with additional option parameter
- Spanned versionString = Html.fromHtml(version);
- versionView.setText(versionString);
- versionView.setMovementMethod(LinkMovementMethod.getInstance());
- String vendor = vendorView.getText().toString();
- vendor = vendor.replace("$VENDOR", tokens[2]);
- vendorView.setText(vendor);
- }
- else
- throw new PackageManager.NameNotFoundException();
+ String version = String.format(getString(R.string.app_version), versionName, BuildConfig.BUILD_ID_SHORT);
+ @SuppressWarnings("deprecation") // since 24 with additional option parameter
+ Spanned versionString = Html.fromHtml(version);
+ TextView versionView = messageView.findViewById(R.id.about_version);
+ versionView.setText(versionString);
+ versionView.setMovementMethod(LinkMovementMethod.getInstance());
+ TextView vendorView = messageView.findViewById(R.id.about_vendor);
+ String vendor = getString(R.string.app_vendor).replace("$VENDOR", BuildConfig.VENDOR);
+ vendorView.setText(vendor);
}
catch (PackageManager.NameNotFoundException e)
{
- versionView.setText("");
- vendorView.setText("");
}
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
- builder .setIcon(R.drawable.lo_icon)
+ builder .setIcon(R.mipmap.ic_launcher)
.setTitle(R.string.app_name)
.setView(messageView)
.setNegativeButton(R.string.about_license, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
- loadFromAbout("/assets/license.txt");
+ loadFromAbout(R.raw.license);
dialog.dismiss();
}
})
.setPositiveButton(R.string.about_notice, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
- loadFromAbout("/assets/notice.txt");
- dialog.dismiss();
- }
- })
- .setNeutralButton(R.string.about_moreinfo, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int id) {
- loadFromAbout(DEFAULT_DOC_PATH);
+ loadFromAbout(R.raw.notice);
dialog.dismiss();
}
});
+ // when privacy policy URL is set (via '--with-privacy-policy-url=<url>' autogen option),
+ // add button to open that URL
+ final String privacyUrl = BuildConfig.PRIVACY_POLICY_URL;
+ if (!privacyUrl.isEmpty() && privacyUrl != "undefined") {
+ builder.setNeutralButton(R.string.about_privacy_policy, (DialogInterface dialog, int id) -> {
+ Intent openPrivacyUrlIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(privacyUrl));
+ startActivity(openPrivacyUrlIntent);
+ dialog.dismiss();
+ });
+ }
+
return builder.create();
}
- private void loadFromAbout(String input) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.fromFile(new File(input)));
+ private void loadFromAbout(int resourceId) {
+ Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse("android.resource://" + BuildConfig.APPLICATION_ID + "/" + resourceId));
String packageName = getActivity().getApplicationContext().getPackageName();
ComponentName componentName = new ComponentName(packageName, LibreOfficeMainActivity.class.getName());
i.setComponent(componentName);
diff --git a/android/source/src/java/org/libreoffice/ColorPaletteAdapter.java b/android/source/src/java/org/libreoffice/ColorPaletteAdapter.java
index 6ec6aa138f66..16d8a977864f 100644
--- a/android/source/src/java/org/libreoffice/ColorPaletteAdapter.java
+++ b/android/source/src/java/org/libreoffice/ColorPaletteAdapter.java
@@ -1,7 +1,7 @@
package org.libreoffice;
import android.content.Context;
-import android.support.v7.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -12,12 +12,12 @@ import android.widget.ImageButton;
public class ColorPaletteAdapter extends RecyclerView.Adapter<ColorPaletteAdapter.ColorPaletteViewHolder> {
- int[][] color_palette;
- Context mContext;
- int upperSelectedBox = -1;
- int selectedBox = 0;
- boolean animate;
- ColorPaletteListener colorPaletteListener;
+ private int[][] color_palette;
+ private final Context mContext;
+ private int upperSelectedBox = -1;
+ private int selectedBox = 0;
+ private boolean animate;
+ private final ColorPaletteListener colorPaletteListener;
public ColorPaletteAdapter(Context mContext, ColorPaletteListener colorPaletteListener) {
this.mContext = mContext;
@@ -36,6 +36,10 @@ public class ColorPaletteAdapter extends RecyclerView.Adapter<ColorPaletteAdapte
return selectedBox;
}
+ public int getUpperSelectedBox() {
+ return upperSelectedBox;
+ }
+
@Override
public void onBindViewHolder(final ColorPaletteViewHolder holder, int position) {
@@ -128,4 +132,4 @@ public class ColorPaletteAdapter extends RecyclerView.Adapter<ColorPaletteAdapte
}
-} \ No newline at end of file
+}
diff --git a/android/source/src/java/org/libreoffice/ColorPickerAdapter.java b/android/source/src/java/org/libreoffice/ColorPickerAdapter.java
index c93d5a01bbb4..a17dd264fb99 100644
--- a/android/source/src/java/org/libreoffice/ColorPickerAdapter.java
+++ b/android/source/src/java/org/libreoffice/ColorPickerAdapter.java
@@ -3,7 +3,7 @@ package org.libreoffice;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
-import android.support.v7.widget.RecyclerView;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -12,12 +12,11 @@ import android.widget.ImageButton;
public class ColorPickerAdapter extends RecyclerView.Adapter<ColorPickerAdapter.ColorPickerViewHolder> {
- Context mContext;
- ColorPaletteAdapter colorPaletteAdapter;
- ColorPaletteListener colorPaletteListener;
- int[] colorList;
- int[][] colorPalette = new int[11][8];
- int selectedBox = 0;
+ private final Context mContext;
+ private final ColorPaletteAdapter colorPaletteAdapter;
+ private final ColorPaletteListener colorPaletteListener;
+ private final int[] colorList;
+ private final int[][] colorPalette = new int[11][8];
public ColorPickerAdapter(Context mContext, final ColorPaletteAdapter colorPaletteAdapter, ColorPaletteListener colorPaletteListener) {
this.mContext = mContext;
@@ -41,10 +40,11 @@ public class ColorPickerAdapter extends RecyclerView.Adapter<ColorPickerAdapter.
public void onBindViewHolder(final ColorPickerViewHolder holder, int position) {
holder.colorBox.setBackgroundColor(colorList[position]);
- if (selectedBox != position)
- holder.colorBox.setImageDrawable(null);
- else {
+ if (colorPaletteAdapter.getUpperSelectedBox() == position
+ && colorPaletteAdapter.getSelectedBox() >= 0) {
holder.colorBox.setImageResource(R.drawable.ic_done_white_12dp);
+ } else {
+ holder.colorBox.setImageDrawable(null);
}
holder.colorBox.setOnClickListener(new View.OnClickListener() {
@@ -64,12 +64,20 @@ public class ColorPickerAdapter extends RecyclerView.Adapter<ColorPickerAdapter.
private void setPosition(int position) {
- this.selectedBox = position;
selectSubColor(position, position==0?0:3);
colorPaletteListener.applyColor(colorList[position]);
updateAdapter();
}
+ /**
+ * Switches to first palette, but doesn't mark any color as selected.
+ * Use this if no color in the palette matches the actual one.
+ */
+ public void unselectColors() {
+ colorPaletteAdapter.changePosition(0, -1);
+ updateAdapter();
+ }
+
private void selectSubColor(int position1, int position2) {
colorPaletteAdapter.setPosition(position1, position2);
}
@@ -88,7 +96,15 @@ public class ColorPickerAdapter extends RecyclerView.Adapter<ColorPickerAdapter.
int red_shade = red;
int green_shade = green;
int blue_shade = blue;
- if (i != 0) {
+ if (i == 0) {
+ colorPalette[0][0] = colorList[i];
+ for (int k = 1; k < 7; k++) {
+ red_tint = (int) (red_tint + (255 - red_tint) * 0.25);
+ green_tint = (int) (green_tint + (255 - green_tint) * 0.25);
+ blue_tint = (int) (blue_tint + (255 - blue_tint) * 0.25);
+ colorPalette[i][k] = (Color.rgb(red_tint, green_tint, blue_tint));
+ }
+ } else {
colorPalette[i][3] = colorList[i];
for (int k = 2; k >= 0; k--) {
red_shade = (int) (red_shade * 0.75);
@@ -102,48 +118,28 @@ public class ColorPickerAdapter extends RecyclerView.Adapter<ColorPickerAdapter.
blue_tint = (int) (blue_tint + (255 - blue_tint) * 0.45);
colorPalette[i][k] = (Color.rgb(red_tint, green_tint, blue_tint));
}
- } else {
- colorPalette[0][0] = colorList[i];
- for (int k = 1; k < 7; k++) {
- red_tint = (int) (red_tint + (255 - red_tint) * 0.25);
- green_tint = (int) (green_tint + (255 - green_tint) * 0.25);
- blue_tint = (int) (blue_tint + (255 - blue_tint) * 0.25);
- colorPalette[i][k] = (Color.rgb(red_tint, green_tint, blue_tint));
- }
}
- }
- for (int i = 0; i < 11; i++){
- this.colorPalette[i][7] = (Color.rgb(255, 255, 255)); // last one is always white
+ colorPalette[i][7] = Color.WHITE; // last one is always white
}
colorPaletteAdapter.setColorPalette(colorPalette);
}
public void findSelectedTextColor(int color) {
- /*
- Libreoffice recognizes -1 as Black
- */
- if (color == -1) {
- colorPaletteAdapter.changePosition(0, 0);
- selectedBox = 0;
- updateAdapter();
- return;
- }
- /*
- Find the color if the palette points another color
- */
- if (colorPalette[selectedBox][colorPaletteAdapter.getSelectedBox()] != color) {
- for (int i = 0; i < 11; i++) {
- for (int k = 0; k < 8; k++) {
- if (colorPalette[i][k] == color) {
- colorPaletteAdapter.changePosition(i, k);
- selectedBox = i;
- updateAdapter();
- return;
- }
+ // try to find and highlight the color in the existing palettes
+ for (int i = 0; i < 11; i++) {
+ for (int k = 0; k < 8; k++) {
+ if (colorPalette[i][k] == color) {
+ colorPaletteAdapter.changePosition(i, k);
+ updateAdapter();
+ return;
}
}
}
+
+ // no color in the palettes matched
+ unselectColors();
}
+
private void updateAdapter(){
LOKitShell.getMainHandler().post(new Runnable() {
@Override
@@ -163,4 +159,4 @@ public class ColorPickerAdapter extends RecyclerView.Adapter<ColorPickerAdapter.
this.colorBox = itemView.findViewById(R.id.fontColorBox);
}
}
-} \ No newline at end of file
+}
diff --git a/android/source/src/java/org/libreoffice/DocumentPartViewListAdapter.java b/android/source/src/java/org/libreoffice/DocumentPartViewListAdapter.java
index a576fc67dcb2..a0ed871a40d2 100644
--- a/android/source/src/java/org/libreoffice/DocumentPartViewListAdapter.java
+++ b/android/source/src/java/org/libreoffice/DocumentPartViewListAdapter.java
@@ -19,7 +19,6 @@ import android.widget.TextView;
import java.util.List;
public class DocumentPartViewListAdapter extends ArrayAdapter<DocumentPartView> {
- private static final String LOGTAG = DocumentPartViewListAdapter.class.getSimpleName();
private final Activity activity;
private final ThumbnailCreator thumbnailCollector;
diff --git a/android/source/src/java/org/libreoffice/FontController.java b/android/source/src/java/org/libreoffice/FontController.java
index a00e13e1485c..72f35d8b42d8 100644
--- a/android/source/src/java/org/libreoffice/FontController.java
+++ b/android/source/src/java/org/libreoffice/FontController.java
@@ -2,12 +2,13 @@ package org.libreoffice;
import android.graphics.Color;
import android.graphics.Rect;
-import android.support.design.widget.BottomSheetBehavior;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.RecyclerView;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
@@ -23,12 +24,15 @@ import java.util.Iterator;
public class FontController implements AdapterView.OnItemSelectedListener {
+ /** -1 as value in ".uno:Color" et al. means "automatic color"/no color set. */
+ private static final int COLOR_AUTO = -1;
+
private boolean mFontNameSpinnerSet = false;
private boolean mFontSizeSpinnerSet = false;
private final LibreOfficeMainActivity mActivity;
- private final ArrayList<String> mFontList = new ArrayList<String>();
- private final ArrayList<String> mFontSizes = new ArrayList<String>();
- private final HashMap<String, ArrayList<String>> mAllFontSizes = new HashMap<String, ArrayList<String>>();
+ private final ArrayList<String> mFontList = new ArrayList<>();
+ private final ArrayList<String> mFontSizes = new ArrayList<>();
+ private final HashMap<String, ArrayList<String>> mAllFontSizes = new HashMap<>();
private String mCurrentFontSelected = null;
private String mCurrentFontSizeSelected = null;
@@ -45,29 +49,44 @@ public class FontController implements AdapterView.OnItemSelectedListener {
final ColorPaletteListener colorPaletteListener = new ColorPaletteListener() {
@Override
public void applyColor(int color) {
- sendFontColorChange(color);
+ sendFontColorChange(color, false);
}
@Override
public void updateColorPickerPosition(int color) {
- if (null == colorPickerAdapter) return;
- colorPickerAdapter.findSelectedTextColor(color + 0xFF000000);
- changeFontColorBoxColor(color + 0xFF000000);
+ if (colorPickerAdapter == null) {
+ return;
+ }
+ if (color == COLOR_AUTO) {
+ colorPickerAdapter.unselectColors();
+ changeFontColorBoxColor(Color.TRANSPARENT);
+ return;
+ }
+ final int colorWithAlpha = color | 0xFF000000;
+ colorPickerAdapter.findSelectedTextColor(colorWithAlpha);
+ changeFontColorBoxColor(colorWithAlpha);
}
};
final ColorPaletteListener backColorPaletteListener = new ColorPaletteListener() {
@Override
public void applyColor(int color) {
- sendFontBackColorChange(color);
+ sendFontBackColorChange(color, false);
}
@Override
public void updateColorPickerPosition(int color) {
- if(backColorPickerAdapter != null)
- backColorPickerAdapter.findSelectedTextColor(color + 0xFF000000);
- changeFontBackColorBoxColor(color + 0xFF000000);
-
+ if (backColorPickerAdapter == null) {
+ return;
+ }
+ if (color == COLOR_AUTO) {
+ backColorPickerAdapter.unselectColors();
+ changeFontBackColorBoxColor(Color.TRANSPARENT);
+ return;
+ }
+ final int colorWithAlpha = color | 0xFF000000;
+ backColorPickerAdapter.findSelectedTextColor(colorWithAlpha);
+ changeFontBackColorBoxColor(colorWithAlpha);
}
};
@@ -77,11 +96,7 @@ public class FontController implements AdapterView.OnItemSelectedListener {
LOKitShell.getMainHandler().post(new Runnable() {
@Override
public void run() {
- if(color == -1){ //Libreoffice recognizes -1 as black
- fontColorPickerButton.setBackgroundColor(Color.BLACK);
- }else{
- fontColorPickerButton.setBackgroundColor(color);
- }
+ fontColorPickerButton.setBackgroundColor(color);
}
});
}
@@ -92,12 +107,7 @@ public class FontController implements AdapterView.OnItemSelectedListener {
LOKitShell.getMainHandler().post(new Runnable() {
@Override
public void run() {
- if(color == -1){ //Libreoffice recognizes -1 as black
- fontBackColorPickerButton.setBackgroundColor(Color.BLACK);
- }else{
- fontBackColorPickerButton.setBackgroundColor(color);
-
- }
+ fontBackColorPickerButton.setBackgroundColor(color);
}
});
}
@@ -132,12 +142,12 @@ public class FontController implements AdapterView.OnItemSelectedListener {
}
}
- private void sendFontColorChange(int color){
+ private void sendFontColorChange(int color, boolean keepAlpha){
try {
JSONObject json = new JSONObject();
JSONObject valueJson = new JSONObject();
valueJson.put("type", "long");
- valueJson.put("value", 0x00FFFFFF & color);
+ valueJson.put("value", keepAlpha ? color : 0x00FFFFFF & color);
json.put("Color", valueJson);
LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Color", json.toString()));
@@ -152,21 +162,18 @@ public class FontController implements AdapterView.OnItemSelectedListener {
* 0x00FFFFFF & color operation removes the alpha which is FF,
* if we don't remove it, the color value becomes negative which is not recognized by LOK
*/
- private void sendFontBackColorChange(int color){
+ private void sendFontBackColorChange(int color, boolean keepAlpha) {
try {
JSONObject json = new JSONObject();
JSONObject valueJson = new JSONObject();
valueJson.put("type", "long");
- valueJson.put("value", 0x00FFFFFF & color);
- if(mActivity.isSpreadsheet()){
+ valueJson.put("value", keepAlpha ? color : 0x00FFFFFF & color);
+ if(mActivity.getTileProvider().isSpreadsheet()){
json.put("BackgroundColor", valueJson);
LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:BackgroundColor", json.toString()));
- }else if(mActivity.getTileProvider().isPresentation()){
+ }else {
json.put("CharBackColor", valueJson);
LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:CharBackColor", json.toString()));
- }else {
- json.put("BackColor", valueJson);
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:BackColor", json.toString()));
}
changeFontBackColorBoxColor(color);
@@ -213,7 +220,7 @@ public class FontController implements AdapterView.OnItemSelectedListener {
String key = keys.next();
mFontList.add(key);
JSONArray array = jObject2.getJSONArray(key);
- fontSizes = new ArrayList<String>();
+ fontSizes = new ArrayList<>();
for (int i = 0; i < array.length(); i++) {
fontSizes.add(array.getString(i));
}
@@ -237,14 +244,14 @@ public class FontController implements AdapterView.OnItemSelectedListener {
private void setupFontNameSpinner() {
Spinner fontSpinner = mActivity.findViewById(R.id.font_name_spinner);
- ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_spinner_item, mFontList);
+ ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(mActivity, android.R.layout.simple_spinner_item, mFontList);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
fontSpinner.setAdapter(dataAdapter);
}
private void setupFontSizeSpinner() {
Spinner fontSizeSpinner = mActivity.findViewById(R.id.font_size_spinner);
- ArrayAdapter<String> dataAdapter = new ArrayAdapter<String>(mActivity, android.R.layout.simple_spinner_item, mFontSizes);
+ ArrayAdapter<String> dataAdapter = new ArrayAdapter<>(mActivity, android.R.layout.simple_spinner_item, mFontSizes);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
fontSizeSpinner.setAdapter(dataAdapter);
}
@@ -306,6 +313,10 @@ public class FontController implements AdapterView.OnItemSelectedListener {
fontColorPicker.setOnClickListener(clickListener);
fontColorPickerButton.setOnClickListener(clickListener);
+ final Button autoColorButton = colorPickerLayout.findViewById(R.id.button_auto_color);
+ autoColorButton.setOnClickListener(view -> {
+ sendFontColorChange(COLOR_AUTO, true);
+ });
}
private void setupBackColorPicker(){
@@ -365,6 +376,10 @@ public class FontController implements AdapterView.OnItemSelectedListener {
fontColorPicker.setOnClickListener(clickListener);
fontColorPickerButton.setOnClickListener(clickListener);
+ final Button autoColorButton = backColorPickerLayout.findViewById(R.id.button_auto_color);
+ autoColorButton.setOnClickListener(view -> {
+ sendFontBackColorChange(COLOR_AUTO, true);
+ });
}
public void selectFont(final String fontName) {
diff --git a/android/source/src/java/org/libreoffice/FormattingController.java b/android/source/src/java/org/libreoffice/FormattingController.java
index a34c4c41ee29..49e81eb69784 100644
--- a/android/source/src/java/org/libreoffice/FormattingController.java
+++ b/android/source/src/java/org/libreoffice/FormattingController.java
@@ -11,8 +11,8 @@ import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
-import android.support.design.widget.Snackbar;
-import android.support.v4.content.FileProvider;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.core.content.FileProvider;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -40,7 +40,7 @@ class FormattingController implements View.OnClickListener {
private static final int SELECT_PHOTO = 2;
private static final int IMAGE_BUFFER_SIZE = 4 * 1024;
- private LibreOfficeMainActivity mContext;
+ private final LibreOfficeMainActivity mContext;
private String mCurrentPhotoPath;
FormattingController(LibreOfficeMainActivity context) {
@@ -48,6 +48,8 @@ class FormattingController implements View.OnClickListener {
mContext.findViewById(R.id.button_insertFormatListBullets).setOnClickListener(this);
mContext.findViewById(R.id.button_insertFormatListNumbering).setOnClickListener(this);
+ mContext.findViewById(R.id.button_increaseIndent).setOnClickListener(this);
+ mContext.findViewById(R.id.button_decreaseIndent).setOnClickListener(this);
mContext.findViewById(R.id.button_bold).setOnClickListener(this);
mContext.findViewById(R.id.button_italic).setOnClickListener(this);
@@ -84,70 +86,51 @@ class FormattingController implements View.OnClickListener {
button.getBackground().setState(new int[]{android.R.attr.state_selected});
}
- switch(button.getId()) {
-
- case R.id.button_insertFormatListBullets:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:DefaultBullet"));
- break;
-
- case R.id.button_insertFormatListNumbering:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:DefaultNumbering"));
- break;
-
- case R.id.button_bold:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Bold"));
- break;
- case R.id.button_italic:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Italic"));
- break;
- case R.id.button_strikethrough:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Strikeout"));
- break;
- case R.id.button_clearformatting:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:ResetAttributes"));
- break;
- case R.id.button_underlined:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:UnderlineDouble"));
- break;
- case R.id.button_align_left:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:LeftPara"));
- break;
- case R.id.button_align_center:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:CenterPara"));
- break;
- case R.id.button_align_right:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:RightPara"));
- break;
- case R.id.button_align_justify:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:JustifyPara"));
- break;
- case R.id.button_insert_line:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Line"));
- break;
- case R.id.button_insert_rect:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Rect"));
- break;
- case R.id.button_font_shrink:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Shrink"));
- break;
- case R.id.button_font_grow:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Grow"));
- break;
- case R.id.button_subscript:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SubScript"));
- break;
- case R.id.button_superscript:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SuperScript"));
- break;
- case R.id.button_insert_picture:
- insertPicture();
- break;
- case R.id.button_insert_table:
- insertTable();
- break;
- case R.id.button_delete_table:
- deleteTable();
- break;
+ final int buttonId = button.getId();
+ if (buttonId == R.id.button_insertFormatListBullets) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:DefaultBullet"));
+ } else if (buttonId == R.id.button_insertFormatListNumbering) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:DefaultNumbering"));
+ } else if (buttonId == R.id.button_increaseIndent) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:IncrementIndent"));
+ } else if (buttonId == R.id.button_decreaseIndent) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:DecrementIndent"));
+ } else if (buttonId == R.id.button_bold) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Bold"));
+ } else if (buttonId == R.id.button_italic) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Italic"));
+ } else if (buttonId == R.id.button_strikethrough) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Strikeout"));
+ } else if (buttonId == R.id.button_clearformatting) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:ResetAttributes"));
+ } else if (buttonId == R.id.button_underlined) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:UnderlineDouble"));
+ } else if (buttonId == R.id.button_align_left) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:LeftPara"));
+ } else if (buttonId == R.id.button_align_center) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:CenterPara"));
+ } else if (buttonId == R.id.button_align_right) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:RightPara"));
+ } else if (buttonId == R.id.button_align_justify) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:JustifyPara"));
+ } else if (buttonId == R.id.button_insert_line) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Line"));
+ } else if (buttonId == R.id.button_insert_rect) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Rect"));
+ } else if (buttonId == R.id.button_font_shrink) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Shrink"));
+ } else if (buttonId == R.id.button_font_grow) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Grow"));
+ } else if (buttonId == R.id.button_subscript) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SubScript"));
+ }else if (buttonId == R.id.button_superscript) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SuperScript"));
+ } else if (buttonId == R.id.button_insert_picture) {
+ insertPicture();
+ } else if (buttonId == R.id.button_insert_table) {
+ insertTable();
+ } else if (buttonId == R.id.button_delete_table) {
+ deleteTable();
}
}
@@ -243,15 +226,11 @@ class FormattingController implements View.OnClickListener {
public void onClick(View v) {
int rowCount = Integer.parseInt(npRowCount.getText().toString());
int colCount = Integer.parseInt(npColCount.getText().toString());
- switch (v.getId()){
- case R.id.number_picker_rows_positive:
- if(rowCount < maxValue)
- npRowCount.setText(String.valueOf(++rowCount));
- break;
- case R.id.number_picker_cols_positive:
- if(colCount < maxValue)
- npColCount.setText(String.valueOf(++colCount));
- break;
+ final int id = v.getId();
+ if (id == R.id.number_picker_rows_positive && rowCount < maxValue) {
+ npRowCount.setText(String.valueOf(++rowCount));
+ } else if (id == R.id.number_picker_cols_positive && colCount < maxValue) {
+ npColCount.setText(String.valueOf(++colCount));
}
}
};
@@ -261,15 +240,11 @@ class FormattingController implements View.OnClickListener {
public void onClick(View v) {
int rowCount = Integer.parseInt(npRowCount.getText().toString());
int colCount = Integer.parseInt(npColCount.getText().toString());
- switch (v.getId()){
- case R.id.number_picker_rows_negative:
- if(rowCount > minValue)
- npRowCount.setText(String.valueOf(--rowCount));
- break;
- case R.id.number_picker_cols_negative:
- if(colCount > minValue)
- npColCount.setText(String.valueOf(--colCount));
- break;
+ final int id = v.getId();
+ if (id == R.id.number_picker_rows_negative && rowCount > minValue) {
+ npRowCount.setText(String.valueOf(--rowCount));
+ } else if (id == R.id.number_picker_cols_negative && colCount > minValue) {
+ npColCount.setText(String.valueOf(--colCount));
}
}
};
@@ -421,15 +396,14 @@ class FormattingController implements View.OnClickListener {
void handleActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == TAKE_PHOTO && resultCode == Activity.RESULT_OK) {
- mContext.pendingInsertGraphic = true;
+ compressAndInsertImage();
} else if (requestCode == SELECT_PHOTO && resultCode == Activity.RESULT_OK) {
getFileFromURI(data.getData());
- mContext.pendingInsertGraphic = true;
+ compressAndInsertImage();
}
}
- // Called by LOKitTileProvider when activity is resumed from photo/gallery/camera/cloud apps
- void popCompressImageGradeSelection() {
+ void compressAndInsertImage() {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
String[] options = {mContext.getResources().getString(R.string.compress_photo_smallest_size),
mContext.getResources().getString(R.string.compress_photo_medium_size),
@@ -493,7 +467,6 @@ class FormattingController implements View.OnClickListener {
LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:InsertGraphic", rootJson.toString()));
LOKitShell.sendEvent(new LOEvent(LOEvent.REFRESH));
mContext.setDocumentChanged(true);
- mContext.pendingInsertGraphic = false;
}
private void compressImage(int grade) {
diff --git a/android/source/src/java/org/libreoffice/InvalidationHandler.java b/android/source/src/java/org/libreoffice/InvalidationHandler.java
index 32e9b56656dd..c48127cce67f 100644
--- a/android/source/src/java/org/libreoffice/InvalidationHandler.java
+++ b/android/source/src/java/org/libreoffice/InvalidationHandler.java
@@ -25,12 +25,12 @@ import java.util.List;
* Parses (interprets) and handles invalidation messages from LibreOffice.
*/
public class InvalidationHandler implements Document.MessageCallback, Office.MessageCallback {
- private static String LOGTAG = InvalidationHandler.class.getSimpleName();
+ private static final String LOGTAG = InvalidationHandler.class.getSimpleName();
private final DocumentOverlay mDocumentOverlay;
private final GeckoLayerClient mLayerClient;
private OverlayState mState;
private boolean mKeyEvent = false;
- private LibreOfficeMainActivity mContext;
+ private final LibreOfficeMainActivity mContext;
private int currentTotalPageNumber = 0; // total page number of the current document
@@ -55,6 +55,7 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
&& messageID != Document.CALLBACK_DOCUMENT_PASSWORD
&& messageID != Document.CALLBACK_HYPERLINK_CLICKED
&& messageID != Document.CALLBACK_SEARCH_RESULT_SELECTION
+ && messageID != Document.CALLBACK_SC_FOLLOW_JUMP
&& messageID != Document.CALLBACK_TEXT_SELECTION
&& messageID != Document.CALLBACK_TEXT_SELECTION_START
&& messageID != Document.CALLBACK_TEXT_SELECTION_END)
@@ -114,6 +115,9 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
case Document.CALLBACK_CELL_CURSOR:
invalidateCellCursor(payload);
break;
+ case Document.CALLBACK_SC_FOLLOW_JUMP:
+ jumpToCell(payload);
+ break;
case Document.CALLBACK_INVALIDATE_HEADER:
invalidateHeader();
break;
@@ -139,7 +143,7 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
JSONObject payloadObject = new JSONObject(payload);
if (payloadObject.getString("commandName").equals(".uno:Save")) {
if (payloadObject.getString("success").equals("true")) {
- mContext.saveFilesToCloud();
+ mContext.saveFileToOriginalSource();
}
}else if(payloadObject.getString("commandName").equals(".uno:Name") ||
payloadObject.getString("commandName").equals(".uno:RenamePage")){
@@ -214,6 +218,14 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
}
}
+ private void jumpToCell(String payload) {
+ RectF cellCursorRect = convertPayloadCellToRectangle(payload);
+
+ if (cellCursorRect != null) {
+ moveViewportToMakeSelectionVisible(cellCursorRect);
+ }
+ }
+
/**
* Handles the search result selection message, which is a JSONObject
*
@@ -330,7 +342,7 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
mContext.getFormattingController().onToggleStateChanged(Document.NUMBERED_LIST, pressed);
} else if (parts[0].equals(".uno:Color")) {
mContext.getFontController().colorPaletteListener.updateColorPickerPosition(Integer.parseInt(value));
- } else if (mContext.getTileProvider().isTextDocument() && parts[0].equals(".uno:BackColor")) {
+ } else if (mContext.getTileProvider().isTextDocument() && (parts[0].equals(".uno:BackColor") || parts[0].equals(".uno:CharBackColor"))) {
mContext.getFontController().backColorPaletteListener.updateColorPickerPosition(Integer.parseInt(value));
} else if (mContext.getTileProvider().isPresentation() && parts[0].equals(".uno:CharBackColor")) {
mContext.getFontController().backColorPaletteListener.updateColorPickerPosition(Integer.parseInt(value));
@@ -368,6 +380,40 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
if (coordinates.length != 4) {
return null;
}
+ return convertPayloadToRectangle(coordinates);
+ }
+
+ /**
+ * Parses the payload text with rectangle coordinates and converts to rectangle in pixel coordinates
+ *
+ * @param payload - invalidation message payload text
+ * @return rectangle in pixel coordinates
+ */
+ public RectF convertPayloadCellToRectangle(String payload) {
+ String payloadWithoutWhitespace = payload.replaceAll("\\s", ""); // remove all whitespace from the string
+
+ if (payloadWithoutWhitespace.isEmpty() || payloadWithoutWhitespace.equals("EMPTY")) {
+ return null;
+ }
+
+ String[] coordinates = payloadWithoutWhitespace.split(",");
+
+ if (coordinates.length != 6 ) {
+ return null;
+ }
+ return convertPayloadToRectangle(coordinates);
+ }
+
+ /**
+ * Converts rectangle coordinates to rectangle in pixel coordinates
+ *
+ * @param coordinates - the first four items defines the rectangle
+ * @return rectangle in pixel coordinates
+ */
+ public RectF convertPayloadToRectangle(String[] coordinates) {
+ if (coordinates.length < 4 ) {
+ return null;
+ }
int x = Integer.decode(coordinates[0]);
int y = Integer.decode(coordinates[1]);
@@ -377,10 +423,10 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
float dpi = LOKitShell.getDpi(mContext);
return new RectF(
- LOKitTileProvider.twipToPixel(x, dpi),
- LOKitTileProvider.twipToPixel(y, dpi),
- LOKitTileProvider.twipToPixel(x + width, dpi),
- LOKitTileProvider.twipToPixel(y + height, dpi)
+ LOKitTileProvider.twipToPixel(x, dpi),
+ LOKitTileProvider.twipToPixel(y, dpi),
+ LOKitTileProvider.twipToPixel(x + width, dpi),
+ LOKitTileProvider.twipToPixel(y + height, dpi)
);
}
@@ -505,7 +551,7 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
changeStateTo(OverlayState.TRANSITION);
}
mDocumentOverlay.changeSelections(Collections.<RectF>emptyList());
- if (mContext.isSpreadsheet()) {
+ if (mContext.getTileProvider().isSpreadsheet()) {
mDocumentOverlay.showHeaderSelection(null);
}
mContext.getToolbarController().showHideClipboardCutAndCopy(false);
@@ -516,7 +562,7 @@ public class InvalidationHandler implements Document.MessageCallback, Office.Mes
}
changeStateTo(OverlayState.SELECTION);
mDocumentOverlay.changeSelections(rectangles);
- if (mContext.isSpreadsheet()) {
+ if (mContext.getTileProvider().isSpreadsheet()) {
mDocumentOverlay.showHeaderSelection(rectangles.get(0));
}
String selectedText = mContext.getTileProvider().getTextSelection("");
diff --git a/android/source/src/java/org/libreoffice/LOEvent.java b/android/source/src/java/org/libreoffice/LOEvent.java
index 4db48a5bbd6d..d1170eee12ad 100644
--- a/android/source/src/java/org/libreoffice/LOEvent.java
+++ b/android/source/src/java/org/libreoffice/LOEvent.java
@@ -33,7 +33,6 @@ public class LOEvent implements Comparable<LOEvent> {
public static final int SWIPE_LEFT = 12;
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 static final int UPDATE_PART_PAGE_RECT = 18;
@@ -42,6 +41,7 @@ public class LOEvent implements Comparable<LOEvent> {
public static final int REFRESH = 21;
public static final int PAGE_SIZE_CHANGED = 22;
public static final int UNO_COMMAND_NOTIFY = 23;
+ public static final int SAVE_COPY_AS = 24;
public final int mType;
@@ -104,13 +104,6 @@ public class LOEvent implements Comparable<LOEvent> {
mValue = value;
}
- public LOEvent(int type, String key, int value) {
- mType = type;
- mTypeString = "Resume partIndex";
- mString = key;
- mPartIndex = value;
- }
-
public LOEvent(String filePath, int type) {
mType = type;
mTypeString = "Load";
diff --git a/android/source/src/java/org/libreoffice/LOKitInputConnectionHandler.java b/android/source/src/java/org/libreoffice/LOKitInputConnectionHandler.java
index bbef709af297..7b50ef5ff707 100644
--- a/android/source/src/java/org/libreoffice/LOKitInputConnectionHandler.java
+++ b/android/source/src/java/org/libreoffice/LOKitInputConnectionHandler.java
@@ -19,7 +19,7 @@ import org.mozilla.gecko.gfx.InputConnectionHandler;
* directed to this class which is then directed further to LOKitThread.
*/
public class LOKitInputConnectionHandler implements InputConnectionHandler {
- private static String LOGTAG = LOKitInputConnectionHandler.class.getSimpleName();
+ private static final String LOGTAG = LOKitInputConnectionHandler.class.getSimpleName();
@Override
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
diff --git a/android/source/src/java/org/libreoffice/LOKitShell.java b/android/source/src/java/org/libreoffice/LOKitShell.java
index c69e02669619..f6a76228e008 100644
--- a/android/source/src/java/org/libreoffice/LOKitShell.java
+++ b/android/source/src/java/org/libreoffice/LOKitShell.java
@@ -24,10 +24,10 @@ import org.mozilla.gecko.gfx.ComposedTileLayer;
* Common static LOKit functions, functions to send events.
*/
public class LOKitShell {
- private static final String LOGTAG = LOKitShell.class.getSimpleName();
-
public static float getDpi(Context context) {
- if (((LibreOfficeMainActivity)context).isSpreadsheet()) return 96f;
+ LOKitTileProvider tileProvider = ((LibreOfficeMainActivity)context).getTileProvider();
+ if (tileProvider != null && tileProvider.isSpreadsheet())
+ return 96f;
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.density * 160;
}
@@ -61,7 +61,7 @@ public class LOKitShell {
}
public static boolean isEditingEnabled() {
- return LibreOfficeMainActivity.isExperimentalMode();
+ return !LibreOfficeMainActivity.isReadOnlyMode();
}
// EVENTS
@@ -119,8 +119,8 @@ public class LOKitShell {
LOKitShell.sendEvent(new LOEvent(filePath, fileFormat, LOEvent.SAVE_AS));
}
- public static void sendResumeEvent(String inputFile, int partIndex) {
- LOKitShell.sendEvent(new LOEvent(LOEvent.RESUME, inputFile, partIndex));
+ public static void sendSaveCopyAsEvent(String filePath, String fileFormat) {
+ LOKitShell.sendEvent(new LOEvent(filePath, fileFormat, LOEvent.SAVE_COPY_AS));
}
public static void sendCloseEvent() {
diff --git a/android/source/src/java/org/libreoffice/LOKitThread.java b/android/source/src/java/org/libreoffice/LOKitThread.java
index e554f0800cf0..fd40c3089102 100644
--- a/android/source/src/java/org/libreoffice/LOKitThread.java
+++ b/android/source/src/java/org/libreoffice/LOKitThread.java
@@ -7,7 +7,6 @@ import android.util.Log;
import android.view.KeyEvent;
import org.libreoffice.canvas.SelectionHandle;
-import org.libreoffice.ui.LibreOfficeUIActivity;
import org.mozilla.gecko.ZoomConstraints;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.ComposedTileLayer;
@@ -26,13 +25,13 @@ import java.util.concurrent.LinkedBlockingQueue;
class LOKitThread extends Thread {
private static final String LOGTAG = LOKitThread.class.getSimpleName();
- private LinkedBlockingQueue<LOEvent> mEventQueue = new LinkedBlockingQueue<LOEvent>();
+ private final LinkedBlockingQueue<LOEvent> mEventQueue = new LinkedBlockingQueue<LOEvent>();
private TileProvider mTileProvider;
private InvalidationHandler mInvalidationHandler;
private ImmutableViewportMetrics mViewportMetrics;
private GeckoLayerClient mLayerClient;
- private LibreOfficeMainActivity mContext;
+ private final LibreOfficeMainActivity mContext;
LOKitThread(LibreOfficeMainActivity context) {
mContext = context;
@@ -112,7 +111,7 @@ class LOKitThread extends Thread {
/**
* Handle the geometry change + draw.
*/
- private void redraw() {
+ private void redraw(boolean resetZoomAndPosition) {
if (mLayerClient == null || mTileProvider == null) {
// called too early...
return;
@@ -122,7 +121,9 @@ class LOKitThread extends Thread {
mViewportMetrics = mLayerClient.getViewportMetrics();
mLayerClient.setViewportMetrics(mViewportMetrics);
- zoomAndRepositionTheDocument();
+ if (resetZoomAndPosition) {
+ zoomAndRepositionTheDocument();
+ }
mLayerClient.forceRedraw();
mLayerClient.forceRender();
@@ -152,9 +153,9 @@ class LOKitThread extends Thread {
/**
* Invalidate everything + handle the geometry change
*/
- private void refresh() {
+ private void refresh(boolean resetZoomAndPosition) {
mLayerClient.clearAndResetlayers();
- redraw();
+ redraw(resetZoomAndPosition);
updatePartPageRectangles();
if (mTileProvider != null && mTileProvider.isSpreadsheet()) {
updateCalcHeaders();
@@ -177,35 +178,17 @@ class LOKitThread extends Thread {
private void updatePageSize(int pageWidth, int pageHeight){
mTileProvider.setDocumentSize(pageWidth, pageHeight);
- redraw();
+ redraw(true);
}
private void updateZoomConstraints() {
if (mTileProvider == null) return;
mLayerClient = mContext.getLayerClient();
- // Set min zoom to the page width so that you cannot zoom below page width
- final float minZoom = mLayerClient.getViewportMetrics().getWidth()/mTileProvider.getPageWidth();
- mLayerClient.setZoomConstraints(new ZoomConstraints(true, 1f, minZoom, 0f));
- }
-
-
- /**
- * Resume the document with the current part
- */
-
- private void resumeDocument(String filename, int partIndex){
-
- mLayerClient = mContext.getLayerClient();
-
- mInvalidationHandler = new InvalidationHandler(mContext);
- mTileProvider = TileProviderFactory.create(mContext, mInvalidationHandler, filename);
-
- if (mTileProvider.isReady()) {
- updateZoomConstraints();
- changePart(partIndex);
- } else {
- closeDocument();
- }
+ // Set default zoom to the page width and min zoom so that the whole page is visible
+ final float pageHeightZoom = mLayerClient.getViewportMetrics().getHeight() / mTileProvider.getPageHeight();
+ final float pageWidthZoom = mLayerClient.getViewportMetrics().getWidth() / mTileProvider.getPageWidth();
+ final float minZoom = Math.min(pageWidthZoom, pageHeightZoom);
+ mLayerClient.setZoomConstraints(new ZoomConstraints(pageWidthZoom, minZoom, 0f));
}
/**
@@ -216,15 +199,16 @@ class LOKitThread extends Thread {
mTileProvider.changePart(partIndex);
mViewportMetrics = mLayerClient.getViewportMetrics();
// mLayerClient.setViewportMetrics(mViewportMetrics.scaleTo(0.9f, new PointF()));
- refresh();
+ refresh(true);
LOKitShell.hideProgressSpinner(mContext);
}
/**
* Handle load document event.
* @param filePath - filePath to where the document is located
+ * @return Whether the document has been loaded successfully.
*/
- private void loadDocument(String filePath) {
+ private boolean loadDocument(String filePath) {
mLayerClient = mContext.getLayerClient();
mInvalidationHandler = new InvalidationHandler(mContext);
@@ -233,18 +217,12 @@ class LOKitThread extends Thread {
if (mTileProvider.isReady()) {
LOKitShell.showProgressSpinner(mContext);
updateZoomConstraints();
- LOKitShell.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- // synchronize to avoid deletion while loading
- synchronized (LOKitThread.this) {
- refresh();
- }
- }
- });
+ refresh(true);
LOKitShell.hideProgressSpinner(mContext);
+ return true;
} else {
closeDocument();
+ return false;
}
}
@@ -254,47 +232,27 @@ class LOKitThread extends Thread {
* @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, fileType);
-
- if (mTileProvider.isReady()) {
- LOKitShell.showProgressSpinner(mContext);
- updateZoomConstraints();
- 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();
+ boolean ok = loadDocument(fileType);
+ if (ok) {
+ mTileProvider.saveDocumentAs(filePath, true);
}
}
/**
* Save the currently loaded document.
*/
- private void saveDocumentAs(String filePath, String fileType) {
+ private void saveDocumentAs(String filePath, String fileType, boolean bTakeOwnership) {
if (mTileProvider == null) {
Log.e(LOGTAG, "Error in saving, Tile Provider instance is null");
} else {
- mTileProvider.saveDocumentAs(filePath, fileType);
+ mTileProvider.saveDocumentAs(filePath, fileType, bTakeOwnership);
}
}
/**
* Close the currently loaded document.
*/
- // needs to be synchronized to not destroy doc while it's loaded
- private synchronized void closeDocument() {
+ private void closeDocument() {
if (mTileProvider != null) {
mTileProvider.close();
mTileProvider = null;
@@ -313,16 +271,16 @@ class LOKitThread extends Thread {
loadNewDocument(event.filePath, event.fileType);
break;
case LOEvent.SAVE_AS:
- saveDocumentAs(event.filePath, event.fileType);
+ saveDocumentAs(event.filePath, event.fileType, true);
break;
- case LOEvent.RESUME:
- resumeDocument(event.mString, event.mPartIndex);
+ case LOEvent.SAVE_COPY_AS:
+ saveDocumentAs(event.filePath, event.fileType, false);
break;
case LOEvent.CLOSE:
closeDocument();
break;
case LOEvent.SIZE_CHANGED:
- redraw();
+ redraw(false);
break;
case LOEvent.CHANGE_PART:
changePart(event.mPartIndex);
@@ -376,7 +334,7 @@ class LOKitThread extends Thread {
mTileProvider.postUnoCommand(event.mString, event.mValue, event.mNotify);
break;
case LOEvent.REFRESH:
- refresh();
+ refresh(false);
break;
case LOEvent.PAGE_SIZE_CHANGED:
updatePageSize(event.mPageWidth, event.mPageHeight);
@@ -448,7 +406,7 @@ class LOKitThread extends Thread {
boolean editing = LOKitShell.isEditingEnabled();
float zoomFactor = mViewportMetrics.getZoomFactor();
- if (touchType.equals("LongPress") && editing) {
+ if (touchType.equals("LongPress")) {
mInvalidationHandler.changeStateTo(InvalidationHandler.OverlayState.TRANSITION);
mTileProvider.mouseButtonDown(documentCoordinate, 1, zoomFactor);
mTileProvider.mouseButtonUp(documentCoordinate, 1, zoomFactor);
diff --git a/android/source/src/java/org/libreoffice/LOKitTileProvider.java b/android/source/src/java/org/libreoffice/LOKitTileProvider.java
index 0e2649337322..5d1cf12209dc 100644
--- a/android/source/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/source/src/java/org/libreoffice/LOKitTileProvider.java
@@ -12,7 +12,6 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.PointF;
import android.os.Build;
-import android.os.Environment;
import android.print.PrintAttributes;
import android.print.PrintDocumentAdapter;
import android.print.PrintManager;
@@ -26,7 +25,6 @@ import org.libreoffice.kit.DirectBufferAllocator;
import org.libreoffice.kit.Document;
import org.libreoffice.kit.LibreOfficeKit;
import org.libreoffice.kit.Office;
-import org.libreoffice.ui.FileUtilities;
import org.mozilla.gecko.gfx.BufferedCairoImage;
import org.mozilla.gecko.gfx.CairoImage;
import org.mozilla.gecko.gfx.IntSize;
@@ -39,22 +37,22 @@ import java.nio.ByteBuffer;
*/
class LOKitTileProvider implements TileProvider {
private static final String LOGTAG = LOKitTileProvider.class.getSimpleName();
- private static int TILE_SIZE = 256;
+ private static final int TILE_SIZE = 256;
private final float mTileWidth;
private final float mTileHeight;
- private final String mInputFile;
+ private String mInputFile;
private Office mOffice;
private Document mDocument;
- private boolean mIsReady = false;
- private LibreOfficeMainActivity mContext;
+ private final boolean mIsReady;
+ private final LibreOfficeMainActivity mContext;
- private float mDPI;
+ private final float mDPI;
private float mWidthTwip;
private float mHeightTwip;
- private Document.MessageCallback mMessageCallback;
+ private final Document.MessageCallback mMessageCallback;
- private long objectCreationTime = System.currentTimeMillis();
+ private final long objectCreationTime = System.currentTimeMillis();
/**
* Initialize LOKit and load the document.
@@ -73,32 +71,16 @@ class LOKitTileProvider implements TileProvider {
mOffice.setOptionalFeatures(Document.LOK_FEATURE_DOCUMENT_PASSWORD);
mContext.setTileProvider(this);
mInputFile = input;
- File f = new File(mInputFile);
- final String cacheFile = mContext.getExternalCacheDir().getAbsolutePath() + "/lo_cached_" + f.getName();
-
- if(mContext.firstStart){
- File cacheFileObj = new File(cacheFile);
- if(cacheFileObj.exists()) {
- cacheFileObj.delete();
- }
- mContext.firstStart=false;
- }
Log.i(LOGTAG, "====> Loading file '" + input + "'");
- File fileToBeEncoded;
- if(isDocumentCached()){
- fileToBeEncoded = new File(cacheFile);
- }else{
- fileToBeEncoded = new File(input);
- }
+ File fileToBeEncoded = new File(input);
String encodedFileName = android.net.Uri.encode(fileToBeEncoded.getName());
mDocument = mOffice.documentLoad(
(new File(fileToBeEncoded.getParent(),encodedFileName)).getPath()
);
-
if (mDocument == null && !mContext.isPasswordProtected()) {
Log.i(LOGTAG, "====> mOffice.documentLoad() returned null, trying to restart 'Office' and loading again");
mOffice.destroy();
@@ -117,10 +99,6 @@ class LOKitTileProvider implements TileProvider {
Log.i(LOGTAG, "====> mDocument = " + mDocument);
- if(isSpreadsheet()) {
- mContext.setIsSpreadsheet(true); // Calc is treated differently e.g. DPI = 96f
- }
-
mDPI = LOKitShell.getDpi(mContext);
mTileWidth = pixelToTwip(TILE_SIZE, mDPI);
mTileHeight = pixelToTwip(TILE_SIZE, mDPI);
@@ -142,27 +120,9 @@ class LOKitTileProvider implements TileProvider {
private void postLoad() {
mDocument.setMessageCallback(mMessageCallback);
- int parts = mDocument.getParts();
- Log.i(LOGTAG, "Document parts: " + parts);
- mContext.getDocumentPartView().clear();
-
+ resetParts();
// Writer documents always have one part, so hide the navigation drawer.
- if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) {
- for (int i = 0; i < parts; i++) {
- String partName = mDocument.getPartName(i);
- if (partName.isEmpty()) {
- partName = getGenericPartName(i);
- }else if (partName.startsWith("Slide") || partName.startsWith("Sheet") || partName.startsWith("Part")) {
- partName = getGenericPartName(i);
- }
- Log.i(LOGTAG, "Document part " + i + " name:'" + partName + "'");
-
- mDocument.setPart(i);
- resetDocumentSize();
- final DocumentPartView partView = new DocumentPartView(i, partName);
- mContext.getDocumentPartView().add(partView);
- }
- } else {
+ if (mDocument.getDocumentType() == Document.DOCTYPE_TEXT) {
mContext.disableNavigationDrawer();
mContext.getToolbarController().hideItem(R.id.action_parts);
}
@@ -182,14 +142,6 @@ class LOKitTileProvider implements TileProvider {
mContext.getDocumentPartViewListAdapter().notifyDataSetChanged();
}
});
- mContext.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- if (mContext.pendingInsertGraphic) {
- mContext.getFormattingController().popCompressImageGradeSelection();
- }
- }
- });
}
public void addPart(){
@@ -224,9 +176,9 @@ class LOKitTileProvider implements TileProvider {
}
public void resetParts(){
- int parts = mDocument.getParts();
mContext.getDocumentPartView().clear();
if (mDocument.getDocumentType() != Document.DOCTYPE_TEXT) {
+ int parts = mDocument.getParts();
for (int i = 0; i < parts; i++) {
String partName = mDocument.getPartName(i);
@@ -240,7 +192,9 @@ class LOKitTileProvider implements TileProvider {
mContext.getDocumentPartView().add(partView);
}
}
- } public void renamePart(String partName) {
+ }
+
+ public void renamePart(String partName) {
try{
for(int i=0; i<mDocument.getParts(); i++){
if(mContext.getDocumentPartView().get(i).partName.equals(partName)){
@@ -270,7 +224,7 @@ class LOKitTileProvider implements TileProvider {
public void removePart() {
try{
- if(isSpreadsheet() == false && isPresentation() == false) {
+ if (!isSpreadsheet() && !isPresentation()) {
//document must be spreadsheet or presentation
return;
}
@@ -295,38 +249,37 @@ class LOKitTileProvider implements TileProvider {
}
}
-
-
@Override
- public void saveDocumentAs(final String filePath, String format) {
+ public boolean saveDocumentAs(final String filePath, String format, boolean takeOwnership) {
+ String options = "";
+ if (takeOwnership) {
+ options = "TakeOwnership";
+ }
+
final String newFilePath = "file://" + filePath;
Log.d("saveFilePathURL", newFilePath);
LOKitShell.showProgressSpinner(mContext);
- mDocument.saveAs(newFilePath, format, "");
+ mDocument.saveAs(newFilePath, format, options);
+ final boolean ok;
if (!mOffice.getError().isEmpty()){
+ ok = true;
Log.e("Save Error", mOffice.getError());
if (format.equals("svg")) {
// error in creating temp slideshow svg file
Log.d(LOGTAG, "Error in creating temp slideshow svg file");
} else if(format.equals("pdf")){
Log.d(LOGTAG, "Error in creating pdf file");
+ } else {
LOKitShell.getMainHandler().post(new Runnable() {
@Override
public void run() {
// There was some error
- mContext.showCustomStatusMessage(mContext.getString(R.string.unable_to_export_pdf));
- }
- });
- }else {
- LOKitShell.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- // There was some error
- mContext.showSaveStatusMessage(true);
+ mContext.showCustomStatusMessage(mContext.getString(R.string.unable_to_save));
}
});
}
} else {
+ ok = false;
if (format.equals("svg")) {
// successfully created temp slideshow svg file
LOKitShell.getMainHandler().post(new Runnable() {
@@ -335,99 +288,49 @@ class LOKitTileProvider implements TileProvider {
mContext.startPresentation(newFilePath);
}
});
- }else if(format.equals("pdf")){
- LOKitShell.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- // There was no error
- mContext.showCustomStatusMessage(mContext.getString(R.string.pdf_exported_at)+filePath);
- }
- });
- } else {
- LOKitShell.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- // There was no error
- mContext.showSaveStatusMessage(false);
- }
- });
+ } else if (takeOwnership) {
+ mInputFile = filePath;
}
}
LOKitShell.hideProgressSpinner(mContext);
+ return ok;
}
- public void exportToPDF(boolean print){
- String dir = Environment.getExternalStorageDirectory().getAbsolutePath()+"/Documents";
- File docDir = new File(dir);
- if(!docDir.exists()){
- docDir.mkdir();
- }
- String mInputFileName = (new File(mInputFile)).getName();
- String file = mInputFileName.substring(0,(mInputFileName.length()-3))+"pdf";
- if(print){
- String cacheFile = mContext.getExternalCacheDir().getAbsolutePath()
- + "/" + file;
- mDocument.saveAs("file://"+cacheFile,"pdf","");
- printDocument(cacheFile);
- }else{
- saveDocumentAs(dir+"/"+file,"pdf");
- }
- }
-
- private void printDocument(String cacheFile) {
- if (Build.VERSION.SDK_INT >= 19) {
- try {
- PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
- PrintDocumentAdapter printAdapter = new PDFDocumentAdapter(mContext, cacheFile);
- printManager.print("Document", printAdapter, new PrintAttributes.Builder().build());
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- } else {
- mContext.showCustomStatusMessage(mContext.getString(R.string.printing_not_supported));
- }
- }
-
- public boolean isDocumentCached(){
- File input = new File(mInputFile);
- final String cacheFile = mContext.getExternalCacheDir().getAbsolutePath() + "/lo_cached_" + input.getName();
- File cacheFileObj = new File(cacheFile);
- if(cacheFileObj.exists())
- return true;
-
+ @Override
+ public boolean saveDocumentAs(final String filePath, boolean takeOwnership) {
+ final int docType = mDocument.getDocumentType();
+ if (docType == Document.DOCTYPE_TEXT)
+ return saveDocumentAs(filePath, "odt", takeOwnership);
+ else if (docType == Document.DOCTYPE_SPREADSHEET)
+ return saveDocumentAs(filePath, "ods", takeOwnership);
+ else if (docType == Document.DOCTYPE_PRESENTATION)
+ return saveDocumentAs(filePath, "odp", takeOwnership);
+ else if (docType == Document.DOCTYPE_DRAWING)
+ return saveDocumentAs(filePath, "odg", takeOwnership);
+
+ Log.w(LOGTAG, "Cannot determine file format from document. Not saving.");
return false;
}
- public void cacheDocument() {
- String cacheDir = mContext.getExternalCacheDir().getAbsolutePath();
- File input = new File(mInputFile);
- final String cacheFile = cacheDir + "/lo_cached_" + input.getName();
- Log.i(LOGTAG, "cacheDocument: " + cacheFile);
- if(isDocumentCached()){
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Save"));
- }else if(mDocument != null){
- mDocument.saveAs("file://"+cacheFile, FileUtilities.getExtension(input.getPath()).substring(1),"");
- }else{
- Log.w(LOGTAG, "mDocument was null when trying to save cacheDocument: " + cacheFile);
+ public void printDocument() {
+ String mInputFileName = (new File(mInputFile)).getName();
+ String file = mInputFileName.substring(0,(mInputFileName.length()-3))+"pdf";
+ String cacheFile = mContext.getExternalCacheDir().getAbsolutePath() + "/" + file;
+ mDocument.saveAs("file://"+cacheFile,"pdf","");
+ try {
+ PrintManager printManager = (PrintManager) mContext.getSystemService(Context.PRINT_SERVICE);
+ PrintDocumentAdapter printAdapter = new PDFDocumentAdapter(mContext, cacheFile);
+ printManager.print("Document", printAdapter, new PrintAttributes.Builder().build());
+
+ } catch (Exception e) {
+ e.printStackTrace();
}
}
public void saveDocument(){
- if(isDocumentCached()){
- String format = FileUtilities.getExtension(mInputFile).substring(1);
- String cacheDir = mContext.getExternalCacheDir().getAbsolutePath();
- File input = new File(mInputFile);
- final String cacheFile = cacheDir + "/lo_cached_" + input.getName();
- String path = input.getAbsolutePath();
- saveDocumentAs(path, format);
- (new File(cacheFile)).delete();
- }else{
- mContext.saveDocument();
- }
+ mContext.saveDocument();
}
-
private void setupDocumentFonts() {
String values = mDocument.getCommandValues(".uno:CharFontName");
if (values == null || values.isEmpty())
@@ -674,6 +577,14 @@ class LOKitTileProvider implements TileProvider {
}
/**
+ * @see TileProvider#isDrawing()
+ */
+ @Override
+ public boolean isDrawing() {
+ return mDocument != null && mDocument.getDocumentType() == Document.DOCTYPE_DRAWING;
+ }
+
+ /**
* @see TileProvider#isTextDocument()
*/
@Override
diff --git a/android/source/src/java/org/libreoffice/LibreOfficeApplication.java b/android/source/src/java/org/libreoffice/LibreOfficeApplication.java
index cb79219fc999..ebe54cf27c64 100644
--- a/android/source/src/java/org/libreoffice/LibreOfficeApplication.java
+++ b/android/source/src/java/org/libreoffice/LibreOfficeApplication.java
@@ -10,11 +10,11 @@
package org.libreoffice;
-import android.app.Application;
import android.content.Context;
import android.os.Handler;
+import androidx.multidex.MultiDexApplication;
-public class LibreOfficeApplication extends Application {
+public class LibreOfficeApplication extends MultiDexApplication {
private static Handler mainHandler;
diff --git a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
index cbc628e94e48..cf60ff37c5da 100644
--- a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -1,6 +1,5 @@
package org.libreoffice;
-import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
@@ -9,19 +8,18 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
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.preference.PreferenceManager;
-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;
+import android.provider.DocumentsContract;
+import com.google.android.material.bottomsheet.BottomSheetBehavior;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
import android.text.InputType;
import android.util.Log;
import android.view.KeyEvent;
@@ -36,24 +34,25 @@ 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;
import org.mozilla.gecko.gfx.LayerView;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.net.URI;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.ArrayList;
import java.util.List;
+import java.util.UUID;
/**
* Main activity of the LibreOffice App. It is started in the UI thread.
@@ -61,10 +60,11 @@ import java.util.List;
public class LibreOfficeMainActivity extends AppCompatActivity implements SettingsListenerModel.OnSettingsPreferenceChangedListener {
private static final String LOGTAG = "LibreOfficeMainActivity";
- private static final String DEFAULT_DOC_PATH = "/assets/example.odt";
- private static final String ENABLE_EXPERIMENTAL_PREFS_KEY = "ENABLE_EXPERIMENTAL";
+ public static final String ENABLE_EXPERIMENTAL_PREFS_KEY = "ENABLE_EXPERIMENTAL";
private static final String ASSETS_EXTRACTED_PREFS_KEY = "ASSETS_EXTRACTED";
private static final String ENABLE_DEVELOPER_PREFS_KEY = "ENABLE_DEVELOPER";
+ private static final int REQUEST_CODE_SAVEAS = 12345;
+ private static final int REQUEST_CODE_EXPORT_TO_PDF = 12346;
//TODO "public static" is a temporary workaround
public static LOKitThread loKitThread;
@@ -73,23 +73,20 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
private static boolean mIsExperimentalMode;
private static boolean mIsDeveloperMode;
-
- private int providerId;
- private URI documentUri;
+ private static boolean mbISReadOnlyMode;
private DrawerLayout mDrawerLayout;
Toolbar toolbarTop;
private ListView mDrawerList;
- private List<DocumentPartView> mDocumentPartView = new ArrayList<DocumentPartView>();
+ private final List<DocumentPartView> mDocumentPartView = new ArrayList<DocumentPartView>();
private DocumentPartViewListAdapter mDocumentPartViewListAdapter;
- private int partIndex=-1;
- private File mInputFile;
private DocumentOverlay mDocumentOverlay;
+ /** URI to save the document to. */
+ private Uri mDocumentUri;
+ /** Temporary local copy of the document. */
private File mTempFile = null;
private File mTempSlideShowFile = null;
- private String newDocumentType = null;
- public boolean firstStart = true;
BottomSheetBehavior bottomToolbarSheetBehavior;
BottomSheetBehavior toolbarColorPickerBottomSheetBehavior;
@@ -100,11 +97,10 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
private SearchController mSearchController;
private UNOCommandsController mUNOCommandsController;
private CalcHeadersController mCalcHeadersController;
- private boolean mIsSpreadsheet;
private LOKitTileProvider mTileProvider;
private String mPassword;
private boolean mPasswordProtected;
- public boolean pendingInsertGraphic; // boolean indicating a pending insert graphic action, used in LOKitTileProvider.postLoad()
+ private boolean mbSkipNextRefresh;
public GeckoLayerClient getLayerClient() {
return mLayerClient;
@@ -118,17 +114,11 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
return mIsDeveloperMode;
}
- public boolean usesTemporaryFile() {
- return mTempFile != null;
- }
-
private boolean isKeyboardOpen = false;
private boolean isFormattingToolbarOpen = false;
private boolean isSearchToolbarOpen = false;
private static boolean isDocumentChanged = false;
private boolean isUNOCommandsToolbarOpen = false;
- public boolean isNewDocument = false;
- private long lastModified = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -168,8 +158,8 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
layerView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
- if(keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK){
- isDocumentChanged=true;
+ if(!isReadOnlyMode() && keyEvent.getKeyCode() != KeyEvent.KEYCODE_BACK){
+ setDocumentChanged(true);
}
return false;
}
@@ -178,40 +168,54 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
// create TextCursorLayer
mDocumentOverlay = new DocumentOverlay(this, layerView);
- // New document type string is not null, meaning we want to open a new document
- if (getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY) != null) {
- String newDocumentType = getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY);
- String newFilePath = getIntent().getStringExtra(LibreOfficeUIActivity.NEW_FILE_PATH_KEY);
+ mbISReadOnlyMode = !isExperimentalMode();
- // Load the new document
- loadNewDocument(newFilePath, newDocumentType);
- }
+ final Uri docUri = getIntent().getData();
+ if (docUri != null) {
+ if (docUri.getScheme().equals(ContentResolver.SCHEME_CONTENT)
+ || docUri.getScheme().equals(ContentResolver.SCHEME_ANDROID_RESOURCE)) {
+ final boolean isReadOnlyDoc = (getIntent().getFlags() & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
+ mbISReadOnlyMode = !isExperimentalMode() || isReadOnlyDoc;
+ Log.d(LOGTAG, "SCHEME_CONTENT: getPath(): " + docUri.getPath());
- 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());
- 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());
- 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");
+ String displayName = FileUtilities.retrieveDisplayNameForDocumentUri(getContentResolver(), docUri);
+ toolbarTop.setTitle(displayName);
+
+ } else if (docUri.getScheme().equals(ContentResolver.SCHEME_FILE)) {
+ mbISReadOnlyMode = true;
+ Log.d(LOGTAG, "SCHEME_FILE: getPath(): " + docUri.getPath());
+ toolbarTop.setTitle(docUri.getLastPathSegment());
}
- } else {
- if (!isNewDocument) {
- mInputFile = new File(DEFAULT_DOC_PATH);
+ // create a temporary local copy to work with
+ boolean copyOK = copyFileToTemp(docUri) && mTempFile != null;
+ if (!copyOK) {
+ // TODO: can't open the file
+ Log.e(LOGTAG, "couldn't create temporary file from " + docUri);
+ return;
+ }
+
+ // if input doc is a template, a new doc is created and a proper URI to save to
+ // will only be available after a "Save As"
+ if (isTemplate(docUri)) {
+ toolbarTop.setTitle(R.string.default_document_name);
+ } else {
+ mDocumentUri = docUri;
}
+
+ LOKitShell.sendLoadEvent(mTempFile.getPath());
+ } else if (getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY) != null) {
+ // New document type string is not null, meaning we want to open a new document
+ String newDocumentType = getIntent().getStringExtra(LibreOfficeUIActivity.NEW_DOC_TYPE_KEY);
+ // create a temporary local file, will be copied to the actual URI when saving
+ loadNewDocument(newDocumentType);
+ toolbarTop.setTitle(getString(R.string.default_document_name));
+ } else {
+ Log.e(LOGTAG, "No document specified. This should never happen.");
+ return;
}
+ // the loadDocument/loadNewDocument event already triggers a refresh as well,
+ // so there's no need to do another refresh in 'onStart'
+ mbSkipNextRefresh = true;
mDrawerLayout = findViewById(R.id.drawer_layout);
@@ -223,8 +227,6 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
mDrawerList.setOnItemClickListener(new DocumentPartClickListener());
}
- lastModified = mInputFile.lastModified();
-
mToolbarController.setupToolbars();
TabHost host = findViewById(R.id.toolbarTabHost);
@@ -274,22 +276,18 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
}
}
- // Loads a new Document
- private void loadNewDocument(String newFilePath, String newDocumentType) {
- mInputFile = new File(newFilePath);
- LOKitShell.sendNewDocumentLoadEvent(newFilePath, newDocumentType);
- isNewDocument = true;
- toolbarTop.setTitle(mInputFile.getName());
+ // Loads a new Document and saves it to a temporary file
+ private void loadNewDocument(String newDocumentType) {
+ String tempFileName = "LibreOffice_" + UUID.randomUUID().toString();
+ mTempFile = new File(this.getCacheDir(), tempFileName);
+ LOKitShell.sendNewDocumentLoadEvent(mTempFile.getPath(), newDocumentType);
}
public RectF getCurrentCursorPosition() {
return mDocumentOverlay.getCurrentCursorPosition();
}
- private boolean copyFileToTemp() {
- ContentResolver contentResolver = getContentResolver();
- FileChannel inputChannel = null;
- FileChannel outputChannel = null;
+ private boolean copyFileToTemp(Uri documentUri) {
// CSV files need a .csv suffix to be opened in Calc.
String suffix = null;
String intentType = getIntent().getType();
@@ -298,27 +296,9 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
suffix = ".csv";
try {
- try {
- AssetFileDescriptor assetFD = contentResolver.openAssetFileDescriptor(getIntent().getData(), "r");
- if (assetFD == null) {
- Log.e(LOGTAG, "couldn't create assetfiledescriptor from " + getIntent().getDataString());
- return false;
- }
- inputChannel = assetFD.createInputStream().getChannel();
- mTempFile = File.createTempFile("LibreOffice", suffix, this.getCacheDir());
-
- outputChannel = new FileOutputStream(mTempFile).getChannel();
- long bytesTransferred = 0;
- // might not copy all at once, so make sure everything gets copied...
- while (bytesTransferred < inputChannel.size()) {
- bytesTransferred += outputChannel.transferFrom(inputChannel, bytesTransferred, inputChannel.size());
- }
- Log.e(LOGTAG, "Success copying " + bytesTransferred + " bytes");
- return true;
- } finally {
- if (inputChannel != null) inputChannel.close();
- if (outputChannel != null) outputChannel.close();
- }
+ mTempFile = File.createTempFile("LibreOffice", suffix, this.getCacheDir());
+ final FileOutputStream outputStream = new FileOutputStream(mTempFile);
+ return copyUriToStream(documentUri, outputStream);
} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {
@@ -327,65 +307,150 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
}
/**
- * 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.
+ * 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
LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND_NOTIFY, ".uno:Save", true));
}
- 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;
+ /**
+ * Open file chooser and save the document to the URI
+ * selected there.
+ */
+ public void saveDocumentAs() {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ String mimeType = getODFMimeTypeForDocument();
+ intent.setType(mimeType);
+ if (Build.VERSION.SDK_INT >= 26) {
+ intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, mDocumentUri);
+ }
+
+ startActivityForResult(intent, REQUEST_CODE_SAVEAS);
+ }
+
+ /**
+ * Saves the document under the given URI using ODF format
+ * and uses that URI from now on for all operations.
+ * @param newUri URI to save the document and use from now on.
+ */
+ private void saveDocumentAs(Uri newUri) {
+ mDocumentUri = newUri;
+ // save in ODF format
+ mTileProvider.saveDocumentAs(mTempFile.getPath(), true);
+ saveFileToOriginalSource();
+
+ String displayName = FileUtilities.retrieveDisplayNameForDocumentUri(getContentResolver(), mDocumentUri);
+ toolbarTop.setTitle(displayName);
+ mbISReadOnlyMode = !isExperimentalMode();
+ getToolbarController().setupToolbars();
+ }
+
+ public void exportToPDF() {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType(FileUtilities.MIMETYPE_PDF);
+ // suggest directory and file name based on the doc
+ if (Build.VERSION.SDK_INT >= 26) {
+ intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, mDocumentUri);
+ }
+ final String displayName = toolbarTop.getTitle().toString();
+ final String suggestedFileName = FileUtilities.stripExtensionFromFileName(displayName) + ".pdf";
+ intent.putExtra(Intent.EXTRA_TITLE, suggestedFileName);
+
+ startActivityForResult(intent, REQUEST_CODE_EXPORT_TO_PDF);
+ }
+
+ private void exportToPDF(final Uri uri) {
+ boolean exportOK = false;
+ File tempFile = null;
+ try {
+ tempFile = File.createTempFile("LibreOffice_", ".pdf");
+ mTileProvider.saveDocumentAs(tempFile.getAbsolutePath(),"pdf", false);
+
+ try {
+ FileInputStream inputStream = new FileInputStream(tempFile);
+ exportOK = copyStreamToUri(inputStream, uri);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
}
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (tempFile != null && tempFile.exists()) {
+ tempFile.delete();
+ }
+ }
+
+ final int msgId = exportOK ? R.string.pdf_export_finished : R.string.unable_to_export_pdf;
+ LOKitShell.getMainHandler().post(new Runnable() {
@Override
- protected void onPostExecute(Void param) {
- Toast.makeText(activity, R.string.message_saved,
- Toast.LENGTH_SHORT).show();
- setDocumentChanged(false);
+ public void run() {
+ showCustomStatusMessage(getString(msgId));
}
- };
+ });
+ }
+
+ /**
+ * Returns the ODF MIME type that can be used for the current document,
+ * regardless of whether the document is an ODF Document or not
+ * (e.g. returns FileUtilities.MIMETYPE_OPENDOCUMENT_TEXT for a DOCX file).
+ * @return MIME type, or empty string, if no appropriate MIME type could be found.
+ */
+ private String getODFMimeTypeForDocument() {
+ if (mTileProvider.isTextDocument())
+ return FileUtilities.MIMETYPE_OPENDOCUMENT_TEXT;
+ else if (mTileProvider.isSpreadsheet())
+ return FileUtilities.MIMETYPE_OPENDOCUMENT_SPREADSHEET;
+ else if (mTileProvider.isPresentation())
+ return FileUtilities.MIMETYPE_OPENDOCUMENT_PRESENTATION;
+ else if (mTileProvider.isDrawing())
+ return FileUtilities.MIMETYPE_OPENDOCUMENT_GRAPHICS;
+ else {
+ Log.w(LOGTAG, "Cannot determine MIME type to use.");
+ return "";
+ }
+ }
- if (lastModified < mInputFile.lastModified()) {
- task.execute();
- lastModified = mInputFile.lastModified();
+ /**
+ * Returns whether the MIME type for the URI is considered one for a document template.
+ */
+ private boolean isTemplate(final Uri documentUri) {
+ final String mimeType = getContentResolver().getType(documentUri);
+ return FileUtilities.isTemplateMimeType(mimeType);
+ }
+
+ public void saveFileToOriginalSource() {
+ if (mTempFile == null || mDocumentUri == null || !mDocumentUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))
+ return;
+
+ boolean copyOK = false;
+ try {
+ final FileInputStream inputStream = new FileInputStream(mTempFile);
+ copyOK = copyStreamToUri(inputStream, mDocumentUri);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ if (copyOK) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(LibreOfficeMainActivity.this, R.string.message_saved,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
+ setDocumentChanged(false);
} else {
- Toast.makeText(activity, R.string.message_save_incomplete, Toast.LENGTH_LONG).show();
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(LibreOfficeMainActivity.this, R.string.message_saving_failed,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
}
}
@@ -412,28 +477,23 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
protected void onStart() {
Log.i(LOGTAG, "onStart..");
super.onStart();
- if (!isNewDocument){
- if (partIndex == -1)
- LOKitShell.sendLoadEvent(mInputFile.getPath());
- else
- LOKitShell.sendResumeEvent(mInputFile.getPath(), partIndex);
+ if (!mbSkipNextRefresh) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.REFRESH));
}
+ mbSkipNextRefresh = false;
}
@Override
protected void onStop() {
Log.i(LOGTAG, "onStop..");
- //save document to cache
- if (mTileProvider != null)
- mTileProvider.cacheDocument();
hideSoftKeyboardDirect();
- LOKitShell.sendCloseEvent();
super.onStop();
}
@Override
protected void onDestroy() {
Log.i(LOGTAG, "onDestroy..");
+ LOKitShell.sendCloseEvent();
mLayerClient.destroy();
super.onDestroy();
@@ -461,11 +521,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
public void onClick(DialogInterface dialog, int which) {
switch (which){
case DialogInterface.BUTTON_POSITIVE:
- if (isNewDocument) {
- saveAs();
- } else {
- mTileProvider.saveDocument();
- }
+ mTileProvider.saveDocument();
isDocumentChanged=false;
onBackPressed();
break;
@@ -802,7 +858,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
// this function can only be called in InvalidationHandler.java
public void setPassword() {
- mTileProvider.setDocumentPassword("file://"+mInputFile.getPath(), mPassword);
+ mTileProvider.setDocumentPassword("file://" + mTempFile.getPath(), mPassword);
}
// setTileProvider is meant to let main activity have a handle of LOKit when dealing with password
@@ -844,12 +900,12 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
});
}
- public void setIsSpreadsheet(boolean b) {
- mIsSpreadsheet = b;
+ public static boolean isReadOnlyMode() {
+ return mbISReadOnlyMode;
}
- public boolean isSpreadsheet() {
- return mIsSpreadsheet;
+ public boolean hasLocationForSave() {
+ return mDocumentUri != null;
}
public static void setDocumentChanged (boolean changed) {
@@ -860,7 +916,6 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
DocumentPartView partView = mDocumentPartViewListAdapter.getItem(position);
- partIndex = partView.partIndex;
LOKitShell.sendChangePartEvent(partView.partIndex);
mDrawerLayout.closeDrawer(mDrawerList);
}
@@ -923,12 +978,103 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
}
}
- // This method is used in LOKitTileProvider.java to show status of new file creation.
- public void showSaveStatusMessage(boolean error) {
- if (!error)
- 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(); }
+ /**
+ * Copies everything from the given input stream to the given output stream
+ * and closes both streams in the end.
+ * @return Whether copy operation was successful.
+ */
+ private boolean copyStream(InputStream inputStream, OutputStream outputStream) {
+ try {
+ byte[] buffer = new byte[4096];
+ int readBytes = inputStream.read(buffer);
+ while (readBytes != -1) {
+ outputStream.write(buffer, 0, readBytes);
+ readBytes = inputStream.read(buffer);
+ }
+ return true;
+ } catch (IOException e) {
+ e.printStackTrace();
+ return false;
+ } finally {
+ try {
+ inputStream.close();
+ outputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * Copies everything from the given Uri to the given OutputStream
+ * and closes the OutputStream in the end.
+ * The copy operation runs in a separate thread, but the method only returns
+ * after the thread has finished its execution.
+ * This can be used to copy in a blocking way when network access is involved,
+ * which is not allowed from the main thread, but that may happen when an underlying
+ * DocumentsProvider (like the NextCloud one) does network access.
+ */
+ private boolean copyUriToStream(final Uri inputUri, final OutputStream outputStream) {
+ class CopyThread extends Thread {
+ /** Whether copy operation was successful. */
+ private boolean result = false;
+
+ @Override
+ public void run() {
+ final ContentResolver contentResolver = getContentResolver();
+ try {
+ InputStream inputStream = contentResolver.openInputStream(inputUri);
+ result = copyStream(inputStream, outputStream);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ CopyThread copyThread = new CopyThread();
+ copyThread.start();
+ try {
+ // wait for copy operation to finish
+ // NOTE: might be useful to add some indicator in UI for long copy operations involving network...
+ copyThread.join();
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ return copyThread.result;
+ }
+
+ /**
+ * Copies everything from the given InputStream to the given URI and closes the
+ * InputStream in the end.
+ * @see LibreOfficeMainActivity#copyUriToStream(Uri, OutputStream)
+ * which does the same thing the other way around.
+ */
+ private boolean copyStreamToUri(final InputStream inputStream, final Uri outputUri) {
+ class CopyThread extends Thread {
+ /** Whether copy operation was successful. */
+ private boolean result = false;
+
+ @Override
+ public void run() {
+ final ContentResolver contentResolver = getContentResolver();
+ try {
+ OutputStream outputStream = contentResolver.openOutputStream(outputUri);
+ result = copyStream(inputStream, outputStream);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ CopyThread copyThread = new CopyThread();
+ copyThread.start();
+ try {
+ // wait for copy operation to finish
+ // NOTE: might be useful to add some indicator in UI for long copy operations involving network...
+ copyThread.join();
+ } catch(InterruptedException e) {
+ e.printStackTrace();
+ }
+ return copyThread.result;
+ }
public void showCustomStatusMessage(String message){
Snackbar.make(mDrawerLayout, message, Snackbar.LENGTH_LONG).show();
@@ -936,45 +1082,36 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
public void preparePresentation() {
if (getExternalCacheDir() != null) {
- String tempPath = getExternalCacheDir().getPath() + "/" + mInputFile.getName() + ".svg";
+ String tempPath = getExternalCacheDir().getPath() + "/" + mTempFile.getName() + ".svg";
mTempSlideShowFile = new File(tempPath);
if (mTempSlideShowFile.exists() && !isDocumentChanged) {
startPresentation("file://" + tempPath);
} else {
- LOKitShell.sendSaveAsEvent(tempPath, "svg");
+ LOKitShell.sendSaveCopyAsEvent(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();
- }
+ Intent intent = new Intent(this, PresentationActivity.class);
+ intent.setData(Uri.parse(tempPath));
+ startActivity(intent);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- mFormattingController.handleActivityResult(requestCode, resultCode, data);
- hideBottomToolbar();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- super.onSaveInstanceState(outState);
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_SAVEAS && resultCode == RESULT_OK) {
+ final Uri fileUri = data.getData();
+ saveDocumentAs(fileUri);
+ } else if (requestCode == REQUEST_CODE_EXPORT_TO_PDF && resultCode == RESULT_OK) {
+ final Uri fileUri = data.getData();
+ exportToPDF(fileUri);
+ } else {
+ mFormattingController.handleActivityResult(requestCode, resultCode, data);
+ hideBottomToolbar();
+ }
}
-
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/android/source/src/java/org/libreoffice/LocaleHelper.java b/android/source/src/java/org/libreoffice/LocaleHelper.java
index 8c0e9b3fbbed..a87c63f09990 100644
--- a/android/source/src/java/org/libreoffice/LocaleHelper.java
+++ b/android/source/src/java/org/libreoffice/LocaleHelper.java
@@ -38,8 +38,7 @@ public class LocaleHelper {
Resources res = context.getResources();
Configuration cfg = res.getConfiguration();
cfg.locale = locale;
- if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
- cfg.setLayoutDirection(locale);
+ cfg.setLayoutDirection(locale);
res.updateConfiguration(cfg, res.getDisplayMetrics());
return context;
diff --git a/android/source/src/java/org/libreoffice/PasswordDialogFragment.java b/android/source/src/java/org/libreoffice/PasswordDialogFragment.java
index 112e35c4b7ed..08bc7f596894 100644
--- a/android/source/src/java/org/libreoffice/PasswordDialogFragment.java
+++ b/android/source/src/java/org/libreoffice/PasswordDialogFragment.java
@@ -4,9 +4,9 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.v4.app.DialogFragment;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/android/source/src/java/org/libreoffice/PresentationActivity.java b/android/source/src/java/org/libreoffice/PresentationActivity.java
index 3308e6884fe9..ede7c0c40101 100644
--- a/android/source/src/java/org/libreoffice/PresentationActivity.java
+++ b/android/source/src/java/org/libreoffice/PresentationActivity.java
@@ -1,16 +1,14 @@
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 androidx.annotation.Nullable;
+import androidx.core.view.GestureDetectorCompat;
+import androidx.appcompat.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;
@@ -25,19 +23,10 @@ public class PresentationActivity extends AppCompatActivity {
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);
- }
+ View decorView = getWindow().getDecorView();
+ // Hide the status bar.
+ int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
+ decorView.setSystemUiVisibility(uiOptions);
setContentView(R.layout.presentation_mode);
@@ -185,4 +174,4 @@ public class PresentationActivity extends AppCompatActivity {
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/SearchController.java b/android/source/src/java/org/libreoffice/SearchController.java
index a9414f7f7a71..6095e1fd2afe 100644
--- a/android/source/src/java/org/libreoffice/SearchController.java
+++ b/android/source/src/java/org/libreoffice/SearchController.java
@@ -11,7 +11,7 @@ import org.json.JSONException;
import org.json.JSONObject;
public class SearchController implements View.OnClickListener {
- private LibreOfficeMainActivity mActivity;
+ private final LibreOfficeMainActivity mActivity;
private enum SearchDirection {
UP, DOWN
@@ -69,15 +69,8 @@ public class SearchController implements View.OnClickListener {
ImageButton button = (ImageButton) view;
SearchDirection direction = SearchDirection.DOWN;
- switch(button.getId()) {
- case R.id.button_search_down:
- direction = SearchDirection.DOWN;
- break;
- case R.id.button_search_up:
- direction = SearchDirection.UP;
- break;
- default:
- break;
+ if (button.getId() == R.id.button_search_up) {
+ direction = SearchDirection.UP;
}
String searchText = ((EditText) mActivity.findViewById(R.id.search_string)).getText().toString();
diff --git a/android/source/src/java/org/libreoffice/ThumbnailCreator.java b/android/source/src/java/org/libreoffice/ThumbnailCreator.java
index 52870b67a5b9..c0c097747c69 100644
--- a/android/source/src/java/org/libreoffice/ThumbnailCreator.java
+++ b/android/source/src/java/org/libreoffice/ThumbnailCreator.java
@@ -71,7 +71,7 @@ public class ThumbnailCreator {
class ThumbnailCreationTask{
private final WeakReference<ImageView> imageViewReference;
- private int partNumber;
+ private final int partNumber;
private boolean cancelled = false;
public ThumbnailCreationTask(ImageView imageView, int partNumber) {
diff --git a/android/source/src/java/org/libreoffice/TileProvider.java b/android/source/src/java/org/libreoffice/TileProvider.java
index dabf30b834f7..c979a9883c13 100644
--- a/android/source/src/java/org/libreoffice/TileProvider.java
+++ b/android/source/src/java/org/libreoffice/TileProvider.java
@@ -22,9 +22,24 @@ import org.mozilla.gecko.gfx.IntSize;
public interface TileProvider {
/**
- * Save the current document.
+ * Save the current document under the given path.
+ * @param takeOwnership Whether to take ownership of the new file,
+ * i.e. whether the current document is changed to the
+ * newly saved document (takeOwnership = true),
+ * as compared to just saving a copy of the current document
+ * or exporting to a different file format.
+ * Must be 'false' when using this method for export to e.g. PNG or PDF.
+ * @return Whether saving was successful.
*/
- void saveDocumentAs(String filePath, String format);
+ boolean saveDocumentAs(String filePath, String format, boolean takeOwnership);
+
+ /**
+ * Saves the current document under the given path,
+ * using the default file format.
+ * @param takeOwnership (s. documentation for
+ * 'saveDocumentAs(String filePath, String format, boolean takeOwnership)')
+ */
+ boolean saveDocumentAs(String filePath, boolean takeOwnership);
/**
* Returns the page width in pixels.
@@ -72,6 +87,11 @@ public interface TileProvider {
void close();
/**
+ * Returns true if the current open document is a drawing.
+ */
+ boolean isDrawing();
+
+ /**
* Returns true if the current open document is a text document.
*/
boolean isTextDocument();
diff --git a/android/source/src/java/org/libreoffice/ToolbarController.java b/android/source/src/java/org/libreoffice/ToolbarController.java
index d21396cf4615..603d2258167e 100644
--- a/android/source/src/java/org/libreoffice/ToolbarController.java
+++ b/android/source/src/java/org/libreoffice/ToolbarController.java
@@ -11,7 +11,7 @@ package org.libreoffice;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
-import android.support.v7.widget.Toolbar;
+import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
@@ -45,12 +45,12 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener {
clipboardManager = (ClipboardManager)mContext.getSystemService(Context.CLIPBOARD_SERVICE);
}
- public void disableMenuItem(final int menuItemId, final boolean disabled) {
+ private void enableMenuItem(final int menuItemId, final boolean enabled) {
LOKitShell.getMainHandler().post(new Runnable() {
public void run() {
MenuItem menuItem = mMainMenu.findItem(menuItemId);
if (menuItem != null) {
- menuItem.setEnabled(!disabled);
+ menuItem.setEnabled(enabled);
} else {
Log.e(LOGTAG, "MenuItem not found.");
}
@@ -72,6 +72,8 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener {
void switchToEditMode() {
if (!LOKitShell.isEditingEnabled())
return;
+
+ setEditModeOn(true);
// Ensure the change is done on UI thread
LOKitShell.getMainHandler().post(new Runnable() {
@Override
@@ -89,7 +91,6 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener {
}
mToolbarTop.setNavigationIcon(R.drawable.ic_check);
mToolbarTop.setLogo(null);
- setEditModeOn(true);
}
});
}
@@ -145,7 +146,7 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener {
@Override
public void run() {
mMainMenu.setGroupVisible(R.id.group_edit_actions, false);
- mToolbarTop.setNavigationIcon(R.drawable.lo_icon);
+ mToolbarTop.setNavigationIcon(R.mipmap.ic_launcher);
mToolbarTop.setLogo(null);
setEditModeOn(false);
mContext.hideBottomToolbar();
@@ -161,94 +162,91 @@ public class ToolbarController implements Toolbar.OnMenuItemClickListener {
@Override
public boolean onMenuItemClick(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.action_keyboard:
- mContext.showSoftKeyboard();
- break;
- case R.id.action_format:
- mContext.showFormattingToolbar();
- break;
- case R.id.action_about:
- mContext.showAbout();
- return true;
- case R.id.action_save:
- if (mContext.isNewDocument) {
- mContext.saveAs();
- } else {
- mContext.getTileProvider().saveDocument();
- }
- return true;
- case R.id.action_parts:
- mContext.openDrawer();
- return true;
- case R.id.action_exportToPDF:
- mContext.getTileProvider().exportToPDF(false);
- return true;
- case R.id.action_print:
- mContext.getTileProvider().exportToPDF(true);
- return true;
- case R.id.action_settings:
- mContext.showSettings();
- return true;
- case R.id.action_search:
- mContext.showSearchToolbar();
- return true;
- case R.id.action_undo:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Undo"));
- return true;
- 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;
- case R.id.action_add_slide:
- mContext.addPart();
- return true;
- case R.id.action_add_worksheet:
- mContext.addPart();
- return true;
- case R.id.action_rename_worksheet:
- case R.id.action_rename_slide:
- mContext.renamePart();
- return true;
- case R.id.action_delete_worksheet:
- mContext.deletePart();
- return true;
- case R.id.action_delete_slide:
- mContext.deletePart();
- return true;
- case R.id.action_back:
- hideClipboardActions();
- return true;
- case R.id.action_copy:
- LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Copy"));
- clipData = ClipData.newPlainText("clipboard data", clipboardText);
- clipboardManager.setPrimaryClip(clipData);
- Toast.makeText(mContext, mContext.getResources().getString(R.string.action_text_copied), Toast.LENGTH_SHORT).show();
- return true;
- case R.id.action_paste:
- clipData = clipboardManager.getPrimaryClip();
- ClipData.Item clipItem = clipData.getItemAt(0);
- mContext.setDocumentChanged(true);
- return mContext.getTileProvider().paste("text/plain;charset=utf-16", clipItem.getText().toString());
- case R.id.action_cut:
- clipData = ClipData.newPlainText("clipboard data", clipboardText);
- clipboardManager.setPrimaryClip(clipData);
- LOKitShell.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
- mContext.setDocumentChanged(true);
- return true;
- case R.id.action_UNO_commands:
- mContext.showUNOCommandsToolbar();
- return true;
+ final int itemId = item.getItemId();
+ if (itemId == R.id.action_keyboard) {
+ mContext.showSoftKeyboard();
+ } else if (itemId == R.id.action_format) {
+ mContext.showFormattingToolbar();
+ } else if (itemId == R.id.action_about) {
+ mContext.showAbout();
+ return true;
+ } else if (itemId == R.id.action_save) {
+ mContext.getTileProvider().saveDocument();
+ return true;
+ } else if (itemId == R.id.action_save_as) {
+ mContext.saveDocumentAs();
+ return true;
+ } else if (itemId == R.id.action_parts) {
+ mContext.openDrawer();
+ return true;
+ } else if (itemId == R.id.action_exportToPDF) {
+ mContext.exportToPDF();
+ return true;
+ } else if (itemId == R.id.action_print) {
+ mContext.getTileProvider().printDocument();
+ return true;
+ } else if (itemId == R.id.action_settings) {
+ mContext.showSettings();
+ return true;
+ } else if (itemId == R.id.action_search) {
+ mContext.showSearchToolbar();
+ return true;
+ } else if (itemId == R.id.action_undo) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Undo"));
+ return true;
+ } else if (itemId == R.id.action_redo) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Redo"));
+ return true;
+ } else if (itemId == R.id.action_presentation) {
+ mContext.preparePresentation();
+ return true;
+ } else if (itemId == R.id.action_add_slide || itemId == R.id.action_add_worksheet) {
+ mContext.addPart();
+ return true;
+ } else if (itemId == R.id.action_rename_worksheet || itemId == R.id.action_rename_slide) {
+ mContext.renamePart();
+ return true;
+ } else if (itemId == R.id.action_delete_worksheet || itemId == R.id.action_delete_slide) {
+ mContext.deletePart();
+ return true;
+ } else if (itemId == R.id.action_back) {
+ hideClipboardActions();
+ return true;
+ } else if (itemId == R.id.action_copy) {
+ LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:Copy"));
+ clipData = ClipData.newPlainText("clipboard data", clipboardText);
+ clipboardManager.setPrimaryClip(clipData);
+ Toast.makeText(mContext, mContext.getResources().getString(R.string.action_text_copied), Toast.LENGTH_SHORT).show();
+ return true;
+ } else if (itemId == R.id.action_paste) {
+ clipData = clipboardManager.getPrimaryClip();
+ ClipData.Item clipItem = clipData.getItemAt(0);
+ mContext.setDocumentChanged(true);
+ return mContext.getTileProvider().paste("text/plain;charset=utf-16", clipItem.getText().toString());
+ } else if (itemId == R.id.action_cut) {
+ clipData = ClipData.newPlainText("clipboard data", clipboardText);
+ clipboardManager.setPrimaryClip(clipData);
+ LOKitShell.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
+ mContext.setDocumentChanged(true);
+ return true;
+ } else if (itemId == R.id.action_UNO_commands) {
+ mContext.showUNOCommandsToolbar();
+ return true;
}
return false;
}
void setupToolbars() {
- if (mContext.usesTemporaryFile()) {
- disableMenuItem(R.id.action_save, true);
- Toast.makeText(mContext, mContext.getString(R.string.temp_file_saving_disabled), Toast.LENGTH_LONG).show();
+ if (LibreOfficeMainActivity.isExperimentalMode()) {
+ boolean enableSaveEntry = !LibreOfficeMainActivity.isReadOnlyMode() && mContext.hasLocationForSave();
+ enableMenuItem(R.id.action_save, enableSaveEntry);
+ if (LibreOfficeMainActivity.isReadOnlyMode()) {
+ // show message in case experimental mode is enabled (i.e. editing is supported in general),
+ // but current document is readonly
+ Toast.makeText(mContext, mContext.getString(R.string.readonly_file), Toast.LENGTH_LONG).show();
+ }
+ } else {
+ hideItem(R.id.action_save);
}
mMainMenu.findItem(R.id.action_parts).setVisible(mContext.isDrawerEnabled());
}
diff --git a/android/source/src/java/org/libreoffice/UNOCommandsController.java b/android/source/src/java/org/libreoffice/UNOCommandsController.java
index 9453b3bd07b7..cba67732cce1 100644
--- a/android/source/src/java/org/libreoffice/UNOCommandsController.java
+++ b/android/source/src/java/org/libreoffice/UNOCommandsController.java
@@ -8,7 +8,7 @@
package org.libreoffice;
import android.content.DialogInterface;
-import android.support.v7.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.EditText;
@@ -21,7 +21,7 @@ import org.json.JSONObject;
import static org.libreoffice.SearchController.addProperty;
class UNOCommandsController implements View.OnClickListener {
- private LibreOfficeMainActivity mActivity;
+ private final LibreOfficeMainActivity mActivity;
private JSONObject mRootJSON = new JSONObject();
@@ -72,7 +72,7 @@ class UNOCommandsController implements View.OnClickListener {
})
.setIcon(android.R.drawable.ic_dialog_info)
.show();
- TextView textView = (TextView) dialog.findViewById(android.R.id.message);
+ TextView textView = dialog.findViewById(android.R.id.message);
if (textView != null) {
textView.setScroller(new Scroller(mActivity));
textView.setVerticalScrollBarEnabled(true);
@@ -82,4 +82,4 @@ class UNOCommandsController implements View.OnClickListener {
e.printStackTrace();
}
}
-} \ No newline at end of file
+}
diff --git a/android/source/src/java/org/libreoffice/canvas/BitmapHandle.java b/android/source/src/java/org/libreoffice/canvas/BitmapHandle.java
index e46173db518f..51f6f7cf8611 100644
--- a/android/source/src/java/org/libreoffice/canvas/BitmapHandle.java
+++ b/android/source/src/java/org/libreoffice/canvas/BitmapHandle.java
@@ -5,7 +5,7 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
-import android.support.v4.content.ContextCompat;
+import androidx.core.content.ContextCompat;
/**
* Bitmap handle canvas element is used to show a handle on the screen.
diff --git a/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java b/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java
index c1f8e74e7ba2..a285234bc8b0 100644
--- a/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java
+++ b/android/source/src/java/org/libreoffice/canvas/CalcHeaderCell.java
@@ -4,29 +4,40 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.text.TextPaint;
public class CalcHeaderCell extends CommonCanvasElement {
- private TextPaint mTextPaint = new TextPaint();
- private Paint mBgPaint = new Paint();
- private RectF mBounds;
- private String mText;
+ private final TextPaint mTextPaint = new TextPaint();
+
+ private final Paint mFramePaint = new Paint();
+ private final Paint mBgPaint = new Paint();
+ private final RectF mBounds;
+ private final Rect mTextBounds = new Rect();
+ private final String mText;
public CalcHeaderCell(float left, float top, float width, float height, String text, boolean selected) {
mBounds = new RectF(left, top, left + width, top + height);
+
+ mFramePaint.setStyle(Style.STROKE);
+ mFramePaint.setColor(Color.BLACK);
+
+ mBgPaint.setStyle(Style.FILL);
+ mBgPaint.setColor(Color.GRAY);
+ // draw background more intensely when cell is selected
if (selected) {
- // if the cell is selected, display filled
- mBgPaint.setStyle(Style.FILL_AND_STROKE);
+ mBgPaint.setAlpha(100);
} else {
- // if not, display only the frame
- mBgPaint.setStyle(Style.STROKE);
+ mBgPaint.setAlpha(25);
}
- mBgPaint.setColor(Color.GRAY);
- mBgPaint.setAlpha(100); // hard coded for now
- mTextPaint.setColor(Color.GRAY);
+
+ mTextPaint.setColor(Color.BLACK);
mTextPaint.setTextSize(24f); // hard coded for now
+ mTextPaint.setTextAlign(Paint.Align.CENTER);
mText = text;
+
+ mTextPaint.getTextBounds(mText, 0, mText.length(), mTextBounds);
}
/**
@@ -49,6 +60,7 @@ public class CalcHeaderCell extends CommonCanvasElement {
@Override
public void onDraw(Canvas canvas) {
canvas.drawRect(mBounds, mBgPaint);
- canvas.drawText(mText, mBounds.left, mBounds.bottom, mTextPaint);
+ canvas.drawRect(mBounds, mFramePaint);
+ canvas.drawText(mText, mBounds.centerX(), mBounds.centerY() - mTextBounds.centerY(), mTextPaint);
}
-} \ No newline at end of file
+}
diff --git a/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java b/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java
index 40c9ddcd8cea..8b99c292cbc5 100644
--- a/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java
+++ b/android/source/src/java/org/libreoffice/overlay/CalcHeadersController.java
@@ -4,7 +4,7 @@ import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable;
-import android.support.design.widget.Snackbar;
+import com.google.android.material.snackbar.Snackbar;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Gravity;
diff --git a/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java b/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java
index a8b2d2048409..98af7a9554e7 100644
--- a/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java
+++ b/android/source/src/java/org/libreoffice/overlay/CalcHeadersView.java
@@ -4,7 +4,7 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.graphics.RectF;
-import android.support.v4.view.GestureDetectorCompat;
+import androidx.core.view.GestureDetectorCompat;
import android.util.AttributeSet;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
@@ -101,12 +101,8 @@ public class CalcHeadersView extends View {
bottom = -origin.y + zoom*mDimens.get(i);
if (top <= getHeight() && bottom >= 0) {
inRangeOfVisibleHeaders = true;
- if (mCellCursorRect != null && bottom > mCellCursorRect.top - origin.y && top < mCellCursorRect.bottom - origin.y) {
- // if cell is within current selected portion
- new CalcHeaderCell(0f, top, getWidth(), bottom - top, mLabels.get(i), true).onDraw(canvas);
- } else {
- new CalcHeaderCell(0f, top, getWidth(), bottom - top, mLabels.get(i), false).onDraw(canvas);
- }
+ boolean isSelected = mCellCursorRect != null && bottom > mCellCursorRect.top - origin.y && top < mCellCursorRect.bottom - origin.y;
+ new CalcHeaderCell(0f, top, getWidth(), bottom - top, mLabels.get(i), isSelected).onDraw(canvas);
} else {
if (inRangeOfVisibleHeaders) {
break;
@@ -116,12 +112,8 @@ public class CalcHeadersView extends View {
left = -origin.x + zoom*mDimens.get(i-1);
right = -origin.x + zoom*mDimens.get(i);
if (left <= getWidth() && right >= 0) {
- if (mCellCursorRect != null && right > mCellCursorRect.left - origin.x && left < mCellCursorRect.right - origin.x) {
- // if cell is within current selected portion
- new CalcHeaderCell(left, 0f, right - left, getHeight(), mLabels.get(i), true).onDraw(canvas);
- } else {
- new CalcHeaderCell(left, 0f, right - left, getHeight(), mLabels.get(i), false).onDraw(canvas);
- }
+ boolean isSelected = mCellCursorRect != null && right > mCellCursorRect.left - origin.x && left < mCellCursorRect.right - origin.x;
+ new CalcHeaderCell(left, 0f, right - left, getHeight(), mLabels.get(i), isSelected).onDraw(canvas);
} else {
if (inRangeOfVisibleHeaders) {
break;
diff --git a/android/source/src/java/org/libreoffice/storage/DocumentProviderFactory.java b/android/source/src/java/org/libreoffice/storage/DocumentProviderFactory.java
deleted file mode 100644
index acf5aebcd6c6..000000000000
--- a/android/source/src/java/org/libreoffice/storage/DocumentProviderFactory.java
+++ /dev/null
@@ -1,128 +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 org.libreoffice.storage.owncloud.OwnCloudProvider;
-
-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[5];
- 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[4] = new OwnCloudProvider(4, 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;
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/DocumentProviderSettingsActivity.java b/android/source/src/java/org/libreoffice/storage/DocumentProviderSettingsActivity.java
deleted file mode 100644
index b842e79fafd6..000000000000
--- a/android/source/src/java/org/libreoffice/storage/DocumentProviderSettingsActivity.java
+++ /dev/null
@@ -1,102 +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_OWNCLOUD_SERVER = "pref_server_url";
- public static final String KEY_PREF_OWNCLOUD_USER_NAME = "pref_user_name";
- public static final String KEY_PREF_OWNCLOUD_PASSWORD = "pref_password";
- 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);
- }
-
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/IDocumentProvider.java b/android/source/src/java/org/libreoffice/storage/IDocumentProvider.java
deleted file mode 100644
index 044d7ddb422b..000000000000
--- a/android/source/src/java/org/libreoffice/storage/IDocumentProvider.java
+++ /dev/null
@@ -1,70 +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, e. g. two
- * instances of OwnCloudProvider pointing to different servers.
- *
- * @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);
-}
diff --git a/android/source/src/java/org/libreoffice/storage/IFile.java b/android/source/src/java/org/libreoffice/storage/IFile.java
deleted file mode 100644
index c9cfa7f1198d..000000000000
--- a/android/source/src/java/org/libreoffice/storage/IFile.java
+++ /dev/null
@@ -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);
-}
diff --git a/android/source/src/java/org/libreoffice/storage/IOUtils.java b/android/source/src/java/org/libreoffice/storage/IOUtils.java
deleted file mode 100644
index f345f5cbed3b..000000000000
--- a/android/source/src/java/org/libreoffice/storage/IOUtils.java
+++ /dev/null
@@ -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;
- }
-
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/BrowserSelectorActivity.java b/android/source/src/java/org/libreoffice/storage/external/BrowserSelectorActivity.java
deleted file mode 100644
index 07b64623b701..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/BrowserSelectorActivity.java
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserActivity.java b/android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserActivity.java
deleted file mode 100644
index 1cf9f52fa7c0..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserActivity.java
+++ /dev/null
@@ -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();
- }
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserFragment.java b/android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserFragment.java
deleted file mode 100644
index 18165650a617..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/DirectoryBrowserFragment.java
+++ /dev/null
@@ -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);
- }
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/ExternalFile.java b/android/source/src/java/org/libreoffice/storage/external/ExternalFile.java
deleted file mode 100644
index aff33e4413ef..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/ExternalFile.java
+++ /dev/null
@@ -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());
- }
-
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/ExtsdDocumentsProvider.java b/android/source/src/java/org/libreoffice/storage/external/ExtsdDocumentsProvider.java
deleted file mode 100644
index e45929374bbd..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/ExtsdDocumentsProvider.java
+++ /dev/null
@@ -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, "");
- }
- }
-
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/IExternalDocumentProvider.java b/android/source/src/java/org/libreoffice/storage/external/IExternalDocumentProvider.java
deleted file mode 100644
index a439417b60cd..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/IExternalDocumentProvider.java
+++ /dev/null
@@ -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);
-
-}
diff --git a/android/source/src/java/org/libreoffice/storage/external/OTGDocumentsProvider.java b/android/source/src/java/org/libreoffice/storage/external/OTGDocumentsProvider.java
deleted file mode 100644
index 4341bc3541e6..000000000000
--- a/android/source/src/java/org/libreoffice/storage/external/OTGDocumentsProvider.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/local/LocalDocumentsDirectoryProvider.java b/android/source/src/java/org/libreoffice/storage/local/LocalDocumentsDirectoryProvider.java
deleted file mode 100644
index 15522e93a45e..000000000000
--- a/android/source/src/java/org/libreoffice/storage/local/LocalDocumentsDirectoryProvider.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/local/LocalDocumentsProvider.java b/android/source/src/java/org/libreoffice/storage/local/LocalDocumentsProvider.java
deleted file mode 100644
index 1a10fad424db..000000000000
--- a/android/source/src/java/org/libreoffice/storage/local/LocalDocumentsProvider.java
+++ /dev/null
@@ -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;
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/local/LocalFile.java b/android/source/src/java/org/libreoffice/storage/local/LocalFile.java
deleted file mode 100644
index 4ff5bbf119f4..000000000000
--- a/android/source/src/java/org/libreoffice/storage/local/LocalFile.java
+++ /dev/null
@@ -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
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudFile.java b/android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudFile.java
deleted file mode 100644
index fa74a54b08e2..000000000000
--- a/android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudFile.java
+++ /dev/null
@@ -1,178 +0,0 @@
-package org.libreoffice.storage.owncloud;
-
-import android.content.Context;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.UnsupportedEncodingException;
-import java.net.URI;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import org.libreoffice.storage.IFile;
-
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.resources.files.ChunkedUploadRemoteFileOperation;
-import com.owncloud.android.lib.resources.files.DownloadRemoteFileOperation;
-import com.owncloud.android.lib.resources.files.ReadRemoteFolderOperation;
-import com.owncloud.android.lib.resources.files.RemoteFile;
-import com.owncloud.android.lib.resources.files.UploadRemoteFileOperation;
-
-/**
- * Implementation of IFile for ownCloud servers.
- */
-public class OwnCloudFile implements IFile {
-
- private OwnCloudProvider provider;
- private RemoteFile file;
-
- private String name;
- private String parentPath;
-
- protected OwnCloudFile(OwnCloudProvider provider, RemoteFile file) {
- this.provider = provider;
- this.file = file;
-
- // get name and parent from path
- File localFile = new File(file.getRemotePath());
- this.name = localFile.getName();
- this.parentPath = localFile.getParent();
- }
-
- @Override
- public URI getUri(){
-
- try{
- return URI.create(URLEncoder.encode(file.getRemotePath(),"UTF-8"));
- }catch(UnsupportedEncodingException e){
- e.printStackTrace();
- }
-
- return null;
- }
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public boolean isDirectory() {
- return file.getMimeType().equals("DIR");
- }
-
- @Override
- public long getSize() {
- return file.getLength();
- }
-
- @Override
- public Date getLastModified() {
- return new Date(file.getModifiedTimestamp());
- }
-
- @Override
- public List<IFile> listFiles() {
- List<IFile> children = new ArrayList<IFile>();
- if (isDirectory()) {
- ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(
- file.getRemotePath());
- RemoteOperationResult result = refreshOperation.execute(provider
- .getClient());
- if (!result.isSuccess()) {
- throw provider.buildRuntimeExceptionForResultCode(result.getCode());
- }
- for (Object obj : result.getData()) {
- RemoteFile child = (RemoteFile) obj;
- if (!child.getRemotePath().equals(file.getRemotePath()))
- children.add(new OwnCloudFile(provider, child));
- }
- }
- return children;
- }
-
- @Override
- public List<IFile> listFiles(FileFilter filter) {
- List<IFile> children = new ArrayList<IFile>();
- if (isDirectory()) {
- ReadRemoteFolderOperation refreshOperation = new ReadRemoteFolderOperation(
- file.getRemotePath());
- RemoteOperationResult result = refreshOperation.execute(provider
- .getClient());
- if (!result.isSuccess()) {
- throw provider.buildRuntimeExceptionForResultCode(result.getCode());
- }
-
- for (Object obj : result.getData()) {
- RemoteFile child = (RemoteFile) obj;
- if (!child.getRemotePath().equals(file.getRemotePath())){
- OwnCloudFile ownCloudFile = new OwnCloudFile(provider, child);
- if(!ownCloudFile.isDirectory()){
- File f = new File(provider.getCacheDir().getAbsolutePath(),
- ownCloudFile.getName());
- if(filter.accept(f))
- children.add(ownCloudFile);
- f.delete();
- }else{
- children.add(ownCloudFile);
- }
- }
- }
- }
- return children;
- }
-
- @Override
- public IFile getParent(Context context) {
- if (parentPath == null)
- // this is the root node
- return null;
-
- return provider.createFromUri(context, URI.create(parentPath));
- }
-
- @Override
- public File getDocument() {
- if (isDirectory()) {
- return null;
- }
- File downFolder = provider.getCacheDir();
- DownloadRemoteFileOperation operation = new DownloadRemoteFileOperation(
- file.getRemotePath(), downFolder.getAbsolutePath());
- RemoteOperationResult result = operation.execute(provider.getClient());
- if (!result.isSuccess()) {
- throw provider.buildRuntimeExceptionForResultCode(result.getCode());
- }
- return new File(downFolder.getAbsolutePath() + file.getRemotePath());
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object)
- return true;
- if (!(object instanceof OwnCloudFile))
- return false;
- OwnCloudFile file = (OwnCloudFile) object;
- return file.getUri().equals(getUri());
- }
-
- @Override
- public void saveDocument(File newFile) {
- UploadRemoteFileOperation uploadOperation;
- if (newFile.length() > ChunkedUploadRemoteFileOperation.CHUNK_SIZE) {
- uploadOperation = new ChunkedUploadRemoteFileOperation(
- newFile.getPath(), file.getRemotePath(), file.getMimeType());
- } else {
- uploadOperation = new UploadRemoteFileOperation(newFile.getPath(),
- file.getRemotePath(), file.getMimeType());
- }
-
- RemoteOperationResult result = uploadOperation.execute(provider
- .getClient());
- if (!result.isSuccess()) {
- throw provider.buildRuntimeExceptionForResultCode(result.getCode());
- }
- }
-}
diff --git a/android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudProvider.java b/android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudProvider.java
deleted file mode 100644
index 0852ab617660..000000000000
--- a/android/source/src/java/org/libreoffice/storage/owncloud/OwnCloudProvider.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package org.libreoffice.storage.owncloud;
-
-import java.io.File;
-import java.net.URI;
-
-import org.libreoffice.R;
-import org.libreoffice.storage.DocumentProviderSettingsActivity;
-import org.libreoffice.storage.IDocumentProvider;
-import org.libreoffice.storage.IFile;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.net.Uri;
-import android.preference.PreferenceManager;
-
-import com.owncloud.android.lib.common.OwnCloudClient;
-import com.owncloud.android.lib.common.OwnCloudClientFactory;
-import com.owncloud.android.lib.common.OwnCloudCredentialsFactory;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult;
-import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
-import com.owncloud.android.lib.resources.files.FileUtils;
-import com.owncloud.android.lib.resources.files.ReadRemoteFileOperation;
-import com.owncloud.android.lib.resources.files.RemoteFile;
-
-
-/**
- * Implementation of IDocumentProvider for ownCloud servers.
- */
-public class OwnCloudProvider implements IDocumentProvider,
- OnSharedPreferenceChangeListener {
-
- private int id;
-
- private Context context;
- private OwnCloudClient client;
- private File cacheDir;
-
- private String serverUrl;
- private String userName;
- private String password;
- private RemoteOperationResult result;
-
- public OwnCloudProvider(int id, Context context) {
- this.id = id;
- this.context = context;
-
- // read preferences
- SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
- serverUrl = preferences.getString(
- DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_SERVER, "");
- userName = preferences.getString(
- DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_USER_NAME, "");
- password = preferences.getString(
- DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_PASSWORD, "");
-
- setupClient();
-
- // make sure cache directory exists, and clear it
- // TODO: probably we should do smarter cache management
- cacheDir = new File(context.getCacheDir(), "ownCloud");
- if (cacheDir.exists()) {
- deleteRecursive(cacheDir);
- }
- cacheDir.mkdirs();
- }
-
- private void setupClient() {
- Uri serverUri = Uri.parse(serverUrl);
- client = OwnCloudClientFactory.createOwnCloudClient(serverUri, context,
- true);
- client.setCredentials(OwnCloudCredentialsFactory.newBasicCredentials(
- userName, password));
- }
-
- @Override
- public IFile getRootDirectory(Context context) {
- return createFromUri(context, URI.create(FileUtils.PATH_SEPARATOR));
- }
-
- @Override
- public IFile createFromUri(Context context, URI uri) {
- if(serverUrl != "" || userName != "" || password != ""){
- ReadRemoteFileOperation refreshOperation = new ReadRemoteFileOperation(
- uri.getPath());
- this.result = refreshOperation.execute(client);
- if (!result.isSuccess()) {
- throw buildRuntimeExceptionForResultCode(result.getCode());
- }
- if (result.getData().size() > 0) {
- return new OwnCloudFile(this, (RemoteFile) result.getData().get(0));
- }
- } else {
- throw buildRuntimeExceptionForResultCode(ResultCode.WRONG_CONNECTION);
- }
-
- return null;
- }
-
- @Override
- public int getNameResource() {
- return R.string.owncloud;
- }
-
- /**
- * Used by OwnCloudFiles to get a configured client to run their own
- * operations.
- *
- * @return configured OwnCloudClient.
- */
- protected OwnCloudClient getClient() {
- return client;
- }
-
- /**
- * Used by OwnCloudFiles to get the cache directory they should download
- * files to.
- *
- * @return cache directory.
- */
- protected File getCacheDir() {
- return cacheDir;
- }
-
- /**
- * Build the proper RuntimeException for some error result.
- *
- * @param code Result code got from some RemoteOperationResult.
- * @return exception with the proper internationalized error message.
- */
- protected RuntimeException buildRuntimeExceptionForResultCode(ResultCode code) {
- int errorMessage;
- switch (code) {
- case WRONG_CONNECTION: // SocketException
- case FILE_NOT_FOUND: // HTTP 404
- errorMessage = R.string.owncloud_wrong_connection;
- break;
- case UNAUTHORIZED: // wrong user/pass
- errorMessage = R.string.owncloud_unauthorized;
- break;
- default:
- errorMessage = R.string.owncloud_unspecified_error;
- break;
- }
- return new RuntimeException(context.getString(errorMessage));
- }
-
- /**
- * Deletes files and recursively deletes directories.
- *
- * @param file
- * File or directory to be deleted.
- */
- private static void deleteRecursive(File file) {
- if (file.isDirectory()) {
- for (File child : file.listFiles())
- deleteRecursive(child);
- }
- file.delete();
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences preferences,
- String key) {
- boolean changed = false;
- if (key.equals(DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_SERVER)) {
- serverUrl = preferences.getString(key, "");
- changed = true;
- }
- else if (key.equals(DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_USER_NAME)) {
- userName = preferences.getString(key, "");
- changed = true;
- }
- else if (key.equals(DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_PASSWORD)) {
- password = preferences.getString(key, "");
- changed = true;
- }
-
- if (changed)
- setupClient();
- }
-
- @Override
- public int getId() {
- return id;
- }
-
- @Override
- public boolean checkProviderAvailability(Context context) {
- return client != null;
- }
-}
diff --git a/android/source/src/java/org/libreoffice/ui/FileUtilities.java b/android/source/src/java/org/libreoffice/ui/FileUtilities.java
index 7a58486004cd..7fc8c3c84eb1 100644
--- a/android/source/src/java/org/libreoffice/ui/FileUtilities.java
+++ b/android/source/src/java/org/libreoffice/ui/FileUtilities.java
@@ -8,25 +8,18 @@
*/
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;
+import android.net.Uri;
+import android.provider.OpenableColumns;
import android.util.Log;
-import android.webkit.MimeTypeMap;
public class FileUtilities {
- private static String LOGTAG = FileUtilities.class.getSimpleName();
-
- static final int ALL = -1;
+ private static final String LOGTAG = FileUtilities.class.getSimpleName();
// These have to be in sync with the file_view_modes resource.
static final int DOC = 0;
@@ -36,26 +29,16 @@ 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";
- public static final String DEFAULT_DRAWING_EXTENSION = ".odg";
+ public static final String MIMETYPE_OPENDOCUMENT_TEXT = "application/vnd.oasis.opendocument.text";
+ public static final String MIMETYPE_OPENDOCUMENT_SPREADSHEET = "application/vnd.oasis.opendocument.spreadsheet";
+ public static final String MIMETYPE_OPENDOCUMENT_PRESENTATION = "application/vnd.oasis.opendocument.presentation";
+ public static final String MIMETYPE_OPENDOCUMENT_GRAPHICS = "application/vnd.oasis.opendocument.graphics";
+ public static final String MIMETYPE_PDF = "application/pdf";
private static final Map<String, Integer> mExtnMap = new HashMap<String, Integer>();
- private static final Map<String, String> extensionToMimeTypeMap = new HashMap<String, String>();
static {
// Please keep this in sync with AndroidManifest.xml
+ // and 'SUPPORTED_MIME_TYPES' in LibreOfficeUIActivity.java
// ODF
mExtnMap.put(".odt", DOC);
@@ -80,7 +63,7 @@ public class FileUtilities {
mExtnMap.put(".vsdx", DRAWING);
mExtnMap.put(".pub", DRAWING);
mExtnMap.put(".ppt", IMPRESS);
- // mExtnMap.put(".pps", IMPRESS);
+ mExtnMap.put(".pps", IMPRESS);
mExtnMap.put(".xls", CALC);
// MS templates
@@ -91,7 +74,7 @@ public class FileUtilities {
// OOXML
mExtnMap.put(".docx", DOC);
mExtnMap.put(".pptx", IMPRESS);
- // mExtnMap.put(".ppsx", IMPRESS);
+ mExtnMap.put(".ppsx", IMPRESS);
mExtnMap.put(".xlsx", CALC);
// OOXML templates
@@ -109,22 +92,6 @@ public class FileUtilities {
mExtnMap.put(".svm", DRAWING);
mExtnMap.put(".wmf", DRAWING);
mExtnMap.put(".svg", DRAWING);
-
- // Some basic MIME types
- // Android's MimeTypeMap lacks some types that we need
- extensionToMimeTypeMap.put("odb", "application/vnd.oasis.opendocument.database");
- extensionToMimeTypeMap.put("odf", "application/vnd.oasis.opendocument.formula");
- extensionToMimeTypeMap.put("odg", "application/vnd.oasis.opendocument.graphics");
- extensionToMimeTypeMap.put("otg", "application/vnd.oasis.opendocument.graphics-template");
- extensionToMimeTypeMap.put("odi", "application/vnd.oasis.opendocument.image");
- extensionToMimeTypeMap.put("odp", "application/vnd.oasis.opendocument.presentation");
- extensionToMimeTypeMap.put("otp", "application/vnd.oasis.opendocument.presentation-template");
- extensionToMimeTypeMap.put("ods", "application/vnd.oasis.opendocument.spreadsheet");
- extensionToMimeTypeMap.put("ots", "application/vnd.oasis.opendocument.spreadsheet-template");
- extensionToMimeTypeMap.put("odt", "application/vnd.oasis.opendocument.text");
- extensionToMimeTypeMap.put("odm", "application/vnd.oasis.opendocument.text-master");
- extensionToMimeTypeMap.put("ott", "application/vnd.oasis.opendocument.text-template");
- extensionToMimeTypeMap.put("oth", "application/vnd.oasis.opendocument.text-web");
}
public static String getExtension(String filename) {
@@ -149,129 +116,42 @@ public class FileUtilities {
return type;
}
- static String getMimeType(String filename) {
- String extension = MimeTypeMap.getFileExtensionFromUrl(filename);
- String mime = extensionToMimeTypeMap.get(extension);
- if (mime == null) {
- //fallback to Android's MimeTypeMap
- mime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
- extension);
- }
- return mime;
+ /**
+ * Returns whether the passed MIME type is one for a document template.
+ */
+ public static boolean isTemplateMimeType(final String mimeType) {
+ // this works for ODF and OOXML template MIME types
+ return mimeType != null && mimeType.endsWith("template");
}
- // Filter by mode, and/or in future by filename/wildcard
- private static boolean doAccept(String filename, int byMode, String byFilename) {
- Log.d(LOGTAG, "doAccept : " + filename + " mode " + byMode + " byFilename " + byFilename);
- if (filename == null)
- return false;
-
- // check extension
- if (byMode != ALL) {
- if (mExtnMap.get (getExtension (filename)) != byMode)
- return false;
- }
- if (!byFilename.equals("")) {
- // FIXME return false on a non-match
- }
- return true;
+ public static String stripExtensionFromFileName(final String fileName)
+ {
+ return fileName.split("\\.[A-Za-z0-9]*$")[0];
}
- 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, "");
+ /**
+ * Tries to retrieve the display (which should be the document name)
+ * for the given URI using the given resolver.
+ */
+ public static String retrieveDisplayNameForDocumentUri(ContentResolver resolver, Uri docUri) {
+ String displayName = "";
+ // try to retrieve original file name
+ Cursor cursor = null;
+ try {
+ String[] columns = {OpenableColumns.DISPLAY_NAME};
+ cursor = resolver.query(docUri, columns, null, null, null);
+ if (cursor != null && cursor.moveToFirst()) {
+ displayName = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME));
}
- };
- }
-
- 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, "");
+ } catch (SecurityException e) {
+ // thrown e.g. when Uri has become invalid, e.g. corresponding file has been deleted
+ Log.i(LOGTAG, "SecurityException when trying to receive display name for Uri " + docUri);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
}
- };
- }
-
- 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(".");
- }
-
- static boolean isThumbnail(File file) {
- 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" ;
+ return displayName;
}
}
diff --git a/android/source/src/java/org/libreoffice/ui/FolderIconView.java b/android/source/src/java/org/libreoffice/ui/FolderIconView.java
deleted file mode 100644
index cde6cd27af4c..000000000000
--- a/android/source/src/java/org/libreoffice/ui/FolderIconView.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/* -*- Mode: Java; 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.ui;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.RectF;
-import android.graphics.Color;
-import android.util.AttributeSet;
-import android.util.Log;
-import android.view.View;
-
-import java.io.File;
-import java.util.Stack;
-
-public class FolderIconView extends View{
- private String LOGTAG = "FolderIconView";
-
- private Paint mPaintBlack;
- private Paint mPaintGray;
- private Paint mPaintShadow;
-
- private File dir;
-
- public FolderIconView(Context context) {
- super(context);
- initialisePaints();
- }
- public FolderIconView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initialisePaints();
- }
- public FolderIconView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- initialisePaints();
- }
-
- private void initialisePaints() {
- mPaintBlack = new Paint();
- mPaintBlack.setColor(Color.DKGRAY);//Can also use parseColor(String "#aarrggbb")
- mPaintBlack.setAntiAlias(true);
-
- mPaintGray = new Paint();
- mPaintGray.setColor(Color.GRAY);//Can also use parseColor(String "#aarrggbb")
- mPaintGray.setAntiAlias(true);
-
- mPaintShadow = new Paint();
- mPaintShadow.setColor(Color.parseColor("#88888888"));
- mPaintShadow.setAntiAlias(true);
- }
-
- public void setDir(File dir) {
- this.dir = dir;
- }
-
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- Log.d(LOGTAG, "onDraw");
- //float width = (float)canvas.getWidth();
- //float height = (float)canvas.getHeight();
- float width = (float) this.getWidth();
- float height = (float) this.getHeight();
- float centerX = width*0.5f;// centered on horz axis
- float centerY = height*0.5f;
- float outerRadius = 0.8f*0.5f*width;
- float innerRadius = 0.7f*0.5f*width;
- float thumbHeight = outerRadius*1.25f;
- float thumbWidth = thumbHeight*(float)(1/Math.sqrt(2));
- float DZx = 0.2f*outerRadius;
- float DZy = 0.2f*outerRadius;
- //Bitmap blankPage = BitmapFactory.decodeResource(getResources(), R.drawable.page);
- Log.i(LOGTAG, Float.toString(width) + "x" + Float.toString(height));
- canvas.drawCircle(centerX, centerY, outerRadius, mPaintGray);
- canvas.drawCircle(centerX, centerY, innerRadius, mPaintBlack);
- //Either get thumbs from directory or use generic page images
- //For now just get the first 4 thumbs -> add some checks later
- if (dir == null)
- return;//TODO
- File[] contents = dir.listFiles();//TODO consider filtering thumbs to match grid.
- if (contents == null)
- // dir is not a directory,
- // or user does not have permissions to read it
- return;
- Stack<Bitmap> thumbs = new Stack<Bitmap>();
- BitmapFactory factory = new BitmapFactory();
- for (File file : contents) {
- if (!FileUtilities.isThumbnail(file))
- continue;
- thumbs.push(BitmapFactory.decodeFile(file.getAbsolutePath()));//TODO switch to push for semantics
- if (thumbs.size() > 3)
- break;
- }
- /*while(thumbs.size() < 4) {// padd out with blanks?
- thumbs.push(blankPage);
- }*/
- Log.i(LOGTAG, Integer.toString(thumbs.size()));
- //should handle empty folders better
- // options:
- // don't show?
- // show generic LO icons for writer etc
- // Show a generic blank page icon
- if (thumbs.isEmpty())
- return;
- /*float left = centerX ;//+ 0.25f*outerRadius;
- float top = centerY - 0.5f*outerRadius;
- float right = left + thumbs.get(0).getWidth()*0.4f;
- float bottom = top + thumbs.get(0).getHeight()*0.4f;
- RectF dest = new RectF(left, top, right, bottom);
- RectF shadowBox = new RectF(dest);
- shadowBox.inset(-1, -1);
- int size = thumbs.size();
- for (int i = 1; i <= size; i++) {
- canvas.drawRect(shadowBox, mPaintShadow);
- canvas.drawBitmap(thumbs.pop(), null, dest, null);
- dest.offset(-outerRadius*0.2f, outerRadius*0.1f);
- shadowBox.offset(-outerRadius*0.2f, outerRadius*0.1f);
- }*/
- float left;
- float top;
- float right;
- float bottom;
- RectF dest;
- RectF shadowBox;
- int size;
- switch(thumbs.size()) {
- case 0:
- break;
- case 1:
- left = centerX - 0.5f*thumbWidth;
- top = centerY - 0.5f*thumbHeight;
- right = left + thumbWidth;
- bottom = top + thumbHeight;
- dest = new RectF(left, top, right, bottom);
- shadowBox = new RectF(dest);
- shadowBox.inset(-1, -1);
- canvas.drawRect(shadowBox, mPaintShadow);
- canvas.drawBitmap(thumbs.pop(), null, dest, null);
- break;
- case 2:
- left = centerX - 0.5f*thumbWidth + 0.5f*DZx;
- top = centerY - 0.5f*thumbHeight - 0.5f*DZy;
- right = left + thumbWidth;
- bottom = top + thumbHeight;
- dest = new RectF(left, top, right, bottom);
- shadowBox = new RectF(dest);
- shadowBox.inset(-1, -1);
- size = thumbs.size();
- for (int i = 1; i <= size; i++) {
- canvas.drawRect(shadowBox, mPaintShadow);
- canvas.drawBitmap(thumbs.pop(), null, dest, null);
- dest.offset(-DZx, DZy);
- shadowBox.offset(-DZx, DZy);
- }
- break;
- case 3:
- left = centerX - 0.5f*thumbWidth + DZx;
- top = centerY - 0.5f*thumbHeight - DZy;
- right = left + thumbWidth;
- bottom = top + thumbHeight;
- dest = new RectF(left, top, right, bottom);
- shadowBox = new RectF(dest);
- shadowBox.inset(-1, -1);
- size = thumbs.size();
- for (int i = 1; i <= size; i++) {
- canvas.drawRect(shadowBox, mPaintShadow);
- canvas.drawBitmap(thumbs.pop(), null, dest, null);
- dest.offset(-DZx, DZy);
- shadowBox.offset(-DZx, DZy);
- }
- break;
- case 4:
- left = centerX - 0.5f*thumbWidth + 1.5f*DZx;
- top = centerY - 0.5f*thumbHeight - 1.5f*DZy;
- right = left + thumbWidth;
- bottom = top + thumbHeight;
- dest = new RectF(left, top, right, bottom);
- shadowBox = new RectF(dest);
- shadowBox.inset(-1, -1);
- size = thumbs.size();
- for (int i = 1; i <= size; i++) {
- canvas.drawRect(shadowBox, mPaintShadow);
- canvas.drawBitmap(thumbs.pop(), null, dest, null);
- dest.offset(-DZx, DZy);
- shadowBox.offset(-DZx, DZy);
- }
- break;
- default:
- break;
- }
- }
-
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java b/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java
index a9d797c4bf28..bc5203d9c6eb 100644
--- a/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java
+++ b/android/source/src/java/org/libreoffice/ui/LibreOfficeUIActivity.java
@@ -10,134 +10,119 @@
package org.libreoffice.ui;
import android.Manifest;
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.BroadcastReceiver;
+import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
-import android.hardware.usb.UsbManager;
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.annotation.NonNull;
-import android.support.design.widget.FloatingActionButton;
-import android.support.design.widget.NavigationView;
-import android.support.v4.app.ActivityCompat;
-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;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.GridLayoutManager;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.Toolbar;
-import android.text.InputType;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+import androidx.core.view.ViewCompat;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.recyclerview.widget.GridLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.appcompat.widget.Toolbar;
+import android.text.TextUtils;
import android.util.Log;
-import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
-import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
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.EditText;
-import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import android.widget.Toast;
import org.libreoffice.AboutDialogFragment;
-import org.libreoffice.LOKitShell;
+import org.libreoffice.BuildConfig;
import org.libreoffice.LibreOfficeMainActivity;
import org.libreoffice.LocaleHelper;
import org.libreoffice.R;
import org.libreoffice.SettingsActivity;
import org.libreoffice.SettingsListenerModel;
-import org.libreoffice.storage.DocumentProviderFactory;
-import org.libreoffice.storage.DocumentProviderSettingsActivity;
-import org.libreoffice.storage.IDocumentProvider;
-import org.libreoffice.storage.IFile;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FilenameFilter;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Date;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
public class LibreOfficeUIActivity extends AppCompatActivity implements SettingsListenerModel.OnSettingsPreferenceChangedListener, View.OnClickListener{
- private String LOGTAG = LibreOfficeUIActivity.class.getSimpleName();
- private SharedPreferences prefs;
- private int filterMode = FileUtilities.ALL;
- private int viewMode;
- private int sortMode;
- private boolean showHiddenFiles;
- private String displayLanguage;
-
- // dynamic permissions IDs
- private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0;
+ public enum DocumentType {
+ WRITER,
+ CALC,
+ IMPRESS,
+ DRAW,
+ INVALID
+ }
- FileFilter fileFilter;
- FilenameFilter filenameFilter;
- private List<IFile> filePaths = new ArrayList<IFile>();
- private DocumentProviderFactory documentProviderFactory;
- private IDocumentProvider documentProvider;
- private IFile homeDirectory;
- private IFile currentDirectory;
- private int currentlySelectedFile;
+ private static final String LOGTAG = LibreOfficeUIActivity.class.getSimpleName();
- private static final String CURRENT_DIRECTORY_KEY = "CURRENT_DIRECTORY";
- private static final String DOC_PROVIDER_KEY = "CURRENT_DOCUMENT_PROVIDER";
- private static final String FILTER_MODE_KEY = "FILTER_MODE";
- public static final String EXPLORER_VIEW_TYPE_KEY = "EXPLORER_VIEW_TYPE";
public static final String EXPLORER_PREFS_KEY = "EXPLORER_PREFS";
- public static final String SORT_MODE_KEY = "SORT_MODE";
- private static final String RECENT_DOCUMENTS_KEY = "RECENT_DOCUMENTS";
- private static final String ENABLE_SHOW_HIDDEN_FILES_KEY = "ENABLE_SHOW_HIDDEN_FILES";
+ private static final String RECENT_DOCUMENTS_KEY = "RECENT_DOCUMENT_URIS";
+ // delimiter used for storing multiple URIs in a string
+ private static final String RECENT_DOCUMENTS_DELIMITER = " ";
private static final String DISPLAY_LANGUAGE = "DISPLAY_LANGUAGE";
- public static final String NEW_FILE_PATH_KEY = "NEW_FILE_PATH_KEY";
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;
+ // keep this in sync with 'AndroidManifext.xml'
+ private static final String[] SUPPORTED_MIME_TYPES = {
+ "application/vnd.oasis.opendocument.text",
+ "application/vnd.oasis.opendocument.graphics",
+ "application/vnd.oasis.opendocument.presentation",
+ "application/vnd.oasis.opendocument.spreadsheet",
+ "application/vnd.oasis.opendocument.text-flat-xml",
+ "application/vnd.oasis.opendocument.graphics-flat-xml",
+ "application/vnd.oasis.opendocument.presentation-flat-xml",
+ "application/vnd.oasis.opendocument.spreadsheet-flat-xml",
+ "application/vnd.oasis.opendocument.text-template",
+ "application/vnd.oasis.opendocument.spreadsheet-template",
+ "application/vnd.oasis.opendocument.graphics-template",
+ "application/vnd.oasis.opendocument.presentation-template",
+ "application/rtf",
+ "text/rtf",
+ "application/msword",
+ "application/vnd.ms-powerpoint",
+ "application/vnd.ms-excel",
+ "application/vnd.visio",
+ "application/vnd.visio.xml",
+ "application/x-mspublisher",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation",
+ "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
+ "application/vnd.openxmlformats-officedocument.presentationml.template",
+ "text/csv",
+ "text/comma-separated-values",
+ "application/vnd.ms-works",
+ "application/vnd.apple.keynote",
+ "application/x-abiword",
+ "application/x-pagemaker",
+ "image/x-emf",
+ "image/x-svm",
+ "image/x-wmf",
+ "image/svg+xml",
+ };
- private DrawerLayout drawerLayout;
- private NavigationView navigationDrawer;
- private ActionBar actionBar;
- private ActionBarDrawerToggle drawerToggle;
- private RecyclerView fileRecyclerView;
- private RecyclerView recentRecyclerView;
+ private static final int REQUEST_CODE_OPEN_FILECHOOSER = 12345;
- private boolean canQuit = false;
+ private static final int PERMISSION_WRITE_EXTERNAL_STORAGE = 0;
private Animation fabOpenAnimation;
private Animation fabCloseAnimation;
@@ -156,45 +141,49 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- // initialize document provider factory
- DocumentProviderFactory.initialize(this);
- documentProviderFactory = DocumentProviderFactory.getInstance();
-
- PreferenceManager.setDefaultValues(this, R.xml.documentprovider_preferences, false);
readPreferences();
SettingsListenerModel.getInstance().setListener(this);
- // Registering the USB detect broadcast receiver
- IntentFilter filter = new IntentFilter();
- filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
- filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
- registerReceiver(mUSBReceiver, filter);
- // init UI and populate with contents from the provider
-
+ // init UI
createUI();
fabOpenAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_open);
fabCloseAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_close);
}
@Override
+ protected void onStart() {
+ super.onStart();
+ if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+ Log.i(LOGTAG, "no permission to read external storage - asking for permission");
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
+ PERMISSION_WRITE_EXTERNAL_STORAGE);
+ }
+ }
+
+ @Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(LocaleHelper.onAttach(newBase));
}
public void createUI() {
-
setContentView(R.layout.activity_document_browser);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- actionBar = getSupportActionBar();
+ ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setIcon(R.mipmap.ic_launcher);
}
editFAB = findViewById(R.id.editFAB);
editFAB.setOnClickListener(this);
+ // allow creating new docs only when experimental editing is enabled
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
+ final boolean bEditingEnabled = BuildConfig.ALLOW_EDITING && preferences.getBoolean(LibreOfficeMainActivity.ENABLE_EXPERIMENTAL_PREFS_KEY, false);
+ editFAB.setVisibility(bEditingEnabled ? View.VISIBLE : View.INVISIBLE);
+
impressFAB = findViewById(R.id.newImpressFAB);
impressFAB.setOnClickListener(this);
writerFAB = findViewById(R.id.newWriterFAB);
@@ -207,110 +196,27 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
impressLayout = findViewById(R.id.impressLayout);
calcLayout = findViewById(R.id.calcLayout);
drawLayout = findViewById(R.id.drawLayout);
+ TextView openFileView = findViewById(R.id.open_file_button);
+ openFileView.setOnClickListener(this);
- recentRecyclerView = findViewById(R.id.list_recent);
- Set<String> recentFileStrings = prefs.getStringSet(RECENT_DOCUMENTS_KEY, new HashSet<String>());
+ RecyclerView recentRecyclerView = findViewById(R.id.list_recent);
- final ArrayList<IFile> recentFiles = new ArrayList<IFile>();
+ SharedPreferences prefs = getSharedPreferences(EXPLORER_PREFS_KEY, MODE_PRIVATE);
+ String recentPref = prefs.getString(RECENT_DOCUMENTS_KEY, "");
+ String[] recentFileStrings = recentPref.split(RECENT_DOCUMENTS_DELIMITER);
+
+ final List<RecentFile> recentFiles = new ArrayList<>();
for (String recentFileString : recentFileStrings) {
- try {
- if(documentProvider != null)
- recentFiles.add(documentProvider.createFromUri(this, new URI(recentFileString)));
- } catch (URISyntaxException e) {
- e.printStackTrace();
- } catch (RuntimeException e){
- e.printStackTrace();
+ Uri uri = Uri.parse(recentFileString);
+ String filename = FileUtilities.retrieveDisplayNameForDocumentUri(getContentResolver(), uri);
+ if (!filename.isEmpty()) {
+ recentFiles.add(new RecentFile(uri, filename));
}
}
recentRecyclerView.setLayoutManager(new GridLayoutManager(this, 2));
recentRecyclerView.setAdapter(new RecentFilesAdapter(this, recentFiles));
-
- fileRecyclerView = findViewById(R.id.file_recycler_view);
- //This should be tested because it possibly disables view recycling
- fileRecyclerView.setNestedScrollingEnabled(false);
- openDirectory(currentDirectory);
- registerForContextMenu(fileRecyclerView);
-
- //Setting up navigation drawer
- drawerLayout = findViewById(R.id.drawer_layout);
- navigationDrawer = findViewById(R.id.navigation_drawer);
-
- final ArrayList<CharSequence> providerNames = new ArrayList<CharSequence>(
- Arrays.asList(documentProviderFactory.getNames())
- );
-
- // Loop through the document providers menu items and check if they are available or not
- for (int index = 0; index < providerNames.size(); index++) {
- MenuItem item = navigationDrawer.getMenu().getItem(index);
- item.setEnabled(documentProviderFactory.getProvider(index).checkProviderAvailability(this));
- }
-
- navigationDrawer.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
- @Override
- public boolean onNavigationItemSelected(@NonNull MenuItem item) {
-
- switch (item.getItemId()) {
- case R.id.menu_storage_preferences: {
- startActivity(new Intent(LibreOfficeUIActivity.this, DocumentProviderSettingsActivity.class));
- return true;
- }
-
- case R.id.menu_provider_documents: {
- switchToDocumentProvider(documentProviderFactory.getProvider(0));
- return true;
- }
-
- case R.id.menu_provider_filesystem: {
- switchToDocumentProvider(documentProviderFactory.getProvider(1));
- return true;
- }
-
- case R.id.menu_provider_extsd: {
- switchToDocumentProvider(documentProviderFactory.getProvider(2));
- return true;
- }
-
- case R.id.menu_provider_otg: {
- switchToDocumentProvider(documentProviderFactory.getProvider(3));
- return true;
- }
-
- case R.id.menu_provider_owncloud: {
- switchToDocumentProvider(documentProviderFactory.getProvider(4));
- return true;
- }
-
- default:
- return false;
- }
-
-
- }
- });
- drawerToggle = new ActionBarDrawerToggle(this, drawerLayout,
- R.string.document_locations, R.string.close_document_locations) {
-
- @Override
- public void onDrawerOpened(View drawerView) {
- super.onDrawerOpened(drawerView);
- supportInvalidateOptionsMenu();
- navigationDrawer.requestFocus(); // Make keypad navigation easier
- if (isFabMenuOpen) {
- collapseFabMenu(); //Collapse FAB Menu when drawer is opened
- }
- }
-
- @Override
- public void onDrawerClosed(View drawerView) {
- super.onDrawerClosed(drawerView);
- supportInvalidateOptionsMenu();
- }
- };
- drawerToggle.setDrawerIndicatorEnabled(true);
- drawerLayout.addDrawerListener(drawerToggle);
- drawerToggle.syncState();
}
private void expandFabMenu() {
@@ -340,658 +246,126 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
}
@Override
- protected void onPostCreate(Bundle savedInstanceState) {
- super.onPostCreate(savedInstanceState);
-
- drawerToggle.syncState();
- }
-
- private void refreshView() {
- // enable home icon as "up" if required
- if (currentDirectory != null && homeDirectory != null && !currentDirectory.equals(homeDirectory)) {
- drawerToggle.setDrawerIndicatorEnabled(false);
- } else {
- drawerToggle.setDrawerIndicatorEnabled(true);
- }
-
- FileUtilities.sortFiles(filePaths, sortMode);
- // refresh view
- fileRecyclerView.setLayoutManager(isViewModeList() ? new LinearLayoutManager(this) : new GridLayoutManager(this, 3));
- fileRecyclerView.setAdapter(new ExplorerItemAdapter(this, filePaths));
- // close drawer if it was open
- drawerLayout.closeDrawer(navigationDrawer);
+ public void onBackPressed() {
if (isFabMenuOpen) {
collapseFabMenu();
+ } else {
+ super.onBackPressed();
}
}
@Override
- public void onBackPressed() {
- if (drawerLayout.isDrawerOpen(navigationDrawer)) {
- drawerLayout.closeDrawer(navigationDrawer);
- if (isFabMenuOpen) {
- collapseFabMenu();
- }
- } else if (currentDirectory != null && homeDirectory != null && !currentDirectory.equals(homeDirectory)) {
- // navigate upwards in directory hierarchy
- openParentDirectory();
- } else if (isFabMenuOpen) {
- collapseFabMenu();
- } else {
- // only exit if warning has been shown
- if (canQuit) {
- super.onBackPressed();
- return;
- }
-
- // show warning about leaving the app and set a timer
- Toast.makeText(this, R.string.back_again_to_quit,
- Toast.LENGTH_SHORT).show();
- canQuit = true;
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- canQuit = false;
- }
- }, 3000);
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_OPEN_FILECHOOSER && resultCode == RESULT_OK) {
+ final Uri fileUri = data.getData();
+ openDocument(fileUri);
}
}
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- super.onCreateContextMenu(menu, v, menuInfo);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.context_menu, menu);
- }
+ private void showSystemFilePickerAndOpenFile() {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, SUPPORTED_MIME_TYPES);
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.context_menu_open:
- open(currentlySelectedFile);
- return true;
- case R.id.context_menu_share:
- share(currentlySelectedFile);
- return true;
- default:
- return super.onContextItemSelected(item);
+ try {
+ startActivityForResult(intent, REQUEST_CODE_OPEN_FILECHOOSER);
+ } catch (ActivityNotFoundException e) {
+ Log.w(LOGTAG, "No activity available that can handle the intent to open a document.");
}
}
- private boolean isViewModeList(){
- return viewMode == LIST_VIEW;
- }
-
- private void switchToDocumentProvider(IDocumentProvider provider) {
+ public void openDocument(final Uri documentUri) {
+ // "forward" to LibreOfficeMainActivity to open the file
+ Intent intent = new Intent(Intent.ACTION_VIEW, documentUri);
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- new AsyncTask<IDocumentProvider, Void, Void>() {
- @Override
- protected Void doInBackground(IDocumentProvider... provider) {
- // switch document provider:
- // these operations may imply network access and must be run in
- // a different thread
- try {
- homeDirectory = provider[0].getRootDirectory(LibreOfficeUIActivity.this);
- List<IFile> paths = homeDirectory.listFiles(FileUtilities
- .getFileFilter(filterMode));
- filePaths = new ArrayList<IFile>();
- for(IFile file: paths) {
- if(showHiddenFiles){
- filePaths.add(file);
- } else {
- if(!file.getName().startsWith(".")){
- filePaths.add(file);
- }
- }
- }
- }
- catch (final RuntimeException e) {
- final Activity activity = LibreOfficeUIActivity.this;
- activity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(activity, e.getMessage(),
- Toast.LENGTH_SHORT).show();
- }
- });
- startActivity(new Intent(activity, DocumentProviderSettingsActivity.class));
- Log.e(LOGTAG, "failed to switch document provider "+ e.getMessage(), e.getCause());
- return null;
- }
- //no exception
- documentProvider = provider[0];
- currentDirectory = homeDirectory;
- return null;
- }
+ addDocumentToRecents(documentUri);
- @Override
- protected void onPostExecute(Void result) {
- refreshView();
- }
- }.execute(provider);
+ String packageName = getApplicationContext().getPackageName();
+ ComponentName componentName = new ComponentName(packageName,
+ LibreOfficeMainActivity.class.getName());
+ intent.setComponent(componentName);
+ startActivity(intent);
}
- public void openDirectory(IFile dir) {
- if (dir == null)
- return;
-
- //show recent files if in home directory
- if (dir.equals(homeDirectory)) {
- recentRecyclerView.setVisibility(View.VISIBLE);
- findViewById(R.id.header_browser).setVisibility((View.VISIBLE));
- findViewById(R.id.header_recents).setVisibility((View.VISIBLE));
- actionBar.setTitle(R.string.app_name);
- findViewById(R.id.text_directory_path).setVisibility(View.GONE);
+ private void loadNewDocument(DocumentType docType) {
+ final String newDocumentType;
+ if (docType == DocumentType.WRITER) {
+ newDocumentType = NEW_WRITER_STRING_KEY;
+ } else if (docType == DocumentType.CALC) {
+ newDocumentType = NEW_CALC_STRING_KEY;
+ } else if (docType == DocumentType.IMPRESS) {
+ newDocumentType = NEW_IMPRESS_STRING_KEY;
+ } else if (docType == DocumentType.DRAW) {
+ newDocumentType = NEW_DRAW_STRING_KEY;
} else {
- recentRecyclerView.setVisibility(View.GONE);
- findViewById(R.id.header_browser).setVisibility((View.GONE));
- findViewById(R.id.header_recents).setVisibility((View.GONE));
- actionBar.setTitle(dir.getName());
- findViewById(R.id.text_directory_path).setVisibility(View.VISIBLE);
- ((TextView)findViewById(R.id.text_directory_path)).setText(getString(R.string.current_dir,
- dir.getUri().getPath()));
+ Log.w(LOGTAG, "invalid document type passed to loadNewDocument method. Ignoring request");
+ return;
}
- new AsyncTask<IFile, Void, Void>() {
- @Override
- protected Void doInBackground(IFile... dir) {
- // get list of files:
- // this operation may imply network access and must be run in
- // a different thread
- currentDirectory = dir[0];
- try {
- List<IFile> paths = currentDirectory.listFiles(FileUtilities
- .getFileFilter(filterMode));
- filePaths = new ArrayList<IFile>();
- for(IFile file: paths) {
- if(showHiddenFiles){
- filePaths.add(file);
- } else {
- if(!file.getName().startsWith(".")){
- filePaths.add(file);
- }
- }
- }
- }
- catch (final RuntimeException e) {
- final Activity activity = LibreOfficeUIActivity.this;
- 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;
- }
-
- @Override
- protected void onPostExecute(Void result) {
- refreshView();
- }
- }.execute(dir);
- }
-
- public void open(final IFile document) {
- addDocumentToRecents(document);
- new AsyncTask<IFile, Void, File>() {
- @Override
- protected File doInBackground(IFile... document) {
- // this operation may imply network access and must be run in
- // a different thread
- try {
- return document[0].getDocument();
- }
- catch (final RuntimeException e) {
- final Activity activity = LibreOfficeUIActivity.this;
- 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;
- }
- }
-
- @Override
- protected void onPostExecute(File file) {
- if (file != null) {
- Intent i = new Intent(Intent.ACTION_VIEW, Uri.fromFile(file));
- String packageName = getApplicationContext().getPackageName();
- ComponentName componentName = new ComponentName(packageName,
- LibreOfficeMainActivity.class.getName());
- i.setComponent(componentName);
-
- // these extras allow to rebuild the IFile object in LOMainActivity
- i.putExtra("org.libreoffice.document_provider_id",
- documentProvider.getId());
- i.putExtra("org.libreoffice.document_uri",
- document.getUri());
-
- startActivity(i);
- }
- }
- }.execute(document);
- }
-
- // Opens an Input dialog to get the name of new file
- private void createNewFileInputDialog(final String defaultFileName, final String newDocumentType) {
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setTitle(R.string.create_new_document_title);
- final EditText input = new EditText(this);
- input.setInputType(InputType.TYPE_CLASS_TEXT);
- input.setText(defaultFileName);
- builder.setView(input);
-
- builder.setPositiveButton(R.string.action_create, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final String newFilePath = currentDirectory.getUri().getPath() + input.getText().toString();
- loadNewDocument(newDocumentType, newFilePath);
- }
- });
-
- builder.setNegativeButton(R.string.action_cancel, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
-
- builder.show();
- }
-
- private void loadNewDocument(String newDocumentType, String newFilePath) {
Intent intent = new Intent(LibreOfficeUIActivity.this, LibreOfficeMainActivity.class);
intent.putExtra(NEW_DOC_TYPE_KEY, newDocumentType);
- intent.putExtra(NEW_FILE_PATH_KEY, newFilePath);
startActivity(intent);
}
- private void open(int position) {
- IFile file = filePaths.get(position);
- if (!file.isDirectory()) {
- open(file);
- } else {
- openDirectory(file);
- }
- }
-
- private void openParentDirectory() {
- new AsyncTask<Void, Void, IFile>() {
- @Override
- protected IFile doInBackground(Void... dir) {
- // this operation may imply network access and must be run in
- // a different thread
- return currentDirectory.getParent(LibreOfficeUIActivity.this);
- }
-
- @Override
- protected void onPostExecute(IFile result) {
- openDirectory(result);
- }
- }.execute();
- }
-
- private void share(int position) {
-
- new AsyncTask<IFile, Void, File>() {
- @Override
- protected File doInBackground(IFile... document) {
- // this operation may imply network access and must be run in
- // a different thread
- try {
- return document[0].getDocument();
- } catch (final RuntimeException e) {
- final Activity activity = LibreOfficeUIActivity.this;
- 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;
- }
- }
-
- @Override
- protected void onPostExecute(File file) {
- if (file != null) {
- Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
- Uri uri = Uri.fromFile(file);
- sharingIntent.setType(FileUtilities.getMimeType(file.getName()));
- sharingIntent.putExtra(android.content.Intent.EXTRA_STREAM, uri);
- sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT,
- file.getName());
- startActivity(Intent.createChooser(sharingIntent,
- getString(R.string.share_via)));
- }
- }
- }.execute(filePaths.get(position));
- }
-
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.view_menu, menu);
- switch (sortMode) {
- case FileUtilities.SORT_SMALLEST: {
- menu.findItem(R.id.menu_sort_size_asc).setChecked(true);
- }
- break;
-
- case FileUtilities.SORT_LARGEST: {
- menu.findItem(R.id.menu_sort_size_desc).setChecked(true);
- }
- break;
-
- case FileUtilities.SORT_AZ: {
- menu.findItem(R.id.menu_sort_az).setChecked(true);
- }
- break;
-
- case FileUtilities.SORT_ZA: {
- menu.findItem(R.id.menu_sort_za).setChecked(true);
- }
- break;
-
- case FileUtilities.SORT_NEWEST: {
- menu.findItem(R.id.menu_sort_modified_newest).setChecked(true);
- }
- break;
-
- case FileUtilities.SORT_OLDEST: {
- menu.findItem(R.id.menu_sort_modified_oldest).setChecked(true);
- }
- break;
- }
-
- switch (filterMode) {
- case FileUtilities.ALL:
- menu.findItem(R.id.menu_filter_everything).setChecked(true);
- break;
-
- case FileUtilities.DOC:
- menu.findItem(R.id.menu_filter_documents).setChecked(true);
- break;
-
- case FileUtilities.CALC:
- menu.findItem(R.id.menu_filter_presentations).setChecked(true);
- break;
-
- case FileUtilities.IMPRESS:
- menu.findItem(R.id.menu_filter_presentations).setChecked(true);
- break;
-
- case FileUtilities.DRAWING:
- menu.findItem(R.id.menu_filter_drawings).setChecked(true);
- break;
- }
-
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- // Will close the drawer if the home button is pressed
- if (drawerToggle.onOptionsItemSelected(item)) {
+ final int itemId = item.getItemId();
+ if (itemId == R.id.action_about) {
+ AboutDialogFragment aboutDialogFragment = new AboutDialogFragment();
+ aboutDialogFragment.show(getSupportFragmentManager(), "AboutDialogFragment");
return true;
}
-
- switch (item.getItemId()) {
- case android.R.id.home:
- if (!currentDirectory.equals(homeDirectory)){
- openParentDirectory();
- }
- break;
-
- case R.id.menu_filter_everything:
- item.setChecked(true);
- filterMode = FileUtilities.ALL;
- openDirectory(currentDirectory);
- break;
-
- case R.id.menu_filter_documents:
- item.setChecked(true);
- filterMode = FileUtilities.DOC;
- openDirectory(currentDirectory);
- break;
-
- case R.id.menu_filter_spreadsheets:
- item.setChecked(true);
- filterMode = FileUtilities.CALC;
- openDirectory(currentDirectory);
- break;
-
- case R.id.menu_filter_presentations:
- item.setChecked(true);
- filterMode = FileUtilities.IMPRESS;
- openDirectory(currentDirectory);
- break;
-
- case R.id.menu_filter_drawings:
- item.setChecked(true);
- filterMode = FileUtilities.DRAWING;
- openDirectory(currentDirectory);
- break;
-
- case R.id.menu_sort_size_asc: {
- sortMode = FileUtilities.SORT_SMALLEST;
- this.onResume();
- }
- break;
-
- case R.id.menu_sort_size_desc: {
- sortMode = FileUtilities.SORT_LARGEST;
- this.onResume();
- }
- break;
-
- case R.id.menu_sort_az: {
- sortMode = FileUtilities.SORT_AZ;
- this.onResume();
- }
- break;
-
- case R.id.menu_sort_za: {
- sortMode = FileUtilities.SORT_ZA;
- this.onResume();
- }
- break;
-
- case R.id.menu_sort_modified_newest: {
- sortMode = FileUtilities.SORT_NEWEST;
- this.onResume();
- }
- break;
-
- case R.id.menu_sort_modified_oldest: {
- sortMode = FileUtilities.SORT_OLDEST;
- this.onResume();
- }
- break;
-
- case R.id.action_about: {
- AboutDialogFragment aboutDialogFragment = new AboutDialogFragment();
- aboutDialogFragment.show(getSupportFragmentManager(), "AboutDialogFragment");
- }
- return true;
- case R.id.action_settings:
- startActivity(new Intent(getApplicationContext(), SettingsActivity.class));
- return true;
-
- default:
- return super.onOptionsItemSelected(item);
+ if (itemId == R.id.action_settings) {
+ startActivity(new Intent(getApplicationContext(), SettingsActivity.class));
+ return true;
}
- return true;
+
+ return super.onOptionsItemSelected(item);
}
public void readPreferences(){
- prefs = getSharedPreferences(EXPLORER_PREFS_KEY, MODE_PRIVATE);
- sortMode = prefs.getInt(SORT_MODE_KEY, FileUtilities.SORT_AZ);
SharedPreferences defaultPrefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
- viewMode = Integer.valueOf(defaultPrefs.getString(EXPLORER_VIEW_TYPE_KEY, ""+ GRID_VIEW));
- filterMode = Integer.valueOf(defaultPrefs.getString(FILTER_MODE_KEY , "-1"));
- showHiddenFiles = defaultPrefs.getBoolean(ENABLE_SHOW_HIDDEN_FILES_KEY, false);
- displayLanguage = defaultPrefs.getString(DISPLAY_LANGUAGE, LocaleHelper.SYSTEM_DEFAULT_LANGUAGE);
-
- Intent i = this.getIntent();
- if (i.hasExtra(CURRENT_DIRECTORY_KEY)) {
- try {
- currentDirectory = documentProvider.createFromUri(this, new URI(
- i.getStringExtra(CURRENT_DIRECTORY_KEY)));
- } catch (URISyntaxException e) {
- currentDirectory = documentProvider.getRootDirectory(this);
- }
- Log.d(LOGTAG, CURRENT_DIRECTORY_KEY);
- }
-
- if (i.hasExtra(FILTER_MODE_KEY)) {
- filterMode = i.getIntExtra( FILTER_MODE_KEY, FileUtilities.ALL);
- Log.d(LOGTAG, FILTER_MODE_KEY);
- }
-
- if (i.hasExtra(EXPLORER_VIEW_TYPE_KEY)) {
- viewMode = i.getIntExtra( EXPLORER_VIEW_TYPE_KEY, GRID_VIEW);
- Log.d(LOGTAG, EXPLORER_VIEW_TYPE_KEY);
- }
-
+ final String displayLanguage = defaultPrefs.getString(DISPLAY_LANGUAGE, LocaleHelper.SYSTEM_DEFAULT_LANGUAGE);
LocaleHelper.setLocale(this, displayLanguage);
}
@Override
public void settingsPreferenceChanged(SharedPreferences sharedPreferences, String key) {
readPreferences();
- refreshView();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle outState) {
- // TODO Auto-generated method stub
- super.onSaveInstanceState(outState);
-
- if(currentDirectory != null) {
- outState.putString(CURRENT_DIRECTORY_KEY, currentDirectory.getUri().toString());
- Log.d(LOGTAG, currentDirectory.toString() + Integer.toString(filterMode) + Integer.toString(viewMode));
- }
- outState.putInt(FILTER_MODE_KEY, filterMode);
- outState.putInt(EXPLORER_VIEW_TYPE_KEY , viewMode);
- if(documentProvider != null)
- outState.putInt(DOC_PROVIDER_KEY, documentProvider.getId());
-
- outState.putBoolean(ENABLE_SHOW_HIDDEN_FILES_KEY , showHiddenFiles);
-
- //prefs.edit().putInt(EXPLORER_VIEW_TYPE, viewType).commit();
- Log.d(LOGTAG, "savedInstanceState");
- }
-
- @Override
- protected void onRestoreInstanceState(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onRestoreInstanceState(savedInstanceState);
- if (savedInstanceState.isEmpty()){
- return;
- }
- if (documentProvider == null) {
- Log.d(LOGTAG, "onRestoreInstanceState - documentProvider is null");
- documentProvider = DocumentProviderFactory.getInstance()
- .getProvider(savedInstanceState.getInt(DOC_PROVIDER_KEY));
- }
- try {
- currentDirectory = documentProvider.createFromUri(this, new URI(
- savedInstanceState.getString(CURRENT_DIRECTORY_KEY)));
- } catch (URISyntaxException e) {
- currentDirectory = documentProvider.getRootDirectory(this);
- }
- filterMode = savedInstanceState.getInt(FILTER_MODE_KEY, FileUtilities.ALL);
- viewMode = savedInstanceState.getInt(EXPLORER_VIEW_TYPE_KEY, GRID_VIEW);
- showHiddenFiles = savedInstanceState.getBoolean(ENABLE_SHOW_HIDDEN_FILES_KEY, false);
- //openDirectory(currentDirectory);
- Log.d(LOGTAG, "onRestoreInstanceState");
- Log.d(LOGTAG, currentDirectory.toString() + Integer.toString(filterMode) + Integer.toString(viewMode));
- }
-
- private final BroadcastReceiver mUSBReceiver = new BroadcastReceiver() {
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
- Toast.makeText(context, R.string.usb_connected_configure, Toast.LENGTH_SHORT).show();
- startActivity(new Intent(context, DocumentProviderSettingsActivity.class));
- Log.d(LOGTAG, "USB device attached");
- } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
- Log.d(LOGTAG, "USB device detached");
- }
- }
- };
- @Override
- protected void onPause() {
- super.onPause();
- Log.d(LOGTAG, "onPause");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOGTAG, "onResume");
- Log.d(LOGTAG, "sortMode="+ sortMode + " filterMode=" + filterMode);
createUI();
}
- @Override
- protected void onStart() {
- super.onStart();
- if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
- Log.i(LOGTAG, "no permission to read external storage - asking for permission");
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
- PERMISSION_WRITE_EXTERNAL_STORAGE);
- } else {
- switchToDocumentProvider(documentProviderFactory.getDefaultProvider());
- setEditFABVisibility(View.VISIBLE);
- }
- Log.d(LOGTAG, "onStart");
- }
-
- @Override
- protected void onStop() {
- super.onStop();
- Log.d(LOGTAG, "onStop");
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- unregisterReceiver(mUSBReceiver);
- Log.d(LOGTAG, "onDestroy");
- }
-
- private int dpToPx(int dp){
- final float scale = getApplicationContext().getResources().getDisplayMetrics().density;
- return (int) (dp * scale + 0.5f);
- }
+ private void addDocumentToRecents(Uri fileUri) {
+ SharedPreferences prefs = getSharedPreferences(EXPLORER_PREFS_KEY, MODE_PRIVATE);
- private void addDocumentToRecents(IFile iFile) {
- String newRecent = iFile.getUri().toString();
- Set<String> recentsSet = prefs.getStringSet(RECENT_DOCUMENTS_KEY, new HashSet<String>());
+ // preserve permissions across device reboots,
+ // s. https://developer.android.com/training/data-storage/shared/documents-files#persist-permissions
+ getContentResolver().takePersistableUriPermission(fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
- //create array to work with
- ArrayList<String> recentsArrayList = new ArrayList<String>(recentsSet);
+ String newRecent = fileUri.toString();
+ List<String> recentsList = new ArrayList<>(Arrays.asList(prefs.getString(RECENT_DOCUMENTS_KEY, "").split(RECENT_DOCUMENTS_DELIMITER)));
- //remove string if present, so that it doesn't appear multiple times
- recentsSet.remove(newRecent);
-
- //put the new value in the first place
- recentsArrayList.add(0, newRecent);
+ // remove string if present, so that it doesn't appear multiple times
+ recentsList.remove(newRecent);
+ // put the new value in the first place
+ recentsList.add(0, newRecent);
/*
* 4 because the number of recommended items in App Shortcuts is 4, and also
@@ -999,15 +373,13 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
*/
final int RECENTS_SIZE = 4;
- while (recentsArrayList.size() > RECENTS_SIZE) {
- recentsArrayList.remove(RECENTS_SIZE);
+ while (recentsList.size() > RECENTS_SIZE) {
+ recentsList.remove(RECENTS_SIZE);
}
- //switch to Set, so that it could be inserted into prefs
- recentsSet = new HashSet<String>(recentsArrayList);
-
- prefs.edit().putStringSet(RECENT_DOCUMENTS_KEY, recentsSet).apply();
-
+ // serialize to String that can be set for pref
+ String value = TextUtils.join(RECENT_DOCUMENTS_DELIMITER, recentsList);
+ prefs.edit().putString(RECENT_DOCUMENTS_KEY, value).apply();
//update app shortcuts (7.0 and above)
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N_MR1) {
@@ -1016,12 +388,17 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
//Remove all shortcuts, and apply new ones.
shortcutManager.removeAllDynamicShortcuts();
- ArrayList<ShortcutInfo> shortcuts = new ArrayList<ShortcutInfo>();
- for (String pathString : recentsArrayList) {
+ ArrayList<ShortcutInfo> shortcuts = new ArrayList<>();
+ for (String recentDoc : recentsList) {
+ Uri docUri = Uri.parse(recentDoc);
+ String filename = FileUtilities.retrieveDisplayNameForDocumentUri(getContentResolver(), docUri);
+ if (filename.isEmpty()) {
+ continue;
+ }
//find the appropriate drawable
int drawable = 0;
- switch (FileUtilities.getType(pathString)) {
+ switch (FileUtilities.getType(filename)) {
case FileUtilities.DOC:
drawable = R.drawable.writer;
break;
@@ -1036,12 +413,7 @@ public class LibreOfficeUIActivity extends AppCompatActivity implements Settings
break;
}
- File file = new File(pathString);
-
- //for some reason, getName uses %20 instead of space
- String filename = file.getName().replace("%20", " ");
-
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.fromFile(file));
+ Intent intent = new Intent(Intent.ACTION_VIEW, docUri);
String packageName = this.getApplicationContext().getPackageName();
ComponentName componentName = new ComponentName(packageName, LibreOfficeMainActivity.class.getName());
intent.setComponent(componentName);
@@ -1062,161 +434,22 @@ 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:
- createNewFileInputDialog(getString(R.string.default_document_name) + FileUtilities.DEFAULT_WRITER_EXTENSION, NEW_WRITER_STRING_KEY);
- break;
- case R.id.newImpressFAB:
- createNewFileInputDialog(getString(R.string.default_document_name) + FileUtilities.DEFAULT_IMPRESS_EXTENSION, NEW_IMPRESS_STRING_KEY);
- break;
- case R.id.newCalcFAB:
- createNewFileInputDialog(getString(R.string.default_document_name) + FileUtilities.DEFAULT_SPREADSHEET_EXTENSION, NEW_CALC_STRING_KEY);
- break;
- case R.id.newDrawFAB:
- createNewFileInputDialog(getString(R.string.default_document_name) + FileUtilities.DEFAULT_DRAWING_EXTENSION, NEW_DRAW_STRING_KEY);
- break;
- }
- }
-
-
- class ExplorerItemAdapter extends RecyclerView.Adapter<ExplorerItemAdapter.ViewHolder> {
-
- private Activity mActivity;
- private List<IFile> filePaths;
- private final long KB = 1024;
- private final long MB = 1048576;
-
- ExplorerItemAdapter(Activity activity, List<IFile> filePaths) {
- this.mActivity = activity;
- this.filePaths = filePaths;
- }
-
- @Override
- public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
- View item = LayoutInflater.from(parent.getContext())
- .inflate(isViewModeList() ? R.layout.file_list_item : R.layout.file_explorer_grid_item, parent, false);
- return new ViewHolder(item);
- }
-
- @Override
- public void onBindViewHolder(final ViewHolder holder, final int position) {
- final IFile file = filePaths.get(position);
-
- holder.itemView.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View view) {
- open(holder.getAdapterPosition());
- }
- });
- holder.itemView.setOnLongClickListener(new OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View view) {
- //to be picked out by floating context menu (workaround-ish)
- currentlySelectedFile = holder.getAdapterPosition();
- //must return false so the click is not consumed
- return false;
- }
- });
-
- holder.filenameView.setText(file.getName());
- switch (FileUtilities.getType(file.getName())) {
- case FileUtilities.DOC:
- holder.iconView.setImageResource(R.drawable.writer);
- break;
- case FileUtilities.CALC:
- holder.iconView.setImageResource(R.drawable.calc);
- break;
- case FileUtilities.DRAWING:
- holder.iconView.setImageResource(R.drawable.draw);
- break;
- case FileUtilities.IMPRESS:
- holder.iconView.setImageResource(R.drawable.impress);
- break;
- }
-
- if (file.isDirectory()) {
- //Eventually have thumbnails of each sub file on a black circle
- //For now just a folder icon
- holder.iconView.setImageResource(R.drawable.ic_folder_black_24dp);
- holder.iconView.setColorFilter(ContextCompat.getColor(mActivity, R.color.text_color_secondary));
- }
-
- // Date and Size field only exist when we are displaying items in a list.
- if(isViewModeList()) {
- if (!file.isDirectory()) {
- String size;
- long length = filePaths.get(position).getSize();
- if (length < KB) {
- size = Long.toString(length) + "B";
- } else if (length < MB) {
- size = Long.toString(length / KB) + "KB";
- } else {
- size = Long.toString(length / MB) + "MB";
- }
- holder.fileSizeView.setText(size);
- }
- SimpleDateFormat df = new SimpleDateFormat("dd MMM yyyy hh:ss");
- Date date = file.getLastModified();
- //TODO format date
- holder.fileDateView.setText(df.format(date));
- }
- }
-
- @Override
- public int getItemCount() {
- return filePaths.size();
- }
-
- class ViewHolder extends RecyclerView.ViewHolder {
-
- View itemView;
- TextView filenameView, fileSizeView, fileDateView;
- ImageView iconView;
-
- ViewHolder(View itemView) {
- super(itemView);
- this.itemView = itemView;
- filenameView = itemView.findViewById(R.id.file_item_name);
- iconView = itemView.findViewById(R.id.file_item_icon);
- // Check if view mode is List, only then initialise Size and Date field
- if (isViewModeList()) {
- fileSizeView = itemView.findViewById(R.id.file_item_size);
- fileDateView = itemView.findViewById(R.id.file_item_date);
- }
- }
- }
- }
-
- private void setEditFABVisibility(final int visibility){
- LOKitShell.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- editFAB.setVisibility(visibility);
- }
- });
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- switch(requestCode){
- case PERMISSION_WRITE_EXTERNAL_STORAGE:
- if(permissions.length>0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){
- switchToDocumentProvider(documentProviderFactory.getDefaultProvider());
- setEditFABVisibility(View.VISIBLE);
- } else {
- setEditFABVisibility(View.INVISIBLE);
- }
- break;
- default:
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ if (id == R.id.editFAB) {
+ if (isFabMenuOpen) {
+ collapseFabMenu();
+ } else {
+ expandFabMenu();
+ }
+ } else if (id == R.id.open_file_button) {
+ showSystemFilePickerAndOpenFile();
+ } else if (id == R.id.newWriterFAB) {
+ loadNewDocument(DocumentType.WRITER);
+ } else if (id == R.id.newImpressFAB) {
+ loadNewDocument(DocumentType.IMPRESS);
+ } else if (id == R.id.newCalcFAB) {
+ loadNewDocument(DocumentType.CALC);
+ } else if (id == R.id.newDrawFAB) {
+ loadNewDocument(DocumentType.DRAW);
}
}
}
diff --git a/android/source/src/java/org/libreoffice/ui/PageView.java b/android/source/src/java/org/libreoffice/ui/PageView.java
index 1d32a7de7e80..4c3f69562250 100644
--- a/android/source/src/java/org/libreoffice/ui/PageView.java
+++ b/android/source/src/java/org/libreoffice/ui/PageView.java
@@ -17,29 +17,29 @@ import android.view.View;
public class PageView extends View{
private Bitmap bmp;
private Paint mPaintBlack;
- private String tag = "PageView";
+ private static final String LOGTAG = "PageView";
public PageView(Context context ) {
super(context);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.dummy_page);
- intialise();
+ initialise();
}
public PageView(Context context, AttributeSet attrs) {
super(context, attrs);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.dummy_page);
- Log.d( tag , bmp.toString());
- intialise();
+ Log.d(LOGTAG, bmp.toString());
+ initialise();
}
public PageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
bmp = BitmapFactory.decodeResource(getResources(), R.drawable.dummy_page);//load a "page"
- intialise();
+ initialise();
}
- private void intialise(){
+ private void initialise(){
mPaintBlack = new Paint();
mPaintBlack.setARGB(255, 0, 0, 0);
- Log.d(tag, " Doing some set-up");
+ Log.d(LOGTAG, " Doing some set-up");
}
public void setBitmap(Bitmap bmp){
@@ -49,8 +49,8 @@ public class PageView extends View{
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- Log.d(tag, "Draw");
- Log.d(tag, Integer.toString(bmp.getHeight()));
+ Log.d(LOGTAG, "Draw");
+ Log.d(LOGTAG, Integer.toString(bmp.getHeight()));
if( bmp != null ){
int horizontalMargin = (int) (canvas.getWidth()*0.1);
//int verticalMargin = (int) (canvas.getHeight()*0.1);
diff --git a/android/source/src/java/org/libreoffice/ui/RecentFile.java b/android/source/src/java/org/libreoffice/ui/RecentFile.java
new file mode 100644
index 000000000000..fdcc688aa140
--- /dev/null
+++ b/android/source/src/java/org/libreoffice/ui/RecentFile.java
@@ -0,0 +1,25 @@
+package org.libreoffice.ui;
+
+import android.net.Uri;
+
+/**
+ * An entry for a recently used file in the RecentFilesAdapter.
+ */
+public class RecentFile {
+
+ private final Uri uri;
+ private final String displayName;
+
+ public RecentFile(Uri docUri, String name) {
+ uri = docUri;
+ displayName = name;
+ }
+
+ public Uri getUri() {
+ return uri;
+ }
+
+ public String getDisplayName() {
+ return displayName;
+ }
+}
diff --git a/android/source/src/java/org/libreoffice/ui/RecentFilesAdapter.java b/android/source/src/java/org/libreoffice/ui/RecentFilesAdapter.java
index fc16d06a48d7..ef00b9fb6cfd 100644
--- a/android/source/src/java/org/libreoffice/ui/RecentFilesAdapter.java
+++ b/android/source/src/java/org/libreoffice/ui/RecentFilesAdapter.java
@@ -9,8 +9,8 @@
package org.libreoffice.ui;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.RecyclerView;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -18,16 +18,15 @@ import android.widget.ImageView;
import android.widget.TextView;
import org.libreoffice.R;
-import org.libreoffice.storage.IFile;
import java.util.List;
class RecentFilesAdapter extends RecyclerView.Adapter<RecentFilesAdapter.ViewHolder> {
- private LibreOfficeUIActivity mActivity;
- private List<IFile> recentFiles;
+ private final LibreOfficeUIActivity mActivity;
+ private final List<RecentFile> recentFiles;
- RecentFilesAdapter(LibreOfficeUIActivity activity, List<IFile> recentFiles) {
+ RecentFilesAdapter(LibreOfficeUIActivity activity, List<RecentFile> recentFiles) {
this.mActivity = activity;
this.recentFiles = recentFiles;
}
@@ -41,17 +40,16 @@ class RecentFilesAdapter extends RecyclerView.Adapter<RecentFilesAdapter.ViewHol
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
- final IFile iFile = recentFiles.get(position);
+ final RecentFile entry = recentFiles.get(position);
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
- mActivity.open(iFile);
+ mActivity.openDocument(entry.getUri());
}
});
- String filename = iFile.getName();
-
+ final String filename = entry.getDisplayName();
holder.textView.setText(filename);
int compoundDrawableInt = 0;
diff --git a/android/source/src/java/org/mozilla/gecko/ZoomConstraints.java b/android/source/src/java/org/mozilla/gecko/ZoomConstraints.java
index f1672ba3dd76..dbe278827279 100644
--- a/android/source/src/java/org/mozilla/gecko/ZoomConstraints.java
+++ b/android/source/src/java/org/mozilla/gecko/ZoomConstraints.java
@@ -6,28 +6,16 @@
package org.mozilla.gecko;
public final class ZoomConstraints {
- private final boolean mAllowZoom;
- private final boolean mAllowDoubleTapZoom;
private final float mDefaultZoom;
private final float mMinZoom;
private final float mMaxZoom;
- public ZoomConstraints(boolean allowZoom, float defaultZoom, float minZoom, float maxZoom) {
- mAllowZoom = allowZoom;
- mAllowDoubleTapZoom = allowZoom;
+ public ZoomConstraints(float defaultZoom, float minZoom, float maxZoom) {
mDefaultZoom = defaultZoom;
mMinZoom = minZoom;
mMaxZoom = maxZoom;
}
- public final boolean getAllowZoom() {
- return mAllowZoom;
- }
-
- public final boolean getAllowDoubleTapZoom() {
- return mAllowDoubleTapZoom;
- }
-
public final float getDefaultZoom() {
return mDefaultZoom;
}
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/GLController.java b/android/source/src/java/org/mozilla/gecko/gfx/GLController.java
index e296f4760f68..6a43dd6a87db 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/GLController.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/GLController.java
@@ -20,7 +20,6 @@ public class GLController {
private LayerView mView;
private int mGLVersion;
- private boolean mSurfaceValid;
private int mWidth, mHeight;
private EGL10 mEGL;
@@ -29,8 +28,6 @@ public class GLController {
private EGLContext mEGLContext;
private EGLSurface mEGLSurface;
- private GL mGL;
-
private static final int LOCAL_EGL_OPENGL_ES2_BIT = 4;
private static final int[] CONFIG_SPEC = {
@@ -45,7 +42,6 @@ public class GLController {
public GLController(LayerView view) {
mView = view;
mGLVersion = 2;
- mSurfaceValid = false;
}
public void setGLVersion(int version) {
@@ -84,12 +80,11 @@ public class GLController {
getEGLError());
}
- mGL = null;
mEGLContext = null;
}
}
- public GL getGL() { return mEGLContext.getGL(); }
+ public GL10 getGL() { return (GL10) mEGLContext.getGL(); }
public EGLDisplay getEGLDisplay() { return mEGLDisplay; }
public EGLConfig getEGLConfig() { return mEGLConfig; }
public EGLContext getEGLContext() { return mEGLContext; }
@@ -104,38 +99,6 @@ public class GLController {
return mEGL.eglSwapBuffers(mEGLDisplay, mEGLSurface);
}
- public boolean checkForLostContext() {
- if (mEGL.eglGetError() != EGL11.EGL_CONTEXT_LOST) {
- return false;
- }
-
- mEGLDisplay = null;
- mEGLConfig = null;
- mEGLContext = null;
- mEGLSurface = null;
- mGL = null;
- return true;
- }
-
- // This function is invoked by JNI
- public synchronized void resumeCompositorIfValid() {
- if (mSurfaceValid) {
- mView.getListener().compositionResumeRequested(mWidth, mHeight);
- }
- }
-
- // Wait until we are allowed to use EGL functions on the Surface backing
- // this window. This function is invoked by JNI
- public synchronized void waitForValidSurface() {
- while (!mSurfaceValid) {
- try {
- wait();
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- }
- }
-
public synchronized int getWidth() {
return mWidth;
}
@@ -145,14 +108,12 @@ public class GLController {
}
synchronized void surfaceDestroyed() {
- mSurfaceValid = false;
notifyAll();
}
synchronized void surfaceChanged(int newWidth, int newHeight) {
mWidth = newWidth;
mHeight = newHeight;
- mSurfaceValid = true;
notifyAll();
}
@@ -183,11 +144,10 @@ public class GLController {
getEGLError());
}
- mGL = mEGLContext.getGL();
-
if (mView.getRenderer() != null) {
- mView.getRenderer().onSurfaceCreated((GL10)mGL, mEGLConfig);
- mView.getRenderer().onSurfaceChanged((GL10)mGL, mWidth, mHeight);
+ GL10 gl = (GL10) mEGLContext.getGL();
+ mView.getRenderer().onSurfaceCreated(gl, mEGLConfig);
+ mView.getRenderer().onSurfaceChanged(gl, mWidth, mHeight);
}
}
@@ -216,7 +176,8 @@ public class GLController {
}
}
- throw new GLControllerException("No suitable EGL configuration found");
+ // if there's no 565 RGB configuration, select another one that fulfils the specification
+ return configs[0];
}
private void createEGLSurface() {
@@ -232,32 +193,11 @@ public class GLController {
"surface! " + getEGLError());
}
- mGL = mEGLContext.getGL();
-
if (mView.getRenderer() != null) {
- mView.getRenderer().onSurfaceCreated((GL10)mGL, mEGLConfig);
- mView.getRenderer().onSurfaceChanged((GL10)mGL, mView.getWidth(), mView.getHeight());
- }
- }
-
- /**
- * Provides an EGLSurface without assuming ownership of this surface.
- * This class does not keep a reference to the provided EGL surface; the
- * caller assumes ownership of the surface once it is returned.
- */
- private EGLSurface provideEGLSurface() {
- if (mEGL == null) {
- initEGL();
+ GL10 gl = (GL10) mEGLContext.getGL();
+ mView.getRenderer().onSurfaceCreated(gl, mEGLConfig);
+ mView.getRenderer().onSurfaceChanged(gl, mView.getWidth(), mView.getHeight());
}
-
- Object window = mView.getNativeWindow();
- EGLSurface surface = mEGL.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, window, null);
- if (surface == null || surface == EGL10.EGL_NO_SURFACE) {
- throw new GLControllerException("EGL window surface could not be created! " +
- getEGLError());
- }
-
- return surface;
}
private String getEGLError() {
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java b/android/source/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
index 681fb6fd6019..72a96f0bb00f 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/GeckoLayerClient.java
@@ -80,7 +80,7 @@ public class GeckoLayerClient implements PanZoomTarget {
mView.setLayerRenderer(mLayerRenderer);
- sendResizeEventIfNecessary();
+ sendResizeEventIfNecessary(false);
mView.requestRender();
}
@@ -124,21 +124,23 @@ public class GeckoLayerClient implements PanZoomTarget {
* to the layer client. That way, the layer client won't be tempted to call this, which might
* result in an infinite loop.
*/
- void setViewportSize(FloatSize size) {
+ void setViewportSize(FloatSize size, boolean forceResizeEvent) {
mViewportMetrics = mViewportMetrics.setViewportSize(size.width, size.height);
- sendResizeEventIfNecessary();
+ sendResizeEventIfNecessary(forceResizeEvent);
}
PanZoomController getPanZoomController() {
return mPanZoomController;
}
- /* Informs Gecko that the screen size has changed. */
- private void sendResizeEventIfNecessary() {
+ /* Informs Gecko that the screen size has changed.
+ * @param force: If true, a resize event will always be sent, otherwise
+ * it is only sent if size has changed. */
+ private void sendResizeEventIfNecessary(boolean force) {
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels);
- if (mScreenSize.equals(newScreenSize)) {
+ if (!force && mScreenSize.equals(newScreenSize)) {
return;
}
@@ -233,7 +235,7 @@ public class GeckoLayerClient implements PanZoomTarget {
}
private void geometryChanged() {
- sendResizeEventIfNecessary();
+ sendResizeEventIfNecessary(false);
if (getRedrawHint()) {
adjustViewport(null);
}
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java b/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
index db2fcc03c5b3..b20d602a21cb 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/JavaPanZoomController.java
@@ -21,6 +21,7 @@ import org.mozilla.gecko.util.FloatUtils;
import java.util.Timer;
import java.util.TimerTask;
+import java.lang.StrictMath;
/*
* Handles the kinetic scrolling and zooming physics for a layer controller.
@@ -143,16 +144,9 @@ class JavaPanZoomController
/** This function MUST be called on the UI thread */
public boolean onMotionEvent(MotionEvent event) {
- if (Build.VERSION.SDK_INT <= 11) {
- return false;
- }
-
- switch (event.getSource() & InputDevice.SOURCE_CLASS_MASK) {
- case InputDevice.SOURCE_CLASS_POINTER:
- switch (event.getAction() & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_SCROLL: return handlePointerScroll(event);
- }
- break;
+ if ((event.getSource() & InputDevice.SOURCE_CLASS_MASK) == InputDevice.SOURCE_CLASS_POINTER
+ && (event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_SCROLL) {
+ return handlePointerScroll(event);
}
return false;
}
@@ -433,7 +427,7 @@ class JavaPanZoomController
private float panDistance(MotionEvent move) {
float dx = mX.panDistance(move.getX(0));
float dy = mY.panDistance(move.getY(0));
- return (float) Math.sqrt(dx * dx + dy * dy);
+ return (float) Math.hypot(dx , dy);
}
private void track(float x, float y, long time) {
@@ -550,7 +544,7 @@ class JavaPanZoomController
private float getVelocity() {
float xvel = mX.getRealVelocity();
float yvel = mY.getRealVelocity();
- return (float) Math.sqrt(xvel * xvel + yvel * yvel);
+ return (float) StrictMath.hypot(xvel, yvel);
}
public PointF getVelocityVector() {
@@ -751,11 +745,6 @@ class JavaPanZoomController
if (constraints.getMaxZoom() > 0)
maxZoomFactor = constraints.getMaxZoom();
- if (!constraints.getAllowZoom()) {
- // If allowZoom is false, clamp to the default zoom level.
- maxZoomFactor = minZoomFactor = constraints.getDefaultZoom();
- }
-
maxZoomFactor = Math.max(maxZoomFactor, minZoomFactor);
if (zoomFactor < minZoomFactor) {
@@ -828,7 +817,7 @@ class JavaPanZoomController
if (mState == PanZoomState.ANIMATED_ZOOM)
return false;
- if (null == mTarget.getZoomConstraints() || !mTarget.getZoomConstraints().getAllowZoom())
+ if (null == mTarget.getZoomConstraints())
return false;
setState(PanZoomState.PINCHING);
@@ -934,10 +923,7 @@ class JavaPanZoomController
@Override
public boolean onDown(MotionEvent motionEvent) {
- if (mTarget.getZoomConstraints() != null)
- mWaitForDoubleTap = mTarget.getZoomConstraints().getAllowDoubleTapZoom();
- else
- mWaitForDoubleTap = false;
+ mWaitForDoubleTap = mTarget.getZoomConstraints() != null;
return false;
}
@@ -991,14 +977,14 @@ class JavaPanZoomController
@Override
public boolean onDoubleTap(MotionEvent motionEvent) {
- if (null == mTarget.getZoomConstraints() || !mTarget.getZoomConstraints().getAllowDoubleTapZoom()) {
+ if (null == mTarget.getZoomConstraints()) {
return true;
}
// Double tap zooms in or out depending on the current zoom factor
PointF pointOfTap = getMotionInDocumentCoordinates(motionEvent);
ImmutableViewportMetrics metrics = getMetrics();
float newZoom = metrics.getZoomFactor() >=
- DOUBLE_TAP_THRESHOLD ? mTarget.getZoomConstraints().getMinZoom() : DOUBLE_TAP_THRESHOLD;
+ DOUBLE_TAP_THRESHOLD ? mTarget.getZoomConstraints().getDefaultZoom() : DOUBLE_TAP_THRESHOLD;
// calculate new top_left point from the point of tap
float ratio = newZoom/metrics.getZoomFactor();
float newLeft = pointOfTap.x - 1/ratio * (pointOfTap.x - metrics.getOrigin().x / metrics.getZoomFactor());
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/LayerRenderer.java b/android/source/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
index b1aea3616d6c..6ea7dd0edc10 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/LayerRenderer.java
@@ -48,13 +48,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
private FloatBuffer mCoordBuffer;
private RenderContext mLastPageContext;
private int mMaxTextureSize;
- private int mBackgroundColor;
private CopyOnWriteArrayList<Layer> mExtraLayers = new CopyOnWriteArrayList<Layer>();
- /* Used by robocop for testing purposes */
- private IntBuffer mPixelBuffer;
-
// Used by GLES 2.0
private int mProgram;
private int mPositionHandle;
@@ -146,6 +142,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
mVertScrollLayer.destroy();
}
+ @Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
createDefaultProgram();
activateDefaultProgram();
@@ -184,9 +181,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
GLES20.glEnableVertexAttribArray(mTextureHandle);
GLES20.glUniform1i(mSampleHandle, 0);
-
- // TODO: Move these calls into a separate deactivate() call that is called after the
- // underlay and overlay are rendered.
}
// Deactivates the shader program. This must be done to avoid crashes after returning to the
@@ -220,8 +214,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
/**
* Called whenever a new frame is about to be drawn.
*/
+ @Override
public void onDrawFrame(GL10 gl) {
- Frame frame = createFrame(mView.getLayerClient().getViewportMetrics());
+ Frame frame = new Frame(mView.getLayerClient().getViewportMetrics());
synchronized (mView.getLayerClient()) {
frame.beginDrawing();
frame.drawBackground();
@@ -249,6 +244,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
mCoordBuffer);
}
+ @Override
public void onSurfaceChanged(GL10 gl, final int width, final int height) {
GLES20.glViewport(0, 0, width, height);
}
@@ -264,10 +260,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
return shader;
}
- public Frame createFrame(ImmutableViewportMetrics metrics) {
- return new Frame(metrics);
- }
-
class FadeRunnable implements Runnable {
private boolean mStarted;
private long mRunAt;
@@ -305,8 +297,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
}
public class Frame {
- // The timestamp recording the start of this frame.
- private long mFrameStartTime;
// A fixed snapshot of the viewport metrics that this frame is using to render content.
private ImmutableViewportMetrics mFrameMetrics;
// A rendering context for page-positioned layers, and one for screen-positioned layers.
@@ -348,10 +338,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
return pageRect;
}
- /** This function is invoked via JNI; be careful when modifying signature. */
public void beginDrawing() {
- mFrameStartTime = SystemClock.uptimeMillis();
-
TextureReaper.get().reap();
TextureGenerator.get().fill();
@@ -386,58 +373,18 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
mUpdated &= layer.update(mPageContext); // called on compositor thread
}
- /** Retrieves the bounds for the layer, rounded in such a way that it
- * can be used as a mask for something that will render underneath it.
- * This will round the bounds inwards, but stretch the mask towards any
- * near page edge, where near is considered to be 'within 2 pixels'.
- * Returns null if the given layer is null.
- */
- private Rect getMaskForLayer(Layer layer) {
- if (layer == null) {
- return null;
- }
-
- RectF bounds = RectUtils.contract(layer.getBounds(mPageContext), 1.0f, 1.0f);
- Rect mask = RectUtils.roundIn(bounds);
-
- // If the mask is within two pixels of any page edge, stretch it over
- // that edge. This is to avoid drawing thin slivers when masking
- // layers.
- if (mask.top <= 2) {
- mask.top = -1;
- }
- if (mask.left <= 2) {
- mask.left = -1;
- }
-
- // Because we're drawing relative to the page-rect, we only need to
- // take into account its width and height (and not its origin)
- int pageRight = mPageRect.width();
- int pageBottom = mPageRect.height();
-
- if (mask.right >= pageRight - 2) {
- mask.right = pageRight + 1;
- }
- if (mask.bottom >= pageBottom - 2) {
- mask.bottom = pageBottom + 1;
- }
-
- return mask;
- }
-
- /** This function is invoked via JNI; be careful when modifying signature. */
public void drawBackground() {
GLES20.glDisable(GLES20.GL_SCISSOR_TEST);
/* Update background color. */
- mBackgroundColor = Color.WHITE;
+ final int backgroundColor = Color.WHITE;
/* Clear to the page background colour. The bits set here need to
* match up with those used in gfx/layers/opengl/LayerManagerOGL.cpp.
*/
- GLES20.glClearColor(((mBackgroundColor>>16)&0xFF) / 255.0f,
- ((mBackgroundColor>>8)&0xFF) / 255.0f,
- (mBackgroundColor&0xFF) / 255.0f,
+ GLES20.glClearColor(((backgroundColor >> 16) & 0xFF) / 255.0f,
+ ((backgroundColor >> 8) & 0xFF) / 255.0f,
+ (backgroundColor & 0xFF) / 255.0f,
0.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT |
GLES20.GL_DEPTH_BUFFER_BIT);
@@ -474,7 +421,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
rootLayer.draw(mPageContext);
}
- /** This function is invoked via JNI; be careful when modifying signature. */
public void drawForeground() {
/* Draw any extra layers that were added (likely plugins) */
if (mExtraLayers.size() > 0) {
@@ -498,23 +444,10 @@ public class LayerRenderer implements GLSurfaceView.Renderer {
mHorizScrollLayer.draw(mPageContext);
}
- /** This function is invoked via JNI; be careful when modifying signature. */
public void endDrawing() {
// If a layer update requires further work, schedule another redraw
if (!mUpdated)
mView.requestRender();
-
- /* Used by robocop for testing purposes */
- IntBuffer pixelBuffer = mPixelBuffer;
- if (mUpdated && pixelBuffer != null) {
- synchronized (pixelBuffer) {
- pixelBuffer.position(0);
- GLES20.glReadPixels(0, 0, (int)mScreenContext.viewport.width(),
- (int)mScreenContext.viewport.height(), GLES20.GL_RGBA,
- GLES20.GL_UNSIGNED_BYTE, pixelBuffer);
- pixelBuffer.notify();
- }
- }
}
}
}
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java b/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java
index 05f2118114c8..29049f92912d 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/LayerView.java
@@ -11,14 +11,12 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.PixelFormat;
-import android.graphics.SurfaceTexture;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
-import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
@@ -37,8 +35,6 @@ import org.mozilla.gecko.OnSlideSwipeListener;
*
* This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a
* mediator between the LayerRenderer and the LayerController.
- *
- * Note that LayerView is accessed by Robocop via reflection.
*/
public class LayerView extends FrameLayout {
private static String LOGTAG = LayerView.class.getName();
@@ -49,59 +45,22 @@ public class LayerView extends FrameLayout {
private InputConnectionHandler mInputConnectionHandler;
private LayerRenderer mRenderer;
- /* Must be a PAINT_xxx constant */
- private int mPaintState = PAINT_NONE;
- private boolean mFullScreen = false;
-
private SurfaceView mSurfaceView;
- private TextureView mTextureView;
private Listener mListener;
private OnInterceptTouchListener mTouchIntercepter;
- //TODO static because of registerCxxCompositor() function, should be fixed in the future
- private static LibreOfficeMainActivity mContext;
-
- /* Flags used to determine when to show the painted surface. The integer
- * order must correspond to the order in which these states occur. */
- public static final int PAINT_NONE = 0;
- public static final int PAINT_BEFORE_FIRST = 1;
- public static final int PAINT_AFTER_FIRST = 2;
-
- boolean shouldUseTextureView() {
- // we can only use TextureView on ICS or higher
- /*if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
- Log.i(LOGTAG, "Not using TextureView: not on ICS+");
- return false;
- }
-
- try {
- // and then we can only use it if we have a hardware accelerated window
- Method m = View.class.getMethod("isHardwareAccelerated", new Class[0]);
- return (Boolean) m.invoke(this);
- } catch (Exception e) {
- Log.i(LOGTAG, "Not using TextureView: caught exception checking for hw accel: " + e.toString());
- return false;
- }*/
- return false;
- }
+ private LibreOfficeMainActivity mContext;
public LayerView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = (LibreOfficeMainActivity) context;
- if (shouldUseTextureView()) {
- mTextureView = new TextureView(context);
- mTextureView.setSurfaceTextureListener(new SurfaceTextureListener());
+ mSurfaceView = new SurfaceView(context);
+ addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- addView(mTextureView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
- } else {
- mSurfaceView = new SurfaceView(context);
- addView(mSurfaceView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
-
- SurfaceHolder holder = mSurfaceView.getHolder();
- holder.addCallback(new SurfaceListener());
+ SurfaceHolder holder = mSurfaceView.getHolder();
+ holder.addCallback(new SurfaceListener());
holder.setFormat(PixelFormat.RGB_565);
- }
mGLController = new GLController(this);
}
@@ -183,10 +142,6 @@ public class LayerView extends FrameLayout {
public GeckoLayerClient getLayerClient() { return mLayerClient; }
public PanZoomController getPanZoomController() { return mPanZoomController; }
- public void setViewportSize(IntSize size) {
- mLayerClient.setViewportSize(new FloatSize(size));
- }
-
public ImmutableViewportMetrics getViewportMetrics() {
return mLayerClient.getViewportMetrics();
}
@@ -223,13 +178,6 @@ public class LayerView extends FrameLayout {
return mInputConnectionHandler != null && mInputConnectionHandler.onKeyUp(keyCode, event);
}
- public boolean isIMEEnabled() {
- /*if (mInputConnectionHandler != null) {
- return mInputConnectionHandler.isIMEEnabled();
- }*/
- return false;
- }
-
public void requestRender() {
if (mListener != null) {
mListener.renderRequested();
@@ -256,19 +204,6 @@ public class LayerView extends FrameLayout {
return mRenderer;
}
- /* paintState must be a PAINT_xxx constant. The state will only be changed
- * if paintState represents a state that occurs after the current state. */
- public void setPaintState(int paintState) {
- if (paintState > mPaintState) {
- Log.d(LOGTAG, "LayerView paint state set to " + paintState);
- mPaintState = paintState;
- }
- }
-
- public int getPaintState() {
- return mPaintState;
- }
-
public LayerRenderer getRenderer() {
return mRenderer;
}
@@ -306,7 +241,7 @@ public class LayerView extends FrameLayout {
private void onSizeChanged(int width, int height) {
mGLController.surfaceChanged(width, height);
- mLayerClient.setViewportSize(new FloatSize(width, height));
+ mLayerClient.setViewportSize(new FloatSize(width, height), false);
if (mListener != null) {
mListener.surfaceChanged(width, height);
@@ -324,29 +259,13 @@ public class LayerView extends FrameLayout {
}
public Object getNativeWindow() {
- if (mSurfaceView != null)
- return mSurfaceView.getHolder();
-
- return mTextureView.getSurfaceTexture();
- }
-
- /** This function is invoked by Gecko (compositor thread) via JNI; be careful when modifying signature. */
- public static GLController registerCxxCompositor() {
- try {
- LayerView layerView = mContext.getLayerClient().getView();
- layerView.mListener.compositorCreated();
- return layerView.getGLController();
- } catch (Exception e) {
- Log.e(LOGTAG, "Error registering compositor!", e);
- return null;
- }
+ return mSurfaceView.getHolder();
}
public interface Listener {
void compositorCreated();
void renderRequested();
void compositionPauseRequested();
- void compositionResumeRequested(int width, int height);
void surfaceChanged(int width, int height);
}
@@ -371,30 +290,7 @@ public class LayerView extends FrameLayout {
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (changed) {
- setViewportSize(new IntSize(right - left, bottom - top));
- }
- }
-
- private class SurfaceTextureListener implements TextureView.SurfaceTextureListener {
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- // We don't do this for surfaceCreated above because it is always followed by a surfaceChanged,
- // but that is not the case here.
- if (mRenderControllerThread != null) {
- mRenderControllerThread.surfaceCreated();
- }
- onSizeChanged(width, height);
- }
-
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- onDestroyed();
- return true; // allow Android to call release() on the SurfaceTexture, we are done drawing to it
- }
-
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- onSizeChanged(width, height);
- }
-
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ mLayerClient.setViewportSize(new FloatSize(right - left, bottom - top), true);
}
}
@@ -438,12 +334,4 @@ public class LayerView extends FrameLayout {
super(e);
}
}
-
- public void setFullScreen(boolean fullScreen) {
- mFullScreen = fullScreen;
- }
-
- public boolean isFullScreen() {
- return mFullScreen;
- }
}
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/PointUtils.java b/android/source/src/java/org/mozilla/gecko/gfx/PointUtils.java
index 4eb07a31f147..4eff380527d2 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/PointUtils.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/PointUtils.java
@@ -11,6 +11,8 @@ import android.graphics.PointF;
import org.json.JSONException;
import org.json.JSONObject;
+import java.lang.StrictMath;
+
public final class PointUtils {
public static PointF add(PointF one, PointF two) {
return new PointF(one.x + two.x, one.y + two.y);
@@ -30,7 +32,7 @@ public final class PointUtils {
/* Computes the magnitude of the given vector. */
public static float distance(PointF point) {
- return (float)Math.sqrt(point.x * point.x + point.y * point.y);
+ return (float)StrictMath.hypot(point.x, point.y);
}
/** Computes the scalar distance between two points. */
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java b/android/source/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java
index 06f82f158366..5c74d56a004b 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/RenderControllerThread.java
@@ -82,11 +82,6 @@ public class RenderControllerThread extends Thread implements LayerView.Listener
}
@Override
- public void compositionResumeRequested(int width, int height) {
-
- }
-
- @Override
public void surfaceChanged(int width, int height) {
this.width = width;
this.height = height;
@@ -115,7 +110,7 @@ public class RenderControllerThread extends Thread implements LayerView.Listener
}
GLSurfaceView.Renderer renderer = getRenderer();
if (renderer != null) {
- renderer.onDrawFrame((GL10) controller.getGL());
+ renderer.onDrawFrame(controller.getGL());
}
controller.swapBuffers();
}
@@ -123,7 +118,7 @@ public class RenderControllerThread extends Thread implements LayerView.Listener
private void doSizeChanged() {
GLSurfaceView.Renderer renderer = getRenderer();
if (renderer != null) {
- renderer.onSurfaceChanged((GL10) controller.getGL(), width, height);
+ renderer.onSurfaceChanged(controller.getGL(), width, height);
}
}
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java b/android/source/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java
index 1d901a02a14b..e89015b5ed8c 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/SimpleScaleGestureDetector.java
@@ -34,7 +34,7 @@ import java.util.Stack;
* - It doesn't take pressure into account, which results in smoother scaling.
*/
public class SimpleScaleGestureDetector {
- private static final String LOGTAG = "GeckoSimpleScaleGestureDetector";
+ private static final String LOGTAG = "ScaleGestureDetector";
private SimpleScaleGestureListener mListener;
private long mLastEventTime;
diff --git a/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java b/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java
index 42750df62838..bdad37195d90 100644
--- a/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java
+++ b/android/source/src/java/org/mozilla/gecko/gfx/SubTile.java
@@ -86,26 +86,24 @@ public class SubTile extends Layer {
protected void finalize() throws Throwable {
try {
destroyImage();
- cleanTexture(false);
+ cleanTexture();
} finally {
super.finalize();
}
}
- private void cleanTexture(boolean immediately) {
+ private void cleanTexture() {
if (mTextureIDs != null) {
TextureReaper.get().add(mTextureIDs);
mTextureIDs = null;
- if (immediately) {
- TextureReaper.get().reap();
- }
+ TextureReaper.get().reap();
}
}
public void destroy() {
try {
destroyImage();
- cleanTexture(false);
+ cleanTexture();
} catch (Exception ex) {
Log.e(LOGTAG, "Error clearing buffers: ", ex);
}
@@ -140,7 +138,7 @@ public class SubTile extends Layer {
if (!textureSize.equals(mSize)) {
mSize = textureSize;
- cleanTexture(true);
+ cleanTexture();
}
}
@@ -253,4 +251,4 @@ public class SubTile extends Layer {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
}
-} \ No newline at end of file
+}