summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--android/source/AndroidManifest.xml11
-rw-r--r--android/source/res/layout/toolbar_bottom.xml10
-rw-r--r--android/source/res/values/strings.xml11
-rw-r--r--android/source/res/xml/file_paths.xml4
-rw-r--r--android/source/src/java/org/libreoffice/FormattingController.java208
-rw-r--r--android/source/src/java/org/libreoffice/LOEvent.java1
-rw-r--r--android/source/src/java/org/libreoffice/LOKitThread.java4
-rw-r--r--android/source/src/java/org/libreoffice/LOKitTileProvider.java8
-rw-r--r--android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java7
9 files changed, 262 insertions, 2 deletions
diff --git a/android/source/AndroidManifest.xml b/android/source/AndroidManifest.xml
index c2a3656f8e92..fb51eb4b0e43 100644
--- a/android/source/AndroidManifest.xml
+++ b/android/source/AndroidManifest.xml
@@ -7,6 +7,7 @@
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<!-- App wants to know if device supports USB host capability(not mandatory) -->
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
+ <uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />
@@ -133,6 +134,16 @@
android:value=".LibreOfficeMainActivity" />
</activity>
+ <provider
+ android:name="android.support.v4.content.FileProvider"
+ android:authorities="${applicationId}.fileprovider"
+ android:exported="false"
+ android:grantUriPermissions="true">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/file_paths" />
+ </provider>
+
</application>
</manifest>
diff --git a/android/source/res/layout/toolbar_bottom.xml b/android/source/res/layout/toolbar_bottom.xml
index 1b4730d89adf..a537a52d32b9 100644
--- a/android/source/res/layout/toolbar_bottom.xml
+++ b/android/source/res/layout/toolbar_bottom.xml
@@ -328,6 +328,16 @@
android:paddingBottom="12dp"
android:paddingTop="12dp"
app:srcCompat="@drawable/ic_rect" />
+
+ <ImageButton
+ android:id="@+id/button_insert_picture"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.25"
+ android:background="@drawable/image_button_background"
+ android:paddingBottom="12dp"
+ android:paddingTop="12dp"
+ app:srcCompat="@drawable/ic_folder_black_24dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
diff --git a/android/source/res/values/strings.xml b/android/source/res/values/strings.xml
index e84c496db3c5..3f6955cd7a7b 100644
--- a/android/source/res/values/strings.xml
+++ b/android/source/res/values/strings.xml
@@ -157,4 +157,15 @@
<string name="action_pwd_dialog_cancel">Cancel</string>
<string name="action_pwd_dialog_title">Please enter password</string>
+ <!-- Insert Image Strings -->
+ <string name="take_photo">Take Photo</string>
+ <string name="select_photo">Select Photo</string>
+ <string name="select_photo_title">Select Picture</string>
+ <string name="no_camera_found">No Camera Found</string>
+ <string name="compress_photo_smallest_size">Smallest Size</string>
+ <string name="compress_photo_medium_size">Medium Size</string>
+ <string name="compress_photo_max_quality">Max Quality</string>
+ <string name="compress_photo_no_compress">Don\'t Compress</string>
+ <string name="compress_photo_title">Do you want to compress the photo?</string>
+
</resources>
diff --git a/android/source/res/xml/file_paths.xml b/android/source/res/xml/file_paths.xml
new file mode 100644
index 000000000000..2bbe2aef17ea
--- /dev/null
+++ b/android/source/res/xml/file_paths.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+ <external-path name="LO_files_universal" path="Android/" />
+</paths> \ No newline at end of file
diff --git a/android/source/src/java/org/libreoffice/FormattingController.java b/android/source/src/java/org/libreoffice/FormattingController.java
index 4d36249dc6b9..0ba72cf50875 100644
--- a/android/source/src/java/org/libreoffice/FormattingController.java
+++ b/android/source/src/java/org/libreoffice/FormattingController.java
@@ -1,15 +1,45 @@
package org.libreoffice;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.graphics.Bitmap;
+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 android.util.Log;
import android.view.View;
import android.widget.ImageButton;
+import org.json.JSONException;
+import org.json.JSONObject;
import org.libreoffice.kit.Document;
- class FormattingController implements View.OnClickListener {
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.List;
+import java.util.Locale;
+
+import static org.libreoffice.SearchController.addProperty;
+
+class FormattingController implements View.OnClickListener {
private static final String LOGTAG = ToolbarController.class.getSimpleName();
+ private static final int TAKE_PHOTO = 1;
+ private static final int SELECT_PHOTO = 2;
+ private static final int IMAGE_BUFFER_SIZE = 4 * 1024;
private LibreOfficeMainActivity mContext;
+ private String mCurrentPhotoPath;
FormattingController(LibreOfficeMainActivity context) {
mContext = context;
@@ -29,6 +59,7 @@ import org.libreoffice.kit.Document;
mContext.findViewById(R.id.button_insert_line).setOnClickListener(this);
mContext.findViewById(R.id.button_insert_rect).setOnClickListener(this);
+ mContext.findViewById(R.id.button_insert_picture).setOnClickListener(this);
mContext.findViewById(R.id.button_font_shrink).setOnClickListener(this);
mContext.findViewById(R.id.button_font_grow).setOnClickListener(this);
@@ -99,6 +130,8 @@ import org.libreoffice.kit.Document;
case R.id.button_superscript:
LOKitShell.sendEvent(new LOEvent(LOEvent.UNO_COMMAND, ".uno:SuperScript"));
break;
+ case R.id.button_insert_picture:
+ insertPicture();
}
}
@@ -152,4 +185,177 @@ import org.libreoffice.kit.Document;
}
});
}
+
+ private void insertPicture() {
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ String[] options = {mContext.getResources().getString(R.string.take_photo),
+ mContext.getResources().getString(R.string.select_photo)};
+ builder.setItems(options, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case 0:
+ dispatchTakePictureIntent();
+ break;
+ case 1:
+ sendImagePickingIntent();
+ break;
+ default:
+ sendImagePickingIntent();
+ }
+ }
+ });
+ builder.show();
+ }
+
+ private void sendImagePickingIntent() {
+ Intent intent = new Intent();
+ intent.setType("image/*");
+ intent.setAction(Intent.ACTION_PICK);
+ mContext.startActivityForResult(Intent.createChooser(intent,
+ mContext.getResources().getString(R.string.select_photo_title)), SELECT_PHOTO);
+ }
+
+ private void dispatchTakePictureIntent() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+ Snackbar.make(mContext.findViewById(R.id.button_insert_picture),
+ mContext.getResources().getString(R.string.no_camera_found), Snackbar.LENGTH_SHORT).show();
+ return;
+ }
+ Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ // Ensure that there's a camera activity to handle the intent
+ if (takePictureIntent.resolveActivity(mContext.getPackageManager()) != null) {
+ // Create the File where the photo should go
+ File photoFile = null;
+ try {
+ photoFile = createImageFile();
+ } catch (IOException ex) {
+ ex.printStackTrace();
+ }
+ // Continue only if the File was successfully created
+ if (photoFile != null) {
+ Uri photoURI = FileProvider.getUriForFile(mContext,
+ mContext.getPackageName() + ".fileprovider",
+ photoFile);
+ takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
+ // Grant permissions to potential photo/camera apps (for some Android versions)
+ List<ResolveInfo> resInfoList = mContext.getPackageManager()
+ .queryIntentActivities(takePictureIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ mContext.grantUriPermission(packageName, photoURI, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+ | Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ mContext.startActivityForResult(takePictureIntent, TAKE_PHOTO);
+ }
+ }
+ }
+
+ void handleActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == TAKE_PHOTO && resultCode == Activity.RESULT_OK) {
+ mContext.pendingInsertGraphic = true;
+ } else if (requestCode == SELECT_PHOTO && resultCode == Activity.RESULT_OK) {
+ getFileFromURI(data.getData());
+ mContext.pendingInsertGraphic = true;
+ }
+ }
+
+ // Called by LOKitTileProvider when activity is resumed from photo/gallery/camera/cloud apps
+ void popCompressImageGradeSelection() {
+ 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),
+ mContext.getResources().getString(R.string.compress_photo_max_quality),
+ mContext.getResources().getString(R.string.compress_photo_no_compress)};
+ builder.setTitle(mContext.getResources().getString(R.string.compress_photo_title));
+ builder.setItems(options, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ int compressGrade;
+ switch (which) {
+ case 0:
+ compressGrade = 0;
+ break;
+ case 1:
+ compressGrade = 50;
+ break;
+ case 2:
+ compressGrade = 100;
+ break;
+ case 3:
+ compressGrade = -1;
+ break;
+ default:
+ compressGrade = -1;
+ }
+ compressImage(compressGrade);
+ sendInsertGraphic();
+ }
+ });
+ builder.show();
+ }
+
+ private void getFileFromURI(Uri uri) {
+ try {
+ InputStream input = mContext.getContentResolver().openInputStream(uri);
+ mCurrentPhotoPath = createImageFile().getAbsolutePath();
+ FileOutputStream output = new FileOutputStream(mCurrentPhotoPath);
+ if (input != null) {
+ byte[] buffer = new byte[IMAGE_BUFFER_SIZE];
+ int read;
+ while ((read = input.read(buffer)) != -1) {
+ output.write(buffer, 0, read);
+ }
+ input.close();
+ }
+ output.flush();
+ output.close();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void sendInsertGraphic() {
+ JSONObject rootJson = new JSONObject();
+ try {
+ addProperty(rootJson, "FileName", "string", "file://" + mCurrentPhotoPath);
+ } catch (JSONException ex) {
+ ex.printStackTrace();
+ }
+ 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) {
+ if (grade < 0 || grade > 100) {
+ return;
+ }
+ mContext.showProgressSpinner();
+ Bitmap bmp = BitmapFactory.decodeFile(mCurrentPhotoPath);
+ try {
+ mCurrentPhotoPath = createImageFile().getAbsolutePath();
+ FileOutputStream out = new FileOutputStream(mCurrentPhotoPath);
+ bmp.compress(Bitmap.CompressFormat.JPEG, grade, out);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ mContext.hideProgressSpinner();
+ }
+
+ private File createImageFile() throws IOException {
+ // Create an image file name
+ String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date());
+ String imageFileName = "JPEG_" + timeStamp + "_";
+ File storageDir = mContext.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
+ File image = File.createTempFile(
+ imageFileName, /* prefix */
+ ".jpg", /* suffix */
+ storageDir /* directory */
+ );
+ // Save a file: path for use with ACTION_VIEW intents
+ mCurrentPhotoPath = image.getAbsolutePath();
+ return image;
+ }
}
diff --git a/android/source/src/java/org/libreoffice/LOEvent.java b/android/source/src/java/org/libreoffice/LOEvent.java
index 7846f7331bbd..4d081e61c0f2 100644
--- a/android/source/src/java/org/libreoffice/LOEvent.java
+++ b/android/source/src/java/org/libreoffice/LOEvent.java
@@ -39,6 +39,7 @@ public class LOEvent implements Comparable<LOEvent> {
public static final int UPDATE_PART_PAGE_RECT = 18;
public static final int UPDATE_ZOOM_CONSTRAINTS = 19;
public static final int UPDATE_CALC_HEADERS = 20;
+ public static final int REFRESH = 21;
public final int mType;
public int mPriority = 0;
diff --git a/android/source/src/java/org/libreoffice/LOKitThread.java b/android/source/src/java/org/libreoffice/LOKitThread.java
index 721853d08a99..99b53397b9d0 100644
--- a/android/source/src/java/org/libreoffice/LOKitThread.java
+++ b/android/source/src/java/org/libreoffice/LOKitThread.java
@@ -354,7 +354,9 @@ class LOKitThread extends Thread {
case LOEvent.UPDATE_CALC_HEADERS:
updateCalcHeaders();
break;
-
+ case LOEvent.REFRESH:
+ refresh();
+ break;
}
}
diff --git a/android/source/src/java/org/libreoffice/LOKitTileProvider.java b/android/source/src/java/org/libreoffice/LOKitTileProvider.java
index 7be5ac31f60c..a68f65221795 100644
--- a/android/source/src/java/org/libreoffice/LOKitTileProvider.java
+++ b/android/source/src/java/org/libreoffice/LOKitTileProvider.java
@@ -150,6 +150,14 @@ class LOKitTileProvider implements TileProvider {
mContext.getDocumentPartViewListAdapter().notifyDataSetChanged();
}
});
+ mContext.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ if (mContext.pendingInsertGraphic) {
+ mContext.getFormattingController().popCompressImageGradeSelection();
+ }
+ }
+ });
}
@Override
diff --git a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
index defd0d18476e..534eaf44de59 100644
--- a/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
+++ b/android/source/src/java/org/libreoffice/LibreOfficeMainActivity.java
@@ -98,6 +98,7 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
private LOKitTileProvider mTileProvider;
private String mPassword;
private boolean mPasswordProtected;
+ public boolean pendingInsertGraphic; // boolean indicating a pending insert graphic action, used in LOKitTileProvider.postLoad()
public GeckoLayerClient getLayerClient() {
return mLayerClient;
@@ -863,6 +864,12 @@ public class LibreOfficeMainActivity extends AppCompatActivity implements Settin
.setPositiveButton(R.string.alert_copy_svg_slide_show_to_clipboard_dismiss, null).show();
}
}
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mFormattingController.handleActivityResult(requestCode, resultCode, data);
+ hideBottomToolbar();
+ }
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */