Skip to content

Commit

Permalink
Implemented #2: Workflow SEND/SENDTO(uri=sourcePhoto.jpg) => crop => …
Browse files Browse the repository at this point in the history
…tempfile.jpg => SEND/SENDTO(uri=tempfile.jpg)
  • Loading branch information
k3b committed May 17, 2019
1 parent fee5e82 commit 3882a4a
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 34 deletions.
2 changes: 2 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'


// implements lossless croppig
// implementation 'com.facebook.spectrum:spectrum-default:1.0.0'
implementation 'com.facebook.spectrum:spectrum-core:1.0.0'
Expand Down
12 changes: 12 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.OPENABLE"/>

<data android:mimeType="image/jpeg" />
</intent-filter>
</activity>

<activity android:name=".CropAreasSendActivity">
<intent-filter >
<action android:name="android.intent.action.SEND" />
<action android:name="android.intent.action.SENDTO" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:mimeType="image/jpeg" />
</intent-filter>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

/** base cropping functionality for all different workflows */
/** For all different workflows CropAreaXxxxActivity:
* * Displays the cropping gui
* * Contains Protected helpers for common functionalities */
abstract class CropAreasChooseBaseActivity extends BaseActivity {
private static int lastInstanceNo = 0;
private int instanceNo = 0;
Expand Down Expand Up @@ -79,7 +81,10 @@ protected void SetImageUriAndLastCropArea(Uri uri, Bundle savedInstanceState) {
}

protected Uri getSourceImageUri(Intent intent) {
return intent.getData();
if (intent == null) return null;

Uri uri = intent.getData();
return uri;
}

// #7: workaround rotation change while picker is open causes Activity re-create without
Expand Down Expand Up @@ -182,10 +187,12 @@ private void createSendIntend(boolean asExtra) {
}
}

