From 996546b3304799d40a56eca5cf0f10026a0b700b Mon Sep 17 00:00:00 2001
From: k3b <1374583+k3b@users.noreply.github.com>
Date: Wed, 25 Jan 2023 11:34:28 +0100
Subject: [PATCH] #15: Define crop box size or aspect ratio
---
.../CropAreasChooseBaseActivity.java | 105 ++++++++++---
.../DefineAspectRatioFragment.java | 147 ++++++++++++++++++
.../layout/fragment_define_aspect_ratio.xml | 107 +++++++++++++
app/src/main/res/menu/menu_aspect_ratio.xml | 23 ++-
app/src/main/res/values/dimens.xml | 27 ++++
app/src/main/res/values/strings.xml | 7 +-
app/src/main/res/xml/file_provider_paths.xml | 22 ++-
7 files changed, 410 insertions(+), 28 deletions(-)
create mode 100644 app/src/main/java/de/k3b/android/lossless_jpg_crop/DefineAspectRatioFragment.java
create mode 100644 app/src/main/res/layout/fragment_define_aspect_ratio.xml
create mode 100644 app/src/main/res/values/dimens.xml
diff --git a/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasChooseBaseActivity.java b/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasChooseBaseActivity.java
index 8a0cdaf..ce0fa06 100644
--- a/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasChooseBaseActivity.java
+++ b/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasChooseBaseActivity.java
@@ -1,3 +1,21 @@
+/*
+Copyright (C) 2019-2023 by k3b
+
+This file is part of de.k3b.android.lossless_jpg_crop (https://github.com/k3b/losslessJpgCrop/)
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see
+ */
package de.k3b.android.lossless_jpg_crop;
import android.Manifest;
@@ -44,14 +62,15 @@
* * Displays the cropping gui
* * Contains Protected helpers for common functionalities
*/
-abstract class CropAreasChooseBaseActivity extends BaseActivity {
+abstract class CropAreasChooseBaseActivity extends BaseActivity implements DefineAspectRatioFragment.AspectRatioHandler {
protected static final String TAG = "LLCrop";
private static final String STATE_CURRENT_CROP_AREA = "CURRENT_CROP_AREA";
private static final String STATE_CURRENT_ASPECT_RATIO = "KEY_ASPECT_RATIO";
protected static final boolean LOAD_ASYNC = false;
- private static final boolean ENABLE_ASPECT_RATIO = false;
+ private static final boolean ENABLE_ASPECT_RATIO = true;
+ public static final String ASPECT_RATIO_SQUARE = "8x8";
private static int lastInstanceNo4Debug = 0;
private int instanceNo4Debug = 0;
@@ -64,7 +83,9 @@ abstract class CropAreasChooseBaseActivity extends BaseActivity {
protected CropImageView uCropView = null;
protected TextView txtStatus = null;
private ImageProcessor mSpectrum;
- private String aspectRatio = null;
+
+ /** Same as last selected menu item text i.e. 9x13 */
+ private String currentAspectRatioString = null;
// #7: workaround rotation change while picker is open causes Activity re-create without
// uCropView recreation completed.
@@ -164,12 +185,10 @@ protected void onGetEditPictureResult(int resultCode, Intent data) {
Toast.makeText(getBaseContext(), R.string.toast_cannot_retrieve_selected_image, Toast.LENGTH_SHORT).show();
finishIfMainMethod(R.id.menu_save);
}
- protected void onSaveEditPictureAsOutputUriPickerResult(Uri _outUri) {
+ protected void onSaveEditPictureAsOutputUriPickerResult(Uri outUri) {
// use to provoke an error to test error handling
- // Uri outUri = Uri.parse(_outUri + "-err");
-
- Uri outUri = _outUri;
+ // Uri outUri = Uri.parse(outUri + "-err");
final Uri inUri = getSourceImageUri(getIntent());
@@ -200,7 +219,7 @@ protected void onSaveEditPictureAsOutputUriPickerResult(Uri _outUri) {
try {
// #14: delete affected file as it is useless
- DocumentsContract.deleteDocument(getContentResolver(), _outUri);
+ DocumentsContract.deleteDocument(getContentResolver(), outUri);
} catch (Exception exDelete) {
// ignore if useless file cannot be deleted
}
@@ -293,7 +312,7 @@ protected void onCreate(Bundle savedInstanceState) {
txtStatus = findViewById(R.id.status);
if (savedInstanceState != null) {
- aspectRatio = savedInstanceState.getString(STATE_CURRENT_ASPECT_RATIO, aspectRatio);
+ currentAspectRatioString = savedInstanceState.getString(STATE_CURRENT_ASPECT_RATIO, currentAspectRatioString);
}
mSpectrum = new ImageProcessor();
@@ -304,10 +323,11 @@ public void onCropOverlayMoved(Rect rect) {
}
});
- setAspectRatio(aspectRatio);
+ setAspectRatio(currentAspectRatioString);
}
private void setAspectRatio(String aspectRatio) {
+ this.currentAspectRatioString = aspectRatio;
String[] xy = (aspectRatio == null) ? null : aspectRatio.split("x");
if (xy == null) {
@@ -321,8 +341,8 @@ private void setAspectRatio(String aspectRatio) {
uCropView.setMinCropResultSize(x,y);
uCropView.setMaxCropResultSize(x,y);
} else {
- uCropView.setMinCropResultSize(40,99999);
- uCropView.setMaxCropResultSize(40,99999);
+ uCropView.setMinCropResultSize(40,40);
+ uCropView.setMaxCropResultSize(99999,99999);
}
} catch (Exception ex) {
String message = "setAspectRatio('" + aspectRatio + "') . Valid example '7x13'";
@@ -465,7 +485,7 @@ protected void onSaveInstanceState(Bundle outState) {
Rect crop = getCropRect();
Log.d(TAG, getInstanceNo4Debug() + "onSaveInstanceState : crop=" + crop);
outState.putParcelable(STATE_CURRENT_CROP_AREA, crop);
- outState.putString(STATE_CURRENT_ASPECT_RATIO, aspectRatio);
+ outState.putString(STATE_CURRENT_ASPECT_RATIO, currentAspectRatioString);
}
@@ -497,7 +517,7 @@ protected String asString(Uri outUri) {
try {
return URLDecoder.decode(outUri.toString(), StandardCharsets.UTF_8.toString());
} catch (Exception e) {
- //!!! UnsupportedEncodingException, IllegalCharsetNameException
+ // UnsupportedEncodingException, IllegalCharsetNameException
Log.e(TAG, getInstanceNo4Debug() + "err cannot convert imageUri to string('" + outUri.toString() + "').", e);
return outUri.toString();
}
@@ -688,21 +708,40 @@ public boolean onPrepareOptionsMenu(Menu menu) {
}
}
if (ENABLE_ASPECT_RATIO) {
- SubMenu menuCrop = menu.findItem(R.menu.menu_aspect_ratio).getSubMenu();
- for (int i = menuCrop.size() - 1; i >= 0; i--) {
- MenuItem item = menuCrop.getItem(i);
- item.setCheckable(true);
- item.setChecked(isCheckedAspectRatio(item));
+ MenuItem menuAspectRatio = menu.findItem(R.id.menu_aspect_ratio);
+ if (menuAspectRatio != null) {
+ SubMenu menuCrop = menuAspectRatio.getSubMenu();
+ for (int i = menuCrop.size() - 1; i >= 0; i--) {
+ MenuItem item = menuCrop.getItem(i);
+ item.setCheckable(true);
+ item.setChecked(isCurrentAspectRatio(item));
+ }
}
}
return super.onPrepareOptionsMenu(menu);
}
- private boolean isCheckedAspectRatio(MenuItem item) {
- // !!! ENABLE_ASPECT_RATIO
- return item.getItemId() == Menu.NONE && item.getTitle() != null;
+ /**
+ * @return true if Menu.NONE with ratio in title
+ */
+ private boolean isAspectRatio(MenuItem item) {
+ return item.getItemId() == Menu.NONE
+ && item.getTitle() != null;
}
+ /**
+ * @return true if item is the current selected ratio
+ */
+ private boolean isCurrentAspectRatio(MenuItem item) {
+ if (currentAspectRatioString == null) {
+ return item.getItemId() == R.id.menu_ratio_free;
+ } else if (currentAspectRatioString.equals(ASPECT_RATIO_SQUARE)) {
+ return item.getItemId() == R.id.menu_ratio_square;
+ } else {
+ return isAspectRatio(item)
+ && currentAspectRatioString.equalsIgnoreCase(item.getTitle().toString());
+ }
+ }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@@ -719,8 +758,30 @@ public boolean onOptionsItemSelected(MenuItem item) {
return send.sendPrivateCroppedImage();
} else if (menuItemId == R.id.menu_get_content) {
return content.returnPrivateCroppedImage();
+ } else if (isAspectRatio(item)) {
+ // Menu.NONE with ratio in title
+ setAspectRatio(item.getTitle().toString());
+ return true;
+ } else if (menuItemId == R.id.menu_ratio_square) {
+ setAspectRatio(ASPECT_RATIO_SQUARE);
+ return true;
+ } else if (menuItemId == R.id.menu_ratio_free) {
+ setAspectRatio(null);
+ return true;
+ } else if (menuItemId == R.id.menu_ratio_userdefined) {
+ /* !!! todo
+ R.id.menu_ratio_free
+ R.id.menu_ratio_square
+ R.id.menu_ratio_userdefined
+ */
+ return false;
} else {
return super.onOptionsItemSelected(item);
}
}
+
+ public void onDefineAspectRatio(String width, String height) {
+ setAspectRatio(width + "x" + height);
+ }
+
}
diff --git a/app/src/main/java/de/k3b/android/lossless_jpg_crop/DefineAspectRatioFragment.java b/app/src/main/java/de/k3b/android/lossless_jpg_crop/DefineAspectRatioFragment.java
new file mode 100644
index 0000000..223223f
--- /dev/null
+++ b/app/src/main/java/de/k3b/android/lossless_jpg_crop/DefineAspectRatioFragment.java
@@ -0,0 +1,147 @@
+/*
+Copyright (C) 2022-2023 by k3b
+
+This file is part of de.k3b.android.lossless_jpg_crop (https://github.com/k3b/losslessJpgCrop/)
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program. If not, see
+ */
+package de.k3b.android.lossless_jpg_crop;
+
+import android.app.DialogFragment;
+import android.os.Bundle;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+
+/**
+ * Define the aspect ratio of the result. (i.e 10x15).
+ *
+ * Use the {@link DefineAspectRatioFragment#newInstance} factory method to
+ * create an instance of this fragment.
+ */
+public class DefineAspectRatioFragment extends DialogFragment {
+
+ private static final String PARAM_WIDTH = "paramWidth";
+ private static final String PARAM_HEIGHT = "paramHeight";
+
+ private String mParamWidth;
+ private String mParamHeight;
+
+ private EditText editWidth;
+ private EditText editHeight;
+
+ /** must be implemented by calling activity to receive change in AspectRatio */
+ public interface AspectRatioHandler {
+ void onDefineAspectRatio(String width, String height);
+ }
+
+ public DefineAspectRatioFragment() {
+ // Required empty public constructor
+ }
+
+ /**
+ * Use this factory method to create a new instance of
+ * this fragment using the provided parameters.
+ *
+ * @param paramWidth aspect ratio width
+ * @param paramHeight aspect ratio height
+ * @return A new instance of fragment DefineAspectRatioFragment.
+ */
+ public static DefineAspectRatioFragment newInstance(String paramWidth, String paramHeight) {
+ DefineAspectRatioFragment fragment = new DefineAspectRatioFragment();
+ Bundle args = new Bundle();
+ args.putString(PARAM_WIDTH, paramWidth);
+ args.putString(PARAM_HEIGHT, paramHeight);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (getArguments() != null) {
+ mParamWidth = getArguments().getString(PARAM_WIDTH);
+ mParamHeight = getArguments().getString(PARAM_HEIGHT);
+ }
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ // Inflate the layout for this fragment
+ View view = inflater.inflate(R.layout.fragment_define_aspect_ratio, container, false);
+ if (getShowsDialog()) {
+ onCreateViewDialog(view);
+ }
+
+ return view;
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ saveDialog();
+ outState.putString(PARAM_WIDTH, mParamWidth);
+ outState.putString(PARAM_HEIGHT, mParamHeight);
+ }
+
+ /** handle init for dialog-only controlls: cmdOk, cmdCancel, status */
+ private void onCreateViewDialog(View view) {
+ view.