CameraResultCallback takes URI

* Do not compress camera output
* Handle attachments from content providers
pull/189/head
Alex Baker 12 years ago
parent 1cdd34fa63
commit 0a9083d43a

@ -63,27 +63,6 @@ public class AndroidUtilities {
}); });
} }
/** Read a bitmap from the specified file, scaling if necessary
* Returns null if scaling failed after several tries */
private static final int[] SAMPLE_SIZES = { 1, 2, 4, 6, 8, 10 };
private static final int MAX_DIM = 1024;
public static Bitmap readScaledBitmap(String file) {
Bitmap bitmap = null;
int tries = 0;
BitmapFactory.Options opts = new BitmapFactory.Options();
while((bitmap == null || (bitmap.getWidth() > MAX_DIM || bitmap.getHeight() > MAX_DIM)) && tries < SAMPLE_SIZES.length) {
opts.inSampleSize = SAMPLE_SIZES[tries];
try {
bitmap = BitmapFactory.decodeFile(file, opts);
} catch (OutOfMemoryError e) {
log.error(e.getMessage(), e);
}
tries++;
}
return bitmap;
}
/** /**
* Start the given intent, handling security exceptions if they arise * Start the given intent, handling security exceptions if they arise
* @param request request code. if negative, no request. * @param request request code. if negative, no request.

@ -6,24 +6,18 @@
package com.todoroo.astrid.data; package com.todoroo.astrid.data;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.text.TextUtils; import android.text.TextUtils;
import com.todoroo.andlib.data.AbstractModel; import com.todoroo.andlib.data.AbstractModel;
import com.todoroo.andlib.data.Property.StringProperty; import com.todoroo.andlib.data.Property.StringProperty;
import com.todoroo.andlib.utility.DateUtilities;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.tasks.files.FileHelper;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/** /**
* A model that is synchronized to a remote server and has a remote id * A model that is synchronized to a remote server and has a remote id
@ -91,35 +85,11 @@ abstract public class RemoteModel extends AbstractModel {
public static class PictureHelper { public static class PictureHelper {
public static final String PICTURES_DIRECTORY = "pictures"; //$NON-NLS-1$ public static JSONObject savePictureJson(final Uri uri) {
public static JSONObject savePictureJson(Context context, Bitmap bitmap) {
try { try {
String name = DateUtilities.now() + ".jpg"; return new JSONObject() {{
JSONObject jsonObject = new JSONObject(); put("uri", uri.toString());
jsonObject.put("name", name); }};
jsonObject.put("type", "image/jpeg");
File dir = FileHelper.getExternalFilesDir(context, PICTURES_DIRECTORY);
if (dir != null) {
File file = new File(dir + File.separator + DateUtilities.now() + ".jpg");
if (file.exists()) {
return null;
}
try {
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fos);
fos.flush();
fos.close();
jsonObject.put("path", file.getAbsolutePath());
} catch (IOException e) {
log.error(e.getMessage(), e);
}
return jsonObject;
} else {
return null;
}
} catch (JSONException e) { } catch (JSONException e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
} }
@ -131,10 +101,13 @@ abstract public class RemoteModel extends AbstractModel {
if (value == null) { if (value == null) {
return null; return null;
} }
if (value.contains("path")) { if (value.contains("uri") || value.contains("path")) {
JSONObject pictureJson = new JSONObject(value); JSONObject json = new JSONObject(value);
if (pictureJson.has("path")) { if (json.has("uri")) {
String path = pictureJson.getString("path"); return Uri.parse(json.getString("uri"));
}
if (json.has("path")) {
String path = json.getString("path");
return Uri.fromFile(new File(path)); return Uri.fromFile(new File(path));
} }
} }

@ -1,9 +1,17 @@
package org.tasks.files; package org.tasks.files;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
public class FileHelper { public class FileHelper {
public static File getExternalFilesDir(Context context, String type) { public static File getExternalFilesDir(Context context, String type) {
@ -14,4 +22,26 @@ public class FileHelper {
return null; return null;
} }
public static String getPathFromUri(Activity activity, Uri uri) {
String[] projection = {MediaStore.Images.Media.DATA};
Cursor cursor = activity.managedQuery(uri, projection, null, null, null);
if (cursor != null) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
return cursor.getString(column_index);
} else {
return uri.getPath();
}
}
public static void copyFile(String from, String to) throws IOException {
FileChannel source = new FileInputStream(from).getChannel();
FileChannel destination = new FileOutputStream(to).getChannel();
destination.transferFrom(source, 0, source.size());
destination.close();
source.close();
}
} }

@ -0,0 +1,42 @@
package org.tasks.files;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
public class ImageHelper {
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
public static Bitmap sampleBitmap(String path, int reqWidth, int reqHeight) {
// First decode with inJustDecodeBounds=true to check dimensions
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
}

@ -10,15 +10,12 @@ import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import com.todoroo.andlib.utility.AndroidUtilities;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -29,6 +26,8 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import static org.tasks.files.FileHelper.getPathFromUri;
public class ActFmCameraModule { public class ActFmCameraModule {
private static final Logger log = LoggerFactory.getLogger(ActFmCameraModule.class); private static final Logger log = LoggerFactory.getLogger(ActFmCameraModule.class);
@ -103,57 +102,28 @@ public class ActFmCameraModule {
} }
public interface CameraResultCallback { public interface CameraResultCallback {
public void handleCameraResult(Bitmap bitmap); public void handleCameraResult(Uri uri);
}
private static Bitmap bitmapFromUri(Activity activity, Uri uri) {
String[] projection = { MediaStore.Images.Media.DATA };
Cursor cursor = activity.managedQuery(uri, projection, null, null, null);
String path;
if(cursor != null) {
int column_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
path = cursor.getString(column_index);
} else {
path = uri.getPath();
}
return AndroidUtilities.readScaledBitmap(path);
} }
public static boolean activityResult(Activity activity, int requestCode, int resultCode, Intent data, public static boolean activityResult(Activity activity, int requestCode, int resultCode, Intent data,
CameraResultCallback cameraResult) { CameraResultCallback cameraResult) {
if(requestCode == ActFmCameraModule.REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) { if(requestCode == ActFmCameraModule.REQUEST_CODE_CAMERA && resultCode == Activity.RESULT_OK) {
Bitmap bitmap; if (lastTempFile != null) {
if (data == null) { // large from camera Uri uri = Uri.fromFile(lastTempFile);
if (lastTempFile != null) { lastTempFile = null;
bitmap = bitmapFromUri(activity, Uri.fromFile(lastTempFile));
lastTempFile.deleteOnExit();
lastTempFile = null;
}
else {
bitmap = null;
}
} else {
bitmap = data.getParcelableExtra("data"); //$NON-NLS-1$
}
if(bitmap != null) {
activity.setResult(Activity.RESULT_OK); activity.setResult(Activity.RESULT_OK);
cameraResult.handleCameraResult(bitmap); cameraResult.handleCameraResult(uri);
} }
return true; return true;
} else if(requestCode == ActFmCameraModule.REQUEST_CODE_PICTURE && resultCode == Activity.RESULT_OK) { } else if(requestCode == ActFmCameraModule.REQUEST_CODE_PICTURE && resultCode == Activity.RESULT_OK) {
Uri uri = data.getData(); Uri uri = data.getData();
Bitmap bitmap = bitmapFromUri(activity, uri); String path = getPathFromUri(activity, uri);
if(bitmap != null) { if (new File(path).exists()) {
activity.setResult(Activity.RESULT_OK); activity.setResult(Activity.RESULT_OK);
cameraResult.handleCameraResult(bitmap); cameraResult.handleCameraResult(uri);
} }
return true; return true;
} }
return false; return false;
} }
} }

@ -8,7 +8,7 @@ package com.todoroo.astrid.actfm;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.text.TextUtils; import android.text.TextUtils;
@ -229,7 +229,7 @@ public class TagSettingsActivity extends InjectingActionBarActivity {
protected void onActivityResult(int requestCode, int resultCode, Intent data) { protected void onActivityResult(int requestCode, int resultCode, Intent data) {
CameraResultCallback callback = new CameraResultCallback() { CameraResultCallback callback = new CameraResultCallback() {
@Override @Override
public void handleCameraResult(Bitmap bitmap) { public void handleCameraResult(Uri uri) {
log.error("Not expecting this"); log.error("Not expecting this");
} }
}; };

@ -12,7 +12,7 @@ import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
@ -90,7 +90,6 @@ import org.tasks.notifications.NotificationManager;
import org.tasks.preferences.ActivityPreferences; import org.tasks.preferences.ActivityPreferences;
import java.io.File; import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -99,6 +98,9 @@ import java.util.concurrent.atomic.AtomicReference;
import javax.inject.Inject; import javax.inject.Inject;
import static org.tasks.files.FileHelper.copyFile;
import static org.tasks.files.FileHelper.getPathFromUri;
/** /**
* This activity is responsible for creating new tasks and editing existing * This activity is responsible for creating new tasks and editing existing
* ones. It saves a task when it is paused (screen rotated, back button pressed) * ones. It saves a task when it is paused (screen rotated, back button pressed)
@ -882,18 +884,13 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
createNewFileAttachment(path, name, type); createNewFileAttachment(path, name, type);
} }
private void attachImage(Bitmap bitmap) { private void attachImage(String input) {
AtomicReference<String> nameRef = new AtomicReference<>(); AtomicReference<String> nameRef = new AtomicReference<>();
String path = FileUtilities.getNewImageAttachmentPath(preferences, getActivity(), nameRef);
try { try {
FileOutputStream fos = new FileOutputStream(path); String path = FileUtilities.getNewImageAttachmentPath(preferences, getActivity(), nameRef);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos); copyFile(input, path);
fos.flush(); String extension = path.substring(path.lastIndexOf('.') + 1);
fos.close(); createNewFileAttachment(path, nameRef.get(), TaskAttachment.FILE_TYPE_IMAGE + extension);
createNewFileAttachment(path, nameRef.get(), TaskAttachment.FILE_TYPE_IMAGE + "png");
} catch (Exception e) { } catch (Exception e) {
log.error(e.getMessage(), e); log.error(e.getMessage(), e);
Toast.makeText(getActivity(), R.string.file_err_copy, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), R.string.file_err_copy, Toast.LENGTH_LONG).show();
@ -997,8 +994,8 @@ ViewPager.OnPageChangeListener, EditNoteActivity.UpdatesChangedListener {
ActFmCameraModule.activityResult(getActivity(), requestCode, resultCode, data, new CameraResultCallback() { ActFmCameraModule.activityResult(getActivity(), requestCode, resultCode, data, new CameraResultCallback() {
@Override @Override
public void handleCameraResult(Bitmap bitmap) { public void handleCameraResult(Uri uri) {
attachImage(bitmap); attachImage(getPathFromUri(getActivity(), uri));
} }
}); });

@ -32,6 +32,9 @@ import com.todoroo.astrid.data.UserActivity;
import org.tasks.R; import org.tasks.R;
import static org.tasks.files.FileHelper.getPathFromUri;
import static org.tasks.files.ImageHelper.sampleBitmap;
/** /**
* Adapter for displaying a user's activity * Adapter for displaying a user's activity
* *
@ -177,7 +180,8 @@ public class UpdateAdapter extends CursorAdapter {
final Fragment fragment) { final Fragment fragment) {
if (updateBitmap != null) { //$NON-NLS-1$ if (updateBitmap != null) { //$NON-NLS-1$
commentPictureView.setVisibility(View.VISIBLE); commentPictureView.setVisibility(View.VISIBLE);
commentPictureView.setImageURI(updateBitmap); String path = getPathFromUri(fragment.getActivity(), updateBitmap);
commentPictureView.setImageBitmap(sampleBitmap(path, commentPictureView.getLayoutParams().width, commentPictureView.getLayoutParams().height));
view.setOnClickListener(new OnClickListener() { view.setOnClickListener(new OnClickListener() {
@Override @Override

@ -8,7 +8,6 @@ package com.todoroo.astrid.notes;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.net.Uri; import android.net.Uri;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
@ -64,6 +63,8 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
import static org.tasks.date.DateTimeUtils.newDate; import static org.tasks.date.DateTimeUtils.newDate;
import static org.tasks.files.FileHelper.getPathFromUri;
import static org.tasks.files.ImageHelper.sampleBitmap;
public class EditNoteActivity extends LinearLayout implements TimerActionListener { public class EditNoteActivity extends LinearLayout implements TimerActionListener {
@ -80,7 +81,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
private View commentButton; private View commentButton;
private int commentItems = 10; private int commentItems = 10;
private ImageButton pictureButton; private ImageButton pictureButton;
private Bitmap pendingCommentPicture = null; private Uri pendingCommentPicture = null;
private final Fragment fragment; private final Fragment fragment;
private final AstridActivity activity; private final AstridActivity activity;
@ -248,10 +249,10 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
if (activity != null) { if (activity != null) {
Bitmap bitmap = activity.getIntent().getParcelableExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS); String uri = activity.getIntent().getStringExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS);
if (bitmap != null) { if (uri != null) {
pendingCommentPicture = bitmap; pendingCommentPicture = Uri.parse(uri);
pictureButton.setImageBitmap(pendingCommentPicture); setPictureButtonToPendingPicture();
} }
} }
} }
@ -379,7 +380,7 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
userActivity.setTargetName(title); userActivity.setTargetName(title);
userActivity.setCreatedAt(DateUtilities.now()); userActivity.setCreatedAt(DateUtilities.now());
if (usePicture && pendingCommentPicture != null) { if (usePicture && pendingCommentPicture != null) {
JSONObject pictureJson = RemoteModel.PictureHelper.savePictureJson(activity, pendingCommentPicture); JSONObject pictureJson = RemoteModel.PictureHelper.savePictureJson(pendingCommentPicture);
if (pictureJson != null) { if (pictureJson != null) {
userActivity.setPicture(pictureJson.toString()); userActivity.setPicture(pictureJson.toString());
} }
@ -485,12 +486,12 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
respondToPicture = false; respondToPicture = false;
CameraResultCallback callback = new CameraResultCallback() { CameraResultCallback callback = new CameraResultCallback() {
@Override @Override
public void handleCameraResult(Bitmap bitmap) { public void handleCameraResult(Uri uri) {
if (activity != null) { if (activity != null) {
activity.getIntent().putExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS, bitmap); activity.getIntent().putExtra(TaskEditFragment.TOKEN_PICTURE_IN_PROGRESS, uri.toString());
} }
pendingCommentPicture = bitmap; pendingCommentPicture = uri;
pictureButton.setImageBitmap(pendingCommentPicture); setPictureButtonToPendingPicture();
commentField.requestFocus(); commentField.requestFocus();
} }
}; };
@ -502,4 +503,8 @@ public class EditNoteActivity extends LinearLayout implements TimerActionListene
} }
} }
private void setPictureButtonToPendingPicture() {
String path = getPathFromUri(activity, pendingCommentPicture);
pictureButton.setImageBitmap(sampleBitmap(path, pictureButton.getWidth(), pictureButton.getHeight()));
}
} }

Loading…
Cancel
Save