private static void copyExtra(Intent outIntent, Bundle extras, String... extraIds) {
for(String id: extraIds) {
String value = extras.getString(id, null);
if (value != null) outIntent.putExtra(id, value);
protected static void copyExtra(Intent outIntent, Bundle extras, String... extraIds) {
if (extras != null) {
for (String id : extraIds) {
String value = extras.getString(id, null);
if (value != null) outIntent.putExtra(id, value);
}
}
}

Expand Down Expand Up @@ -264,7 +271,7 @@ private Uri __delete_saveAsPrivate() {
Log.d(TAG, context_message);

try {
final File sharedFolder = new File(getFilesDir(), "shared");
final File sharedFolder = getSharedDir();
final File sharedFile = File.createTempFile("llCrop_",".jpg", sharedFolder);
sharedFolder.mkdirs();
outStream = new FileOutputStream(sharedFile);
Expand All @@ -290,7 +297,7 @@ protected void crop(InputStream inStream, OutputStream outStream, Rect rect) {

private Uri createPrivateOutUriOrNull() {
try {
final File sharedFolder = new File(getFilesDir(), "shared");
final File sharedFolder = getSharedDir();
final File sharedFile = File.createTempFile("llCrop_",".jpg", sharedFolder);
sharedFolder.mkdirs();

Expand Down Expand Up @@ -323,6 +330,25 @@ private Uri createPrivateOutUriOrNull() {
*/
}

protected File getSharedDir() {
File sharedDir = new File(this.getFilesDir(), "shared");
sharedDir.mkdirs();
return sharedDir;
}

protected String createCropFileName() {
Uri inUri = getSourceImageUri(getIntent());
String originalFileName = (inUri == null) ? "" : inUri.getLastPathSegment();
return replaceExtension(originalFileName, "_llcrop.jpg");
}

/** replaceExtension("/path/to/image.jpg", ".xmp") becomes "/path/to/image.xmp" */
private static String replaceExtension(String path, String extension) {
if (path == null) return null;
int ext = path.lastIndexOf(".");
return ((ext >= 0) ? path.substring(0, ext) : path) + extension;
}



}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@
import java.io.InputStream;
import java.io.OutputStream;

/**
* #1: ACTION_EDIT(uri=sourcePhoto.jpg) => crop => public-file.jpg
* #1: ACTION_MAIN => ACTION_EDIT(uri=GetContent(mime=image/jpeg)) => crop => public-file.jpg
*
* Handles ACTION_EDIT(uri=DATA) and ACTION_MAIN:
*/

public class CropAreasEditActivity extends CropAreasChooseBaseActivity {
private static final int REQUEST_SAVE_PICTURE = 2;
private static final int REQUEST_SAVE_PICTURE_PERMISSION = 102;
Expand All @@ -35,10 +42,12 @@ protected void onCreate(Bundle savedInstanceState) {
}
}

/*
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
*/

@Override
public boolean onCreateOptionsMenu(final Menu menu) {
Expand Down Expand Up @@ -82,8 +91,10 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
}

if (requestCode == REQUEST_SAVE_PICTURE) {
final Uri outUri = (data == null) ? null : data.getData();
onOpenPublicOutputUriPickerResult(resultCode, outUri);
if (resultCode == RESULT_OK) {
final Uri outUri = (data == null) ? null : data.getData();
onOpenPublicOutputUriPickerResult(outUri);
} else finish();
return;
}

Expand All @@ -103,9 +114,7 @@ private boolean saveAsPublicCroppedImage() {
}

private boolean openPublicOutputUriPicker(int folderpickerCode) {
Uri inUri = getIntent().getData();
String originalFileName = (inUri == null) ? "" : inUri.getLastPathSegment();
String proposedFileName = replaceExtension(originalFileName, "_llcrop.jpg");
String proposedFileName = createCropFileName();
// String proposedOutPath = inUri.getP new Uri() replaceExtension(originalFileName, "_llcrop.jpg");


Expand All @@ -126,9 +135,10 @@ private boolean openPublicOutputUriPicker(int folderpickerCode) {
return true;
}

private void onOpenPublicOutputUriPickerResult(int resultCode, Uri outUri) {
private void onOpenPublicOutputUriPickerResult(Uri outUri) {
final Intent intent = getIntent();
final Uri inUri = ((resultCode == RESULT_OK) && (intent != null)) ? intent.getData() : null;

final Uri inUri = getSourceImageUri(getIntent());

if (inUri != null) {
Rect rect = getCropRect();
Expand Down Expand Up @@ -182,10 +192,4 @@ private void onGetPictureResult(int resultCode, Intent data) {
return;
}

/** replaceExtension("/path/to/image.jpg", ".xmp") becomes "/path/to/image.xmp" */
private static String replaceExtension(String path, String extension) {
if (path == null) return null;
int ext = path.lastIndexOf(".");
return ((ext >= 0) ? path.substring(0, ext) : path) + extension;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
package de.k3b.android.lossless_jpg_crop;

import android.content.ClipData;
import android.content.Intent;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import androidx.core.content.FileProvider;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
* #2: SEND/SENDTO(uri=sourcePhoto.jpg) => crop => tempfile.jpg => SEND/SENDTO(uri=tempfile.jpg)
*
* Handles ACTION_SENDTO(uri=DATA) and ACTION_SEND(uri=EXTRA_STREAM):
*/
public class CropAreasSendActivity extends CropAreasChooseBaseActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getSourceImageUri(getIntent());

//
/*As of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}, the data
* being sent can be supplied through {@link #setClipData(ClipData)}. This
* allows you to use {@link #FLAG_GRANT_READ_URI_PERMISSION} when sharing
* content: URIs and other advanced features of {@link ClipData}. If
* using this approach, you still must supply the same data through the
* {@link #EXTRA_TEXT} or {@link #EXTRA_STREAM} fields described below
* for compatibility with old applications. */

SetImageUriAndLastCropArea(uri, savedInstanceState);
}

@Override
public boolean onCreateOptionsMenu(final Menu menu) {
getMenuInflater().inflate(R.menu.menu_send, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_send:
return sendPrivateCroppedImage();
default:
return super.onOptionsItemSelected(item);
}
// return true;
}

/** get uri from intent: ACTION_SENDTO(uri=DATA) and ACTION_SEND(uri=EXTRA_STREAM) */
@Override
protected Uri getSourceImageUri(Intent intent) {
Uri uri = super.getSourceImageUri(intent);

if ((uri == null) && (intent != null)) {
Bundle extras = (uri != null) ? null : intent.getExtras();
Object stream = (extras == null) ? null : extras.get(Intent.EXTRA_STREAM);
if (stream != null) {
uri = Uri.parse(stream.toString());
}

}
return uri;
}

private boolean sendPrivateCroppedImage() {

final Intent parentIntent = getIntent();
final Uri inUri = getSourceImageUri(parentIntent);
Uri outUri = null;

File outFile = new File(getSharedDir(), createCropFileName());

if (inUri != null) {
Rect rect = getCropRect();
InputStream inStream = null;
OutputStream outStream = null;

final String context_message = getInstanceNo() + "Cropping '" + inUri + "'(" + rect + ") => '"
+ outFile.getName();
Log.i(TAG, context_message);

try {
inStream = getContentResolver().openInputStream(inUri);
outStream = new FileOutputStream(outFile, false);
crop(inStream, outStream, rect);

outUri = FileProvider.getUriForFile(this, "de.k3b.llCrop", outFile);

} catch (Exception e) {
Log.e(TAG, "Error " + context_message + e.getMessage(), e);
} finally {
close(outStream, outStream);
close(inStream, inStream);
}
boolean isSend = isSendAction();

Intent childSend = new Intent();

if (isSend) {
childSend
.setAction(Intent.ACTION_SEND)
.putExtra(Intent.EXTRA_STREAM, outUri)
.setType(IMAGE_JPEG_MIME);
} else {
childSend
.setAction(Intent.ACTION_SENDTO)
.setDataAndType(outUri, IMAGE_JPEG_MIME);
}

childSend.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
copyExtra(childSend, parentIntent.getExtras(),
Intent.EXTRA_EMAIL, Intent.EXTRA_CC, Intent.EXTRA_BCC, Intent.EXTRA_SUBJECT,
Intent.EXTRA_TEXT);


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
ClipData clip = ClipData.newUri(getContentResolver(), outUri.toString(), outUri);
childSend.setClipData(clip);
}

final Intent execIntent = Intent.createChooser(childSend, this.getText(R.string.label_send));

startActivity(execIntent);

finish();
return true;
} else {
// uri==null or error
Log.i(TAG, getInstanceNo() + "onOpenPublicOutputUriPickerResult(null): No output url, not saved.");
}
return false;
}

private boolean isSendAction() {
Intent i = getIntent();
String action = (i != null) ? i.getAction() : null;
return (action != null) && Intent.ACTION_SEND.equalsIgnoreCase(action);
}
}
2 changes: 1 addition & 1 deletion app/src/main/res/menu/menu_send.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

android:showAsAction="always"
android:icon="@android:drawable/ic_menu_share"
android:title="@android:string/ok"
android:title="@string/label_send"
tools:ignore="AppCompatResource" />

</menu>
12 changes: 0 additions & 12 deletions app/src/main/res/menu/menu_send_to.xml

This file was deleted.

1 change: 1 addition & 0 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

<string name="label_select_picture">Wähle Photo</string>
<string name="label_save_as">Speichern als</string>
<string name="label_send">Senden/Teilen</string>

<string name="permission_title_rationale">Berechtigung erforderlich</string>
<string name="permission_read_storage_rationale">Benötige Datei-Leserechte um ein Photo zu laden.</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

<string name="label_select_picture">Select Picture</string>
<string name="label_save_as">Save as</string>
<string name="label_send">Send/Share to</string>

<string name="permission_title_rationale">Permission needed</string>
<string name="permission_read_storage_rationale">Storage read permission is needed to pick files.</string>
Expand Down

0 comments on commit 3882a4a

Please sign in to comment.