From dd3f7912fffcb6398b4aaf7ff5cdfdfd32454224 Mon Sep 17 00:00:00 2001 From: AbdurazaaqMohammed Date: Mon, 23 Dec 2024 23:47:24 -0400 Subject: [PATCH] support Android 4.0.3+/SDK 15, target SDK 36, pure/AMOLED black theme, add option to remove EXIF, work with no permissions on Android 6+/SDK 23+ --- .gitignore | 1 + app/build.gradle | 24 +- .../ExampleInstrumentedTest.java | 5 - app/src/main/AndroidManifest.xml | 5 +- .../lossless_jpg_crop/BaseActivity.java | 24 +- .../CropAreasChooseBaseActivity.java | 273 ++++++++++------- .../CropAreasSendActivity.java | 2 +- .../DefineAspectRatioFragment.java | 34 +-- .../lossless_jpg_crop/ImageProcessor.java | 5 +- app/src/main/java/de/k3b/util/FileUtils.java | 73 +++++ .../main/java/de/k3b/util/LegacyUtils.java | 9 + .../main/java/de/k3b/util/TempFileUtil.java | 9 +- app/src/main/res/layout/activity_crop.xml | 2 + .../layout/fragment_define_aspect_ratio.xml | 111 +++---- app/src/main/res/menu/menu_aspect_ratio.xml | 4 +- app/src/main/res/menu/menu_edit.xml | 1 - app/src/main/res/menu/menu_exif.xml | 7 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 6 +- app/src/main/res/xml/file_provider_paths.xml | 6 +- build.gradle | 3 +- cropper/build.gradle | 8 +- .../BitmapCroppingWorkerTask.java | 4 +- .../lib/androidimagecropper/BitmapUtils.java | 12 +- .../lib/androidimagecropper/CropImage.java | 48 ++- .../androidimagecropper/CropImageOptions.java | 2 +- .../androidimagecropper/CropImageView.java | 254 ++++++++-------- .../androidimagecropper/CropOverlayView.java | 4 +- .../CropWindowHandler.java | 8 +- .../CropWindowMoveHandler.java | 22 +- .../src/main/res/layout/crop_image_view.xml | 2 +- gradle.properties | 8 +- gradle/wrapper/gradle-wrapper.jar | Bin 54329 -> 59536 bytes gradle/wrapper/gradle-wrapper.properties | 5 +- gradlew | 282 +++++++++++------- gradlew.bat | 43 +-- 36 files changed, 723 insertions(+), 584 deletions(-) create mode 100644 app/src/main/java/de/k3b/util/FileUtils.java create mode 100644 app/src/main/java/de/k3b/util/LegacyUtils.java create mode 100644 app/src/main/res/menu/menu_exif.xml diff --git a/.gitignore b/.gitignore index 2ec2b00..dc94d2f 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ /cropper/build /todo.txt /advertise-llCrop.md +/app/release diff --git a/app/build.gradle b/app/build.gradle index fbf6994..005e68f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,15 +1,12 @@ apply plugin: 'com.android.application' android { - //noinspection GradleCompatible - compileSdkVersion 28 + compileSdk 34 defaultConfig { applicationId "de.k3b.android.lossless_jpg_crop" - // SAF ACTION_CREATE_DOCUMENT requires api-19 and later - minSdkVersion 19 // Android 4.4 KitKat (API 19); Android 5.0 Lollipop (API 21); Android 6.0 Marshmallow (API 23); Android 7.0 Nougat (API 24) - //noinspection ExpiredTargetSdkVersion - targetSdkVersion 29 + minSdk 15 + targetSdk 36 // 1.0.0.190425 (1) initial version // 1.0.1.190507 (2) Bugfix: missing read permission; port to android-x @@ -23,7 +20,6 @@ android { // 1.3.0.230218 (10) new features: #15:modifyable dimensions; #35:Display crop box coordinates and size versionCode 10 versionName "1.3.0.230218" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" // all supported locales. Note: the lib has more translations which are supressed here // resConfigs "ar","de","es","fr","hi","in","it","ja","nl","pl","ro","ru","tr","uk","zz","pt-rBR","zh-rCN","zh-rTW" @@ -33,11 +29,11 @@ android { debug { shrinkResources true minifyEnabled true - // minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' applicationIdSuffix ".debug" - versionNameSuffix "-DEBUG" } + versionNameSuffix "-DEBUG" + } release { shrinkResources true minifyEnabled true @@ -47,8 +43,7 @@ android { lintOptions { // http://stackoverflow.com/questions/31350350/generating-signed-apk-error7-missingtranslation-in-build-generated-res-gen // MissingTranslation : not all crowdwin translations are complete so ignore them - disable 'MissingTranslation' - + // Hell nah dont just ignore the translations abortOnError false } compileOptions { @@ -59,17 +54,12 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.annotation:annotation:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:2.0.4' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' // implements lossless croppig // implementation 'com.facebook.spectrum:spectrum-default:1.0.0' + implementation("io.github.tommy-geenexus:exif-interface-extended:1.0.3") implementation 'com.facebook.spectrum:spectrum-core:1.3.0' implementation 'com.facebook.spectrum:spectrum-jpeg:1.3.0' // the cropping gui diff --git a/app/src/androidTest/java/de/k3b/android/lossless_jpg_crop/ExampleInstrumentedTest.java b/app/src/androidTest/java/de/k3b/android/lossless_jpg_crop/ExampleInstrumentedTest.java index 87ef8e0..9d63701 100644 --- a/app/src/androidTest/java/de/k3b/android/lossless_jpg_crop/ExampleInstrumentedTest.java +++ b/app/src/androidTest/java/de/k3b/android/lossless_jpg_crop/ExampleInstrumentedTest.java @@ -1,11 +1,6 @@ package de.k3b.android.lossless_jpg_crop; import android.content.Context; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; import static org.junit.Assert.*; diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e2ce505..2dadb70 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,8 +3,8 @@ package="de.k3b.android.lossless_jpg_crop" android:installLocation="auto"> - - + + ...). */ public class BaseActivity extends Activity { @@ -25,6 +29,12 @@ protected void onStop() { } } + @Override + protected void onStart() { + ActionBar ab = getActionBar(); + if(ab != null) ab.setBackgroundDrawable(new ColorDrawable(Color.BLACK)); + super.onStart(); + } /** * Requests given permission. @@ -34,13 +44,8 @@ protected void onStop() { protected void requestPermission(final String permission, String rationale, final int requestCode) { if (ActivityCompat.shouldShowRequestPermissionRationale(this, permission)) { showAlertDialog(getString(R.string.permission_title_rationale), rationale, - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - ActivityCompat.requestPermissions(BaseActivity.this, - new String[]{permission}, requestCode); - } - }, getString(android.R.string.ok), null, getString(android.R.string.cancel)); + (dialog, which) -> ActivityCompat.requestPermissions(BaseActivity.this, + new String[]{permission}, requestCode), getString(android.R.string.ok), null, getString(android.R.string.cancel)); } else { ActivityCompat.requestPermissions(this, new String[]{permission}, requestCode); } @@ -69,5 +74,4 @@ protected void showAlertDialog(@Nullable String title, @Nullable String message, builder.setNegativeButton(negativeText, onNegativeButtonClickListener); mAlertDialog = builder.show(); } - -} +} \ No newline at end of file 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 70dacbe..83f0c4b 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 @@ -18,7 +18,8 @@ */ package de.k3b.android.lossless_jpg_crop; -import android.Manifest; +import static android.content.Intent.FLAG_GRANT_WRITE_URI_PERMISSION; + import android.app.Activity; import android.content.ClipData; import android.content.Context; @@ -31,6 +32,7 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.preference.PreferenceManager; import android.provider.DocumentsContract; import android.util.Log; @@ -41,10 +43,11 @@ import android.widget.Toast; import androidx.annotation.NonNull; -import androidx.core.app.ActivityCompat; import androidx.core.content.FileProvider; import androidx.exifinterface.media.ExifInterface; +import com.tomg.exifinterfaceextended.ExifInterfaceExtended; + import net.realify.lib.androidimagecropper.CropImageView; import java.io.Closeable; @@ -54,13 +57,14 @@ import java.io.InputStream; import java.io.OutputStream; import java.net.URLDecoder; -import java.nio.charset.StandardCharsets; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import de.k3b.util.FileUtils; import de.k3b.util.TempFileUtil; /** @@ -153,14 +157,14 @@ protected void pickFromGalleryForEdit() { } protected boolean saveAsPublicCroppedImage() { - if (ActivityCompat.checkSelfPermission(getBaseContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, - getString(R.string.permission_write_storage_rationale), - Edit.REQUEST_SAVE_EDIT_PICTURE_PERMISSION); - } else { +// if (ActivityCompat.checkSelfPermission(getBaseContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) +// != PackageManager.PERMISSION_GRANTED) { +// requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE, +// getString(R.string.permission_write_storage_rationale), +// Edit.REQUEST_SAVE_EDIT_PICTURE_PERMISSION); +// } else edit.openPublicOutputUriPicker(Edit.REQUEST_SAVE_EDIT_PICTURE_AS); - } + return true; } @@ -168,26 +172,37 @@ protected boolean openPublicOutputUriPicker(int folderpickerCode) { String proposedFileName = createCropFileName(); // String proposedOutPath = inUri.getP new Uri() replaceExtension(originalFileName, "_llcrop.jpg"); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT) + .setType(IMAGE_JPEG_MIME) + .addCategory(Intent.CATEGORY_OPENABLE) + .putExtra(Intent.EXTRA_TITLE, proposedFileName) + .setFlags(FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { + intent.putExtra(DocumentsContract.EXTRA_PROMPT, getString(R.string.label_save_as)); + } + Log.d(TAG, getInstanceNo4Debug() + "openPublicOutputUriPicker '" + proposedFileName + "'"); - // DocumentsContract#EXTRA_INITIAL_URI - Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT) - .setType(IMAGE_JPEG_MIME) - .addCategory(Intent.CATEGORY_OPENABLE) - .putExtra(Intent.EXTRA_TITLE, proposedFileName) - .setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION - | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) { - intent.putExtra(DocumentsContract.EXTRA_PROMPT, getString(R.string.label_save_as)); + startActivityForResult(intent, folderpickerCode); + } else { + File parent = new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES); + parent.mkdir(); + File file = new File(parent, proposedFileName); + try(OutputStream os = new FileOutputStream(file)) { + edit.saveEditPictureToOutputStream(os); + Toast.makeText(getBaseContext(), + getString(R.string.toast_saved_as, Environment.DIRECTORY_PICTURES + '/' + file.getName()), + Toast.LENGTH_LONG).show(); + } catch (Exception e) { + Log.e(TAG, e.getMessage()); + } } - - Log.d(TAG, getInstanceNo4Debug() + "openPublicOutputUriPicker '" + proposedFileName + "'"); - - startActivityForResult(intent, folderpickerCode); return true; } protected void onGetEditPictureResult(int resultCode, Intent data) { if (resultCode == RESULT_OK) { - final Uri selectedUri = (data == null) ? null : getSourceImageUri(data); + final Uri selectedUri = (data == null) ? null : (data.getData()); if (selectedUri != null) { Log.d(TAG, getInstanceNo4Debug() + "Restarting with uri '" + selectedUri + "'"); @@ -203,7 +218,8 @@ 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 saveEditPictureToOutputStream(OutputStream outStream) { // use to provoke an error to test error handling // Uri outUri = Uri.parse(outUri + "-err"); @@ -212,44 +228,85 @@ protected void onSaveEditPictureAsOutputUriPickerResult(Uri outUri) { if (inUri != null) { Rect rect = getCropRect(); - InputStream inStream = null; - OutputStream outStream = null; +// final String context_message = getInstanceNo4Debug() + "Cropping '" + inUri + "'(" + rect + ") => '" +// + outUri + "' ('" + asString(outUri) + "')"; +// Log.i(TAG, context_message); + try (InputStream inStream = getContentResolver().openInputStream(inUri)) { + if(PreferenceManager + .getDefaultSharedPreferences(getApplicationContext()) + .getBoolean("remove-exif", false)) { + File tempImg = new File(getCacheDir(), "t.jpg"); + try(OutputStream temp = new FileOutputStream(tempImg)) { + crop(inStream, temp, rect); + try(InputStream is = FileUtils.getInputStream(tempImg)) { + new ExifInterfaceExtended(tempImg).saveExclusive(is, outStream, true); + } + tempImg.delete(); + } + } else crop(inStream, outStream, rect); + +// String message = getString(R.string.toast_saved_as, +// asString(outUri)); +// Toast.makeText(getBaseContext(), message, Toast.LENGTH_SHORT).show(); - final String context_message = getInstanceNo4Debug() + "Cropping '" + inUri + "'(" + rect + ") => '" - + outUri + "' ('" + asString(outUri) + "')"; - Log.i(TAG, context_message); + finishIfMainMethod(R.id.menu_save); + } catch (Exception e) { + close(outStream, outStream); - try { - inStream = getContentResolver().openInputStream(inUri); - outStream = getContentResolver().openOutputStream(outUri, "w"); - crop(inStream, outStream, rect); + // Log.e(TAG, "Error " + context_message + e.getMessage(), e); - String message = getString(R.string.toast_saved_as, - asString(outUri)); - Toast.makeText(getBaseContext(), message, Toast.LENGTH_SHORT).show(); +// Log.e(TAG, "Error " + context_message + "(" + outUri +") => " + e.getMessage(), e); + Toast.makeText(getBaseContext(), + //getString(R.string.toast_saved_error, asString(outUri), + e.getMessage() + // ) + , + Toast.LENGTH_LONG).show(); + } finally { + close(outStream, outStream); + } + } else { + // outUri==null or error + Log.i(TAG, getInstanceNo4Debug() + "onOpenPublicOutputUriPickerResult(null): No output url, not saved."); + } + } + + protected void onSaveEditPictureAsOutputUriPickerResult(Uri outUri) { + + // use to provoke an error to test error handling + // Uri outUri = Uri.parse(outUri + "-err"); + + final Uri inUri = getSourceImageUri(getIntent()); + + if (inUri != null) { +// final String context_message = getInstanceNo4Debug() + "Cropping '" + inUri + "'(" + rect + ") => '" +// + outUri + "' ('" + asString(outUri) + "')"; +// Log.i(TAG, context_message); + + try (OutputStream outStream = getContentResolver().openOutputStream(outUri)) { + saveEditPictureToOutputStream(outStream); +// String message = getString(R.string.toast_saved_as, +// asString(outUri)); +// Toast.makeText(getBaseContext(), message, Toast.LENGTH_SHORT).show(); finishIfMainMethod(R.id.menu_save); - return; } catch (Exception e) { - close(outStream, outStream); - - Log.e(TAG, "Error " + context_message + e.getMessage(), e); + // Log.e(TAG, "Error " + context_message + e.getMessage(), e); - try { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) try { // #14: delete affected file as it is useless DocumentsContract.deleteDocument(getContentResolver(), outUri); } catch (Exception exDelete) { // ignore if useless file cannot be deleted } - - Log.e(TAG, "Error " + context_message + "(" + outUri +") => " + e.getMessage(), e); +// Log.e(TAG, "Error " + context_message + "(" + outUri +") => " + e.getMessage(), e); Toast.makeText(getBaseContext(), - getString(R.string.toast_saved_error, asString(outUri), e.getMessage()), + //getString(R.string.toast_saved_error, asString(outUri), + e.getMessage() + // ) + , Toast.LENGTH_LONG).show(); - } finally { - close(outStream, outStream); - close(inStream, inStream); } } else { // outUri==null or error @@ -268,7 +325,7 @@ protected boolean sendPrivateCroppedImage() { Uri outUri = cropToSharedUri(); if (outUri != null) { - boolean isSend = isSendAction(); + boolean isSend = true;//isSendAction(); ??? Intent childSend = new Intent(); @@ -307,7 +364,7 @@ protected boolean sendPrivateCroppedImage() { private boolean isSendAction() { Intent i = getIntent(); String action = (i != null) ? i.getAction() : null; - return (action != null) && Intent.ACTION_SEND.equalsIgnoreCase(action); + return Intent.ACTION_SEND.equalsIgnoreCase(action); } private void copyExtra(Intent outIntent, Bundle extras, String... extraIds) { @@ -335,7 +392,7 @@ protected void onCreate(Bundle savedInstanceState) { String aspectRatioDefinitions = prefs.getString(KEY_ASPECT_RATIO_DEFINITIONS, null); if (aspectRatioDefinitions != null) { - currentAspectRatioDefinitions = new ArrayList(Arrays.asList(aspectRatioDefinitions.split(";"))); + currentAspectRatioDefinitions = new ArrayList<>(Arrays.asList(aspectRatioDefinitions.split(";"))); } } @@ -344,12 +401,7 @@ protected void onCreate(Bundle savedInstanceState) { } mSpectrum = new ImageProcessor(); - uCropView.setOnSetCropOverlayMovedListener(new CropImageView.OnSetCropOverlayMovedListener() { - @Override - public void onCropOverlayMoved(Rect rect) { - onUpdateCropping(); - } - }); + uCropView.setOnSetCropOverlayMovedListener(rect -> onUpdateCropping()); setAspectRatio(currentAspectRatioString); } @@ -384,9 +436,9 @@ protected void finishIfMainMethod(int idMenuMainMethod) { } protected void SetImageUriAndLastCropArea(Uri imageUri, Bundle savedInstanceState) { - final Rect crop = (Rect) ((savedInstanceState == null) + final Rect crop = (savedInstanceState == null) ? null - : savedInstanceState.getParcelable(KEY_CURRENT_CROP_AREA)); + : savedInstanceState.getParcelable(KEY_CURRENT_CROP_AREA); SetImageUriAndLastCropArea(imageUri, crop); } @@ -454,17 +506,14 @@ private void setCropRect(final Rect crop) { uCropView.setCropRect(crop); if (LOAD_ASYNC) { - uCropView.setOnSetImageUriCompleteListener(new CropImageView.OnSetImageUriCompleteListener() { - @Override - public void onSetImageUriComplete(CropImageView view, Uri imageUri, Exception error) { - // called when uCropView recreation is completed. - uCropView.setCropRect(crop); - Rect newCrop = getCropRect(); - Log.d(TAG, getInstanceNo4Debug() + "delayed onCreate(): crop=" + crop + "/" + newCrop); - uCropView.setOnSetImageUriCompleteListener(null); - - setRotationBeforeCrop(uCropView.getRotatedDegrees()); - } + uCropView.setOnSetImageUriCompleteListener((view, imageUri, error) -> { + // called when uCropView recreation is completed. + uCropView.setCropRect(crop); + Rect newCrop = getCropRect(); + Log.d(TAG, getInstanceNo4Debug() + "delayed onCreate(): crop=" + crop + "/" + newCrop); + uCropView.setOnSetImageUriCompleteListener(null); + + setRotationBeforeCrop(uCropView.getRotatedDegrees()); }); } } @@ -497,15 +546,18 @@ protected void onSaveInstanceState(Bundle outState) { } private void pickFromGallery(int requestId, int requestPermissionId) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN - && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - requestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, - getString(R.string.permission_read_storage_rationale), - requestPermissionId); - } else { - Log.d(TAG, getInstanceNo4Debug() + "Opening Image Picker"); - Intent intent = new Intent(Intent.ACTION_GET_CONTENT) +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN +// && ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) +// != PackageManager.PERMISSION_GRANTED) { +// requestPermission(Manifest.permission.READ_EXTERNAL_STORAGE, +// getString(R.string.permission_read_storage_rationale), +// requestPermissionId); +// } +// else + { + //Log.d(TAG, getInstanceNo4Debug() + "Opening Image Picker"); + Intent intent = new Intent(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT ? + Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_GET_CONTENT) .setType(IMAGE_JPEG_MIME) .putExtra(Intent.EXTRA_TITLE, getString(R.string.label_select_picture)) .addCategory(Intent.CATEGORY_OPENABLE) @@ -522,10 +574,10 @@ protected String asString(Uri outUri) { if (outUri == null) return ""; // may crash with "IllegalCharsetNameException" in https://github.com/k3b/LosslessJpgCrop/issues/7 try { - return URLDecoder.decode(outUri.toString(), StandardCharsets.UTF_8.toString()); + return URLDecoder.decode(outUri.toString(), Charset.forName("UTF-8").toString()); } catch (Exception e) { // UnsupportedEncodingException, IllegalCharsetNameException - Log.e(TAG, getInstanceNo4Debug() + "err cannot convert imageUri to string('" + outUri.toString() + "').", e); + Log.e(TAG, getInstanceNo4Debug() + "err cannot convert imageUri to string('" + outUri + "').", e); return outUri.toString(); } } @@ -634,31 +686,34 @@ protected Uri cropToSharedUri() { if (inUri != null) { Rect rect = getCropRect(); - InputStream inStream = null; - OutputStream outStream = null; final String context_message = getInstanceNo4Debug() + "Cropping '" + inUri + "'(" + rect + ") => '" + outFile.getName() + " "; Log.i(TAG, context_message); - try { - inStream = getContentResolver().openInputStream(inUri); - outStream = new FileOutputStream(outFile, false); + try (InputStream inStream = getContentResolver().openInputStream(inUri); + OutputStream outStream = FileUtils.getOutputStream(outFile)) { crop(inStream, outStream, rect); - + if(PreferenceManager + .getDefaultSharedPreferences(this.getApplicationContext()) + .getBoolean("remove-exif", false)) { + File img; + try(InputStream is = FileUtils.getInputStream(img = outFile); + OutputStream os = FileUtils.getOutputStream(outFile = new File(getCacheDir(), "s.jpg"))) { + new ExifInterfaceExtended(img).saveExclusive(is, os, true); + } + } outUri = FileProvider.getUriForFile(this, "de.k3b.LLCrop", outFile); - } catch (Exception e) { // #14: delete affected file as it is useless - close(outStream, outStream); outFile.delete(); Log.e(TAG, "Error " + context_message + "(" + outUri +") => " + e.getMessage(), e); Toast.makeText(this, - getString(R.string.toast_saved_error, outFile.getAbsolutePath(), e.getMessage()), + // getString(R.string.toast_saved_error, outFile.getAbsolutePath(), + e.getMessage() + // ) + , Toast.LENGTH_LONG).show(); - } finally { - close(outStream, outStream); - close(inStream, inStream); } } else { Log.e(TAG, getInstanceNo4Debug() + "Error cropToSharedUri(): Missing input imageUri."); @@ -700,20 +755,20 @@ public boolean onCreateOptionsMenu(final Menu menu) { Format 9:16: 9x15,10x18 */ - + getMenuInflater().inflate(R.menu.menu_exif, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { for(Integer key : menu2Rotation.keySet()) { - if (key != null) { - menu.findItem(key).setChecked(getRotationAfterCrop() == menu2Rotation.get(key)); - } - } - if (ENABLE_ASPECT_RATIO) { - onPrepareMenuAspectRatio(menu); + if (key != null) menu.findItem(key).setChecked(getRotationAfterCrop() == menu2Rotation.get(key)); } + if (ENABLE_ASPECT_RATIO) onPrepareMenuAspectRatio(menu); + + menu.findItem(R.id.menu_exif).setTitle(getString(R.string.remove_exif, new StringBuilder().append('(').append(PreferenceManager + .getDefaultSharedPreferences(this.getApplicationContext()) + .getBoolean("remove-exif", false)).append(')'))); return super.onPrepareOptionsMenu(menu); } @@ -745,6 +800,14 @@ public boolean onOptionsItemSelected(MenuItem item) { return true; } else if (menuItemId == R.id.menu_ratio_userdefined) { return onAspectRatioUserdefined((currentAspectRatioString == null) ? null : currentAspectRatioString.split("x")); + } else if (menuItemId == R.id.menu_exif) { + SharedPreferences sharedPreferences = PreferenceManager + .getDefaultSharedPreferences(this.getApplicationContext()); + sharedPreferences + .edit() + .putBoolean("remove-exif", !sharedPreferences.getBoolean("remove-exif", false)) + .apply(); + return true; } else { return super.onOptionsItemSelected(item); } @@ -903,11 +966,11 @@ private void redefineAspectMenu(List currentAspectRatioDefinitions, Stri this.currentAspectRatioDefinitions.add(0, aspectRatio); } if (currentAspectRatioDefinitions != null) { - SharedPreferences.Editor edit = PreferenceManager - .getDefaultSharedPreferences(this.getApplicationContext()).edit(); - - edit.putString(KEY_ASPECT_RATIO_DEFINITIONS, String.join(";", currentAspectRatioDefinitions)); - edit.apply(); + PreferenceManager + .getDefaultSharedPreferences(this.getApplicationContext()) + .edit() + .putString(KEY_ASPECT_RATIO_DEFINITIONS, String.join(";", currentAspectRatioDefinitions)) + .apply(); } } diff --git a/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasSendActivity.java b/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasSendActivity.java index 28e98a8..18e7c02 100644 --- a/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasSendActivity.java +++ b/app/src/main/java/de/k3b/android/lossless_jpg_crop/CropAreasSendActivity.java @@ -36,7 +36,7 @@ protected Uri getSourceImageUri(Intent intent) { Uri uri = super.getSourceImageUri(intent); if ((uri == null) && (intent != null)) { - Bundle extras = (uri != null) ? null : intent.getExtras(); + Bundle extras = intent.getExtras(); Object stream = (extras == null) ? null : extras.get(Intent.EXTRA_STREAM); if (stream != null) { uri = Uri.parse(stream.toString()); 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 index 16df3fd..1f04b22 100644 --- 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 @@ -98,30 +98,20 @@ public void onSaveInstanceState(Bundle outState) { outState.putString(PARAM_HEIGHT, mParamHeight); } - /** handle init for dialog-only controlls: cmdOk, cmdCancel, status */ + /** handle init for dialog-only controlls: cmdOk, cmdCancel, status + * @noinspection SuspiciousNameCombination*/ private void onCreateViewDialog(View view) { - view.