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.