summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/ListPreference.java2
-rw-r--r--src/com/android/camera/PhotoMenu.java67
-rw-r--r--src/com/android/camera/PieController.java55
-rw-r--r--src/com/android/camera/SoundClips.java9
-rw-r--r--src/com/android/gallery3d/app/GalleryActionBar.java5
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java34
-rw-r--r--src/com/android/gallery3d/filtershow/FilterShowActivity.java62
-rw-r--r--src/com/android/gallery3d/filtershow/PanelController.java1
-rw-r--r--src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java1
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropActivity.java565
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java13
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropLoader.java144
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropMath.java30
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropObject.java2
-rw-r--r--src/com/android/gallery3d/filtershow/crop/CropView.java244
-rw-r--r--src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java6
-rw-r--r--src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java43
-rw-r--r--src/com/android/gallery3d/filtershow/imageshow/ImageShow.java56
-rw-r--r--src/com/android/gallery3d/filtershow/ui/ImageCurves.java2
19 files changed, 934 insertions, 407 deletions
diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java
index 9ea2e34ec..d1d34911b 100644
--- a/src/com/android/camera/ListPreference.java
+++ b/src/com/android/camera/ListPreference.java
@@ -70,7 +70,7 @@ public class ListPreference extends CameraPreference {
setEntryValues(a.getTextArray(
R.styleable.ListPreference_entryValues));
setLabels(a.getTextArray(
- R.styleable.ListPreference_labels));
+ R.styleable.ListPreference_labelList));
a.recycle();
}
diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java
index 77f2fc508..31fe670ff 100644
--- a/src/com/android/camera/PhotoMenu.java
+++ b/src/com/android/camera/PhotoMenu.java
@@ -41,8 +41,10 @@ public class PhotoMenu extends PieController
private static final int POS_MORE = 2;
private static final int POS_FLASH = 3;
private static final int POS_SWITCH = 4;
- private static final int POS_WB = 1;
+ private static final int POS_LOCATION = 1;
+ private static final int POS_WB = 3;
private static final int POS_SET = 2;
+ private static final int POS_SCENE = 4;
private final String mSettingOff;
@@ -81,61 +83,31 @@ public class PhotoMenu extends PieController
}
// camera switcher
if (group.findPreference(CameraSettings.KEY_CAMERA_ID) != null) {
- item = makeItem(R.drawable.ic_switch_back);
- item.setPosition(POS_SWITCH, 5);
- IconListPreference lpref = (IconListPreference) group.findPreference(
- CameraSettings.KEY_CAMERA_ID);
- item.setImageResource(mActivity,
- ((IconListPreference) lpref).getIconIds()
- [lpref.findIndexOfValue(lpref.getValue())]);
- item.setLabel(lpref.getLabel());
+ item = makeSwitchItem(CameraSettings.KEY_CAMERA_ID, POS_SWITCH, 5, false);
final PieItem fitem = item;
item.setOnClickListener(new OnClickListener() {
@Override
public void onClick(PieItem item) {
// Find the index of next camera.
- ListPreference camPref = mPreferenceGroup
+ ListPreference pref = mPreferenceGroup
.findPreference(CameraSettings.KEY_CAMERA_ID);
- if (camPref != null) {
- int index = camPref.findIndexOfValue(camPref.getValue());
- CharSequence[] values = camPref.getEntryValues();
+ if (pref != null) {
+ int index = pref.findIndexOfValue(pref.getValue());
+ CharSequence[] values = pref.getEntryValues();
index = (index + 1) % values.length;
- int newCameraId = Integer
- .parseInt((String) values[index]);
- fitem.setLabel(camPref.getLabel());
- fitem.setImageResource(mActivity,
- ((IconListPreference) camPref).getIconIds()[index]);
- mListener.onCameraPickerClicked(newCameraId);
+ pref.setValueIndex(index);
+ mListener.onCameraPickerClicked(index);
}
+ updateItem(fitem, CameraSettings.KEY_CAMERA_ID);
}
});
mRenderer.addItem(item);
}
// hdr
if (group.findPreference(CameraSettings.KEY_CAMERA_HDR) != null) {
- ListPreference lp = group.findPreference(CameraSettings.KEY_CAMERA_HDR);
- item = makeItem(R.drawable.ic_hdr);
- item.setLabel(lp.getLabel());
- item.setPosition(POS_HDR, 5);
- final PieItem fitem = item;
- item.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(PieItem item) {
- // Find the index of next camera.
- ListPreference pref = mPreferenceGroup
- .findPreference(CameraSettings.KEY_CAMERA_HDR);
- if (pref != null) {
- // toggle hdr value
- int index = (pref.findIndexOfValue(pref.getValue()) + 1) % 2;
- pref.setValueIndex(index);
- onSettingChanged(pref);
- fitem.setLabel(pref.getLabel());
- }
- }
- });
+ item = makeSwitchItem(CameraSettings.KEY_CAMERA_HDR, POS_HDR, 5, true);
mRenderer.addItem(item);
}
-
// more settings
PieItem more = makeItem(R.drawable.ic_settings_holo_light);
more.setPosition(POS_MORE, 5);
@@ -147,10 +119,21 @@ public class PhotoMenu extends PieController
item.setLabel(res.getString(R.string.pref_camera_whitebalance_label));
more.addItem(item);
}
+ // location
+ if (group.findPreference(CameraSettings.KEY_RECORD_LOCATION) != null) {
+ item = makeSwitchItem(CameraSettings.KEY_RECORD_LOCATION, POS_LOCATION, 5, true);
+ more.addItem(item);
+ }
+ // scene mode
+ if (group.findPreference(CameraSettings.KEY_SCENE_MODE) != null) {
+ IconListPreference pref = (IconListPreference) group.findPreference(
+ CameraSettings.KEY_SCENE_MODE);
+ pref.setUseSingleIcon(true);
+ item = makeItem(CameraSettings.KEY_SCENE_MODE, POS_SCENE, 5);
+ more.addItem(item);
+ }
// settings popup
mOtherKeys = new String[] {
- CameraSettings.KEY_SCENE_MODE,
- CameraSettings.KEY_RECORD_LOCATION,
CameraSettings.KEY_PICTURE_SIZE,
CameraSettings.KEY_FOCUS_MODE,
CameraSettings.KEY_TIMER,
diff --git a/src/com/android/camera/PieController.java b/src/com/android/camera/PieController.java
index 5ccab5e72..565a20ed1 100644
--- a/src/com/android/camera/PieController.java
+++ b/src/com/android/camera/PieController.java
@@ -133,6 +133,50 @@ public class PieController {
return item;
}
+ public PieItem makeSwitchItem(final String prefKey, int position, int count,
+ boolean addListener) {
+ final IconListPreference pref =
+ (IconListPreference) mPreferenceGroup.findPreference(prefKey);
+ if (pref == null) return null;
+ int[] iconIds = pref.getLargeIconIds();
+ int resid = -1;
+ int index = pref.findIndexOfValue(pref.getValue());
+ if (!pref.getUseSingleIcon() && iconIds != null) {
+ // Each entry has a corresponding icon.
+ resid = iconIds[index];
+ } else {
+ // The preference only has a single icon to represent it.
+ resid = pref.getSingleIcon();
+ }
+ PieItem item = makeItem(resid);
+ item.setPosition(position, count);
+ item.setLabel(pref.getLabels()[index]);
+ item.setImageResource(mActivity, resid);
+ mPreferences.add(pref);
+ mPreferenceMap.put(pref, item);
+ if (addListener) {
+ final PieItem fitem = item;
+ item.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(PieItem item) {
+ IconListPreference pref = (IconListPreference) mPreferenceGroup
+ .findPreference(prefKey);
+ int index = pref.findIndexOfValue(pref.getValue());
+ CharSequence[] values = pref.getEntryValues();
+ index = (index + 1) % values.length;
+ pref.setValueIndex(index);
+ fitem.setLabel(pref.getLabels()[index]);
+ fitem.setImageResource(mActivity,
+ ((IconListPreference) pref).getLargeIconIds()[index]);
+ reloadPreference(pref);
+ onSettingChanged(pref);
+ }
+ });
+ }
+ return item;
+ }
+
+
public PieItem makeDialItem(ListPreference pref, int iconId, float center, float sweep) {
PieItem item = makeItem(iconId);
return item;
@@ -143,6 +187,17 @@ public class PieController {
mRenderer.addItem(item);
}
+ public void updateItem(PieItem item, String prefKey) {
+ IconListPreference pref = (IconListPreference) mPreferenceGroup
+ .findPreference(prefKey);
+ if (pref != null) {
+ int index = pref.findIndexOfValue(pref.getValue());
+ item.setLabel(pref.getLabels()[index]);
+ item.setImageResource(mActivity,
+ ((IconListPreference) pref).getLargeIconIds()[index]);
+ }
+ }
+
public void setPreferenceGroup(PreferenceGroup group) {
mPreferenceGroup = group;
}
diff --git a/src/com/android/camera/SoundClips.java b/src/com/android/camera/SoundClips.java
index 6388fec56..8155c03dc 100644
--- a/src/com/android/camera/SoundClips.java
+++ b/src/com/android/camera/SoundClips.java
@@ -48,6 +48,11 @@ public class SoundClips {
}
}
+ public static int getAudioTypeForSoundPool() {
+ return ApiHelper.getIntFieldIfExists(AudioManager.class,
+ "STREAM_SYSTEM_ENFORCED", null, AudioManager.STREAM_RING);
+ }
+
/**
* This class implements SoundClips.Player using MediaActionSound,
* which exists since API level 16.
@@ -121,12 +126,10 @@ public class SoundClips {
public SoundPoolPlayer(Context context) {
mContext = context;
- int audioType = ApiHelper.getIntFieldIfExists(AudioManager.class,
- "STREAM_SYSTEM_ENFORCED", null, AudioManager.STREAM_RING);
mSoundIDToPlay = ID_NOT_LOADED;
- mSoundPool = new SoundPool(NUM_SOUND_STREAMS, audioType, 0);
+ mSoundPool = new SoundPool(NUM_SOUND_STREAMS, getAudioTypeForSoundPool(), 0);
mSoundPool.setOnLoadCompleteListener(this);
mSoundIDs = new int[SOUND_RES.length];
diff --git a/src/com/android/gallery3d/app/GalleryActionBar.java b/src/com/android/gallery3d/app/GalleryActionBar.java
index 0fb5e51b4..588f5842a 100644
--- a/src/com/android/gallery3d/app/GalleryActionBar.java
+++ b/src/com/android/gallery3d/app/GalleryActionBar.java
@@ -422,7 +422,8 @@ public class GalleryActionBar implements OnNavigationListener {
return mActionBarMenu;
}
- public void setShareIntents(Intent sharePanoramaIntent, Intent shareIntent) {
+ public void setShareIntents(Intent sharePanoramaIntent, Intent shareIntent,
+ ShareActionProvider.OnShareTargetSelectedListener onShareListener) {
mSharePanoramaIntent = sharePanoramaIntent;
if (mSharePanoramaActionProvider != null) {
mSharePanoramaActionProvider.setShareIntent(sharePanoramaIntent);
@@ -430,6 +431,8 @@ public class GalleryActionBar implements OnNavigationListener {
mShareIntent = shareIntent;
if (mShareActionProvider != null) {
mShareActionProvider.setShareIntent(shareIntent);
+ mShareActionProvider.setOnShareTargetSelectedListener(
+ onShareListener);
}
}
}
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 9b6f2b961..613ac17eb 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -36,6 +36,7 @@ import android.os.SystemClock;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.RelativeLayout;
+import android.widget.ShareActionProvider;
import android.widget.Toast;
import com.android.camera.CameraActivity;
@@ -59,6 +60,7 @@ import com.android.gallery3d.data.SnailAlbum;
import com.android.gallery3d.data.SnailItem;
import com.android.gallery3d.data.SnailSource;
import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.crop.CropActivity;
import com.android.gallery3d.picasasource.PicasaSource;
import com.android.gallery3d.ui.DetailsHelper;
import com.android.gallery3d.ui.DetailsHelper.CloseListener;
@@ -72,7 +74,7 @@ import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.UsageStatistics;
public abstract class PhotoPage extends ActivityState implements
- PhotoView.Listener, AppBridge.Server,
+ PhotoView.Listener, AppBridge.Server, ShareActionProvider.OnShareTargetSelectedListener,
PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener {
private static final String TAG = "PhotoPage";
@@ -391,7 +393,7 @@ public abstract class PhotoPage extends ActivityState implements
}
Intent shareIntent = createShareIntent(mCurrentPhoto);
- mActionBar.setShareIntents(panoramaIntent, shareIntent);
+ mActionBar.setShareIntents(panoramaIntent, shareIntent, PhotoPage.this);
setNfcBeamPushUri(contentUri);
}
break;
@@ -1081,8 +1083,8 @@ public abstract class PhotoPage extends ActivityState implements
}
case R.id.action_crop: {
Activity activity = mActivity;
- Intent intent = new Intent(FilterShowActivity.CROP_ACTION);
- intent.setClass(activity, FilterShowActivity.class);
+ Intent intent = new Intent(CropActivity.CROP_ACTION);
+ intent.setClass(activity, CropActivity.class);
intent.setDataAndType(manager.getContentUri(path), current.getMimeType())
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivityForResult(intent, PicasaSource.isPicasaImage(current)
@@ -1540,4 +1542,28 @@ public abstract class PhotoPage extends ActivityState implements
public void onUndoBarVisibilityChanged(boolean visible) {
refreshBottomControlsWhenReady();
}
+
+ @Override
+ public boolean onShareTargetSelected(ShareActionProvider source, Intent intent) {
+ final long timestampMillis = mCurrentPhoto.getDateInMs();
+ final String mediaType = getMediaTypeString(mCurrentPhoto);
+ UsageStatistics.onEvent(UsageStatistics.COMPONENT_GALLERY,
+ UsageStatistics.ACTION_SHARE,
+ mediaType,
+ timestampMillis > 0
+ ? System.currentTimeMillis() - timestampMillis
+ : -1);
+ return false;
+ }
+
+ private static String getMediaTypeString(MediaItem item) {
+ if (item.getMediaType() == MediaObject.MEDIA_TYPE_VIDEO) {
+ return "Video";
+ } else if (item.getMediaType() == MediaObject.MEDIA_TYPE_IMAGE) {
+ return "Photo";
+ } else {
+ return "Unknown:" + item.getMediaType();
+ }
+ }
+
}
diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
index 4133d8915..fb8984989 100644
--- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java
+++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java
@@ -119,7 +119,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
private static final int SELECT_PICTURE = 1;
private static final String LOGTAG = "FilterShowActivity";
protected static final boolean ANIMATE_PANELS = true;
- private static int mImageBorderSize = 4; // in percent
private boolean mShowingTinyPlanet = false;
private boolean mShowingImageStatePanel = false;
@@ -398,27 +397,10 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
borders.add(new FilterImageBorderRepresentation(0));
// Google-build borders
- FiltersManager.getManager().addBorders(borders);
-
- // Regular borders
- borders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_4x5));
- borders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_brush));
- borders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_grunge));
- borders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_sumi_e));
- borders.add(new FilterImageBorderRepresentation(R.drawable.filtershow_border_tape));
- borders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize, 0));
- borders.add(new FilterColorBorderRepresentation(Color.BLACK, mImageBorderSize,
- mImageBorderSize));
- borders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize, 0));
- borders.add(new FilterColorBorderRepresentation(Color.WHITE, mImageBorderSize,
- mImageBorderSize));
- int creamColor = Color.argb(255, 237, 237, 227);
- borders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize, 0));
- borders.add(new FilterColorBorderRepresentation(creamColor, mImageBorderSize,
- mImageBorderSize));
+ FiltersManager.getManager().addBorders(this, borders);
+
for (int i = 0; i < borders.size(); i++) {
FilterRepresentation filter = borders.elementAt(i);
- filter.setName(getString(R.string.borders));
if (i == 0) {
filter.setName(getString(R.string.none));
}
@@ -738,41 +720,6 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
}
private void fillFx(LinearLayout listFilters, int buttonId) {
- // TODO: use listview
- // TODO: load the filters straight from the filesystem
-
- FilterFxRepresentation[] fxArray = new FilterFxRepresentation[18];
- int p = 0;
-
- int[] drawid = {
- R.drawable.filtershow_fx_0005_punch,
- R.drawable.filtershow_fx_0000_vintage,
- R.drawable.filtershow_fx_0004_bw_contrast,
- R.drawable.filtershow_fx_0002_bleach,
- R.drawable.filtershow_fx_0001_instant,
- R.drawable.filtershow_fx_0007_washout,
- R.drawable.filtershow_fx_0003_blue_crush,
- R.drawable.filtershow_fx_0008_washout_color,
- R.drawable.filtershow_fx_0006_x_process
- };
-
- int[] fxNameid = {
- R.string.ffx_punch,
- R.string.ffx_vintage,
- R.string.ffx_bw_contrast,
- R.string.ffx_bleach,
- R.string.ffx_instant,
- R.string.ffx_washout,
- R.string.ffx_blue_crush,
- R.string.ffx_washout_color,
- R.string.ffx_x_process
- };
-
- for (int i = 0; i < drawid.length; i++) {
- FilterFxRepresentation fx = new FilterFxRepresentation(getString(fxNameid[i]), drawid[i], fxNameid[i]);
- fxArray[p++] = fx;
- }
-
ImageButton button = (ImageButton) findViewById(buttonId);
FilterFxRepresentation nullFx = new FilterFxRepresentation(getString(R.string.none), 0, R.string.none);
@@ -780,14 +727,11 @@ public class FilterShowActivity extends FragmentActivity implements OnItemClickL
mNullFxFilter.setSelected(true);
Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
- FiltersManager.getManager().addLooks(filtersRepresentations);
+ FiltersManager.getManager().addLooks(this, filtersRepresentations);
for (FilterRepresentation representation : filtersRepresentations) {
setupFilterRepresentationButton(representation, listFilters, button);
}
- for (int i = 0; i < p; i++) {
- setupFilterRepresentationButton(fxArray[i], listFilters, button);
- }
}
public void setDefaultPreset() {
diff --git a/src/com/android/gallery3d/filtershow/PanelController.java b/src/com/android/gallery3d/filtershow/PanelController.java
index e9a913610..2cd70e3aa 100644
--- a/src/com/android/gallery3d/filtershow/PanelController.java
+++ b/src/com/android/gallery3d/filtershow/PanelController.java
@@ -598,6 +598,7 @@ public class PanelController implements OnClickListener {
mCurrentEditor = null;
FilterIconButton component = (FilterIconButton) view;
FilterRepresentation representation = component.getFilterRepresentation();
+
if (representation != null) {
mUtilityPanel.setEffectName(representation.getName());
mUtilityPanel.setShowParameter(representation.showParameterValue());
diff --git a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
index e63323b92..8202d71bb 100644
--- a/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
+++ b/src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
@@ -151,7 +151,6 @@ public class FilteringPipeline implements Handler.Callback {
Log.e(LOGTAG, "setOriginal called after pipeline initialization!");
return;
}
- Log.v(LOGTAG,"setOriginal, size " + bitmap.getWidth() + " x " + bitmap.getHeight());
mAccessoryPipeline.setOriginal(bitmap);
mPreviewPipeline.setOriginal(bitmap);
mHighresPreviewPipeline.setOriginal(bitmap);
diff --git a/src/com/android/gallery3d/filtershow/crop/CropActivity.java b/src/com/android/gallery3d/filtershow/crop/CropActivity.java
index 26659a600..3429e0baf 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropActivity.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropActivity.java
@@ -21,11 +21,11 @@ import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
-import android.graphics.Point;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.net.Uri;
@@ -34,8 +34,6 @@ import android.os.Bundle;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
-import android.view.Display;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
@@ -43,8 +41,11 @@ import android.widget.Toast;
import com.android.gallery3d.R;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.InputStream;
import java.io.OutputStream;
/**
@@ -52,25 +53,35 @@ import java.io.OutputStream;
*/
public class CropActivity extends Activity {
private static final String LOGTAG = "CropActivity";
+ public static final String CROP_ACTION = "com.android.camera.action.CROP";
private CropExtras mCropExtras = null;
private LoadBitmapTask mLoadBitmapTask = null;
- private SaveBitmapTask mSaveBitmapTask = null;
- private SetWallpaperTask mSetWallpaperTask = null;
+
private Bitmap mOriginalBitmap = null;
+ private RectF mOriginalBounds = null;
+ private int mOriginalRotation = 0;
+ private Uri mSourceUri = null;
private CropView mCropView = null;
- private int mActiveBackgroundIO = 0;
- private Intent mResultIntent = null;
+ private View mSaveButton = null;
+ private boolean finalIOGuard = false;
+
private static final int SELECT_PICTURE = 1; // request code for picker
- private static final int DEFAULT_DENSITY = 133;
+
private static final int DEFAULT_COMPRESS_QUALITY = 90;
public static final int MAX_BMAP_IN_INTENT = 990000;
+ // Flags
+ private static final int DO_SET_WALLPAPER = 1;
+ private static final int DO_RETURN_DATA = 1 << 1;
+ private static final int DO_EXTRA_OUTPUT = 1 << 2;
+
+ private static final int FLAG_CHECK = DO_SET_WALLPAPER | DO_RETURN_DATA | DO_EXTRA_OUTPUT;
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
- mResultIntent = new Intent();
- setResult(RESULT_CANCELED, mResultIntent);
+ setResult(RESULT_CANCELED, new Intent());
mCropExtras = getExtrasFromIntent(intent);
if (mCropExtras != null && mCropExtras.getShowWhenLocked()) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
@@ -79,22 +90,30 @@ public class CropActivity extends Activity {
setContentView(R.layout.crop_activity);
mCropView = (CropView) findViewById(R.id.cropView);
- if (intent.getData() != null) {
- startLoadBitmap(intent.getData());
- } else {
- pickImage();
- }
ActionBar actionBar = getActionBar();
actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
actionBar.setCustomView(R.layout.filtershow_actionbar);
- View saveButton = actionBar.getCustomView();
- saveButton.setOnClickListener(new OnClickListener() {
+ View mSaveButton = actionBar.getCustomView();
+ mSaveButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
startFinishOutput();
}
});
+
+ if (intent.getData() != null) {
+ mSourceUri = intent.getData();
+ startLoadBitmap(mSourceUri);
+ } else {
+ pickImage();
+ }
+ }
+
+ private void enableSave(boolean enable) {
+ if (mSaveButton != null) {
+ mSaveButton.setEnabled(enable);
+ }
}
@Override
@@ -109,7 +128,7 @@ public class CropActivity extends Activity {
* Opens a selector in Gallery to chose an image for use when none was given
* in the CROP intent.
*/
- public void pickImage() {
+ private void pickImage() {
Intent intent = new Intent();
intent.setType("image/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
@@ -123,74 +142,65 @@ public class CropActivity extends Activity {
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK && requestCode == SELECT_PICTURE) {
- Uri selectedImageUri = data.getData();
- startLoadBitmap(selectedImageUri);
- }
- }
-
- /**
- * Gets the crop extras from the intent, or null if none exist.
- */
- public static CropExtras getExtrasFromIntent(Intent intent) {
- Bundle extras = intent.getExtras();
- if (extras != null) {
- return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
- extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
- extras.getBoolean(CropExtras.KEY_SCALE, true) &&
- extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
- extras.getInt(CropExtras.KEY_ASPECT_X, 0),
- extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
- extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
- extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
- (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
- extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
- extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
- extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
- extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
+ mSourceUri = data.getData();
+ startLoadBitmap(mSourceUri);
}
- return null;
}
/**
* Gets screen size metric.
*/
private int getScreenImageSize() {
- DisplayMetrics metrics = new DisplayMetrics();
- Display display = getWindowManager().getDefaultDisplay();
- Point size = new Point();
- display.getSize(size);
- display.getMetrics(metrics);
- int msize = Math.min(size.x, size.y);
- // TODO: WTF
- return (DEFAULT_DENSITY * msize) / metrics.densityDpi + 512;
+ DisplayMetrics outMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
+ return (int) Math.max(outMetrics.heightPixels, outMetrics.widthPixels);
}
/**
* Method that loads a bitmap in an async task.
*/
private void startLoadBitmap(Uri uri) {
- mActiveBackgroundIO++;
- final View loading = findViewById(R.id.loading);
- loading.setVisibility(View.VISIBLE);
- mLoadBitmapTask = new LoadBitmapTask();
- mLoadBitmapTask.execute(uri);
+ if (uri != null) {
+ enableSave(false);
+ final View loading = findViewById(R.id.loading);
+ loading.setVisibility(View.VISIBLE);
+ mLoadBitmapTask = new LoadBitmapTask();
+ mLoadBitmapTask.execute(uri);
+ } else {
+ cannotLoadImage();
+ done();
+ }
}
/**
* Method called on UI thread with loaded bitmap.
*/
- private void doneLoadBitmap(Bitmap bitmap) {
- mActiveBackgroundIO--;
+ private void doneLoadBitmap(Bitmap bitmap, RectF bounds, int orientation) {
final View loading = findViewById(R.id.loading);
loading.setVisibility(View.GONE);
mOriginalBitmap = bitmap;
- // TODO: move these to dimens folder
- if (bitmap != null) {
- mCropView.setup(bitmap, (int) getPixelsFromDip(55), (int) getPixelsFromDip(25));
+ mOriginalBounds = bounds;
+ mOriginalRotation = orientation;
+ if (bitmap != null && bitmap.getWidth() != 0 && bitmap.getHeight() != 0) {
+ RectF imgBounds = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ mCropView.initialize(bitmap, imgBounds, imgBounds, orientation);
+ if (mCropExtras != null) {
+ int aspectX = mCropExtras.getAspectX();
+ int aspectY = mCropExtras.getAspectY();
+ int outputX = mCropExtras.getOutputX();
+ int outputY = mCropExtras.getOutputY();
+ if (outputX > 0 && outputY > 0) {
+ mCropView.applyAspect(outputX, outputY);
+ }
+ if (aspectX > 0 && aspectY > 0) {
+ mCropView.applyAspect(aspectX, aspectY);
+ }
+ }
+ enableSave(true);
} else {
Log.w(LOGTAG, "could not load image for cropping");
cannotLoadImage();
- setResult(RESULT_CANCELED, mResultIntent);
+ setResult(RESULT_CANCELED, new Intent());
done();
}
}
@@ -214,172 +224,328 @@ public class CropActivity extends Activity {
int mBitmapSize;
Context mContext;
Rect mOriginalBounds;
+ int mOrientation;
public LoadBitmapTask() {
mBitmapSize = getScreenImageSize();
- Log.v(LOGTAG, "bitmap size: " + mBitmapSize);
mContext = getApplicationContext();
mOriginalBounds = new Rect();
+ mOrientation = 0;
}
@Override
protected Bitmap doInBackground(Uri... params) {
- Bitmap bmap = CropLoader.getConstrainedBitmap(params[0], mContext, mBitmapSize,
+ Uri uri = params[0];
+ Bitmap bmap = CropLoader.getConstrainedBitmap(uri, mContext, mBitmapSize,
mOriginalBounds);
+ mOrientation = CropLoader.getMetadataRotation(uri, mContext);
return bmap;
}
@Override
protected void onPostExecute(Bitmap result) {
- doneLoadBitmap(result);
- // super.onPostExecute(result);
+ doneLoadBitmap(result, new RectF(mOriginalBounds), mOrientation);
}
}
- private void startSaveBitmap(Bitmap bmap, Uri uri, String format) {
- if (bmap == null || uri == null) {
- throw new IllegalArgumentException("bad argument to startSaveBitmap");
+ private void startFinishOutput() {
+ if (finalIOGuard) {
+ return;
+ } else {
+ finalIOGuard = true;
+ }
+ enableSave(false);
+ Uri destinationUri = null;
+ int flags = 0;
+ if (mOriginalBitmap != null && mCropExtras != null) {
+ if (mCropExtras.getExtraOutput() != null) {
+ destinationUri = mCropExtras.getExtraOutput();
+ flags |= DO_EXTRA_OUTPUT;
+ }
+ if (mCropExtras.getSetAsWallpaper()) {
+ flags |= DO_SET_WALLPAPER;
+ }
+ if (mCropExtras.getReturnData()) {
+ flags |= DO_RETURN_DATA;
+ }
}
- mActiveBackgroundIO++;
+ if (flags == 0) {
+ destinationUri = CropLoader.makeAndInsertUri(this, mSourceUri);
+ if (destinationUri != null) {
+ flags |= DO_EXTRA_OUTPUT;
+ }
+ }
+ if ((flags & FLAG_CHECK) != 0) {
+ RectF photo = new RectF(0, 0, mOriginalBitmap.getWidth(), mOriginalBitmap.getHeight());
+ RectF crop = getBitmapCrop(photo);
+ startBitmapIO(flags, mOriginalBitmap, mSourceUri, destinationUri, crop,
+ photo, mOriginalBounds,
+ (mCropExtras == null) ? null : mCropExtras.getOutputFormat(), mOriginalRotation);
+ return;
+ }
+ setResult(RESULT_CANCELED, new Intent());
+ done();
+ return;
+ }
+
+ private void startBitmapIO(int flags, Bitmap currentBitmap, Uri sourceUri, Uri destUri,
+ RectF cropBounds, RectF photoBounds, RectF currentBitmapBounds, String format,
+ int rotation) {
+ if (cropBounds == null || photoBounds == null || currentBitmap == null
+ || currentBitmap.getWidth() == 0 || currentBitmap.getHeight() == 0
+ || cropBounds.width() == 0 || cropBounds.height() == 0 || photoBounds.width() == 0
+ || photoBounds.height() == 0) {
+ return; // fail fast
+ }
+ if ((flags & FLAG_CHECK) == 0) {
+ return; // no output options
+ }
+ if ((flags & DO_SET_WALLPAPER) != 0) {
+ Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
+ }
+
final View loading = findViewById(R.id.loading);
loading.setVisibility(View.VISIBLE);
- mSaveBitmapTask = new SaveBitmapTask(uri, format);
- mSaveBitmapTask.execute(bmap);
+ BitmapIOTask ioTask = new BitmapIOTask(sourceUri, destUri, format, flags, cropBounds,
+ photoBounds, currentBitmapBounds, rotation);
+ ioTask.execute(currentBitmap);
}
- private void doneSaveBitmap(Uri uri) {
- mActiveBackgroundIO--;
+ private void doneBitmapIO(boolean success, Intent intent) {
final View loading = findViewById(R.id.loading);
loading.setVisibility(View.GONE);
- if (uri == null) {
- Log.w(LOGTAG, "failed to save bitmap");
- setResult(RESULT_CANCELED, mResultIntent);
- done();
- return;
+ if (success) {
+ setResult(RESULT_OK, intent);
+ } else {
+ setResult(RESULT_CANCELED, intent);
}
done();
}
- private class SaveBitmapTask extends AsyncTask<Bitmap, Void, Boolean> {
+ private class BitmapIOTask extends AsyncTask<Bitmap, Void, Boolean> {
+ private final WallpaperManager mWPManager;
+ InputStream mInStream = null;
OutputStream mOutStream = null;
String mOutputFormat = null;
Uri mOutUri = null;
-
- public SaveBitmapTask(Uri uri, String outputFormat) {
+ Uri mInUri = null;
+ int mFlags = 0;
+ RectF mCrop = null;
+ RectF mPhoto = null;
+ RectF mOrig = null;
+ Intent mResultIntent = null;
+ int mRotation = 0;
+
+ public BitmapIOTask(Uri sourceUri, Uri destUri, String outputFormat, int flags,
+ RectF cropBounds, RectF photoBounds, RectF originalBitmapBounds, int rotation) {
mOutputFormat = outputFormat;
mOutStream = null;
- mOutUri = uri;
- try {
- mOutStream = getContentResolver().openOutputStream(uri);
- } catch (FileNotFoundException e) {
- Log.w(LOGTAG, "cannot write output: " + mOutUri.toString(), e);
+ mOutUri = destUri;
+ mInUri = sourceUri;
+ mFlags = flags;
+ mCrop = cropBounds;
+ mPhoto = photoBounds;
+ mOrig = originalBitmapBounds;
+ mWPManager = WallpaperManager.getInstance(getApplicationContext());
+ mResultIntent = new Intent();
+ mRotation = (rotation < 0) ? -rotation : rotation;
+ mRotation %= 360;
+ mRotation = 90 * (int) (mRotation / 90); // now mRotation is a multiple of 90
+
+ if ((flags & DO_EXTRA_OUTPUT) != 0) {
+ if (mOutUri == null) {
+ Log.w(LOGTAG, "cannot write file, no output URI given");
+ } else {
+ try {
+ mOutStream = getContentResolver().openOutputStream(mOutUri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOGTAG, "cannot write file: " + mOutUri.toString(), e);
+ }
+ }
}
- }
- @Override
- protected Boolean doInBackground(Bitmap... params) {
- if (mOutStream == null) {
- return false;
+ if ((flags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0) {
+ if (mInUri == null) {
+ Log.w(LOGTAG, "cannot read original file, no input URI given");
+ } else {
+ try {
+ mInStream = getContentResolver().openInputStream(mInUri);
+ } catch (FileNotFoundException e) {
+ Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
+ }
+ }
}
- CompressFormat cf = convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
- return params[0].compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream);
}
@Override
- protected void onPostExecute(Boolean result) {
- if (result.booleanValue() == false) {
- Log.w(LOGTAG, "could not compress to output: " + mOutUri.toString());
- doneSaveBitmap(null);
+ protected Boolean doInBackground(Bitmap... params) {
+ boolean failure = false;
+ Bitmap img = params[0];
+
+ // Set extra for crop bounds
+ if (mCrop != null && mPhoto != null && mOrig != null) {
+ RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig);
+ Matrix m = new Matrix();
+ m.setRotate(mRotation);
+ m.mapRect(trueCrop);
+ if (trueCrop != null) {
+ Rect rounded = new Rect();
+ trueCrop.roundOut(rounded);
+ mResultIntent.putExtra(CropExtras.KEY_CROPPED_RECT, rounded);
+ }
}
- doneSaveBitmap(mOutUri);
- }
- }
-
- private void startSetWallpaper(Bitmap bmap) {
- if (bmap == null) {
- throw new IllegalArgumentException("bad argument to startSetWallpaper");
- }
- mActiveBackgroundIO++;
- Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
- mSetWallpaperTask = new SetWallpaperTask();
- mSetWallpaperTask.execute(bmap);
-
- }
-
- private void doneSetWallpaper() {
- mActiveBackgroundIO--;
- done();
- }
-
- private class SetWallpaperTask extends AsyncTask<Bitmap, Void, Boolean> {
- private final WallpaperManager mWPManager;
-
- public SetWallpaperTask() {
- mWPManager = WallpaperManager.getInstance(getApplicationContext());
- }
- @Override
- protected Boolean doInBackground(Bitmap... params) {
- try {
- mWPManager.setBitmap(params[0]);
- } catch (IOException e) {
- Log.w(LOGTAG, "fail to set wall paper", e);
+ // Find the small cropped bitmap that is returned in the intent
+ if ((mFlags & DO_RETURN_DATA) != 0) {
+ assert (img != null);
+ Bitmap ret = getCroppedImage(img, mCrop, mPhoto);
+ if (ret != null) {
+ ret = getDownsampledBitmap(ret, MAX_BMAP_IN_INTENT);
+ }
+ if (ret == null) {
+ Log.w(LOGTAG, "could not downsample bitmap to return in data");
+ failure = true;
+ } else {
+ if (mRotation > 0) {
+ Matrix m = new Matrix();
+ m.setRotate(mRotation);
+ Bitmap tmp = Bitmap.createBitmap(ret, 0, 0, ret.getWidth(),
+ ret.getHeight(), m, true);
+ if (tmp != null) {
+ ret = tmp;
+ }
+ }
+ mResultIntent.putExtra(CropExtras.KEY_DATA, ret);
+ }
}
- return true;
- }
- @Override
- protected void onPostExecute(Boolean result) {
- doneSetWallpaper();
- }
- }
+ // Do the large cropped bitmap and/or set the wallpaper
+ if ((mFlags & (DO_EXTRA_OUTPUT | DO_SET_WALLPAPER)) != 0 && mInStream != null) {
+ BitmapRegionDecoder decoder = null;
+ try {
+ decoder = BitmapRegionDecoder.newInstance(mInStream, true);
+ } catch (IOException e) {
+ Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+ }
+ if (decoder == null) {
+ failure = true;
+ return false;
+ }
- private void startFinishOutput() {
- if (mOriginalBitmap != null && mCropExtras != null) {
- Bitmap cropped = null;
- if (mCropExtras.getExtraOutput() != null) {
- if (cropped == null) {
- cropped = getCroppedImage(mOriginalBitmap);
+ // Find crop bounds (scaled to original image size)
+ RectF trueCrop = CropMath.getScaledCropBounds(mCrop, mPhoto, mOrig);
+ if (trueCrop == null) {
+ Log.w(LOGTAG, "cannot find crop for full size image");
+ failure = true;
+ return false;
}
- startSaveBitmap(cropped, mCropExtras.getExtraOutput(),
- mCropExtras.getOutputFormat());
- }
- if (mCropExtras.getSetAsWallpaper()) {
- if (cropped == null) {
- cropped = getCroppedImage(mOriginalBitmap);
+ Rect roundedTrueCrop = new Rect();
+ trueCrop.roundOut(roundedTrueCrop);
+
+ if (roundedTrueCrop.width() <= 0 || roundedTrueCrop.height() <= 0) {
+ Log.w(LOGTAG, "crop has bad values for full size image");
+ failure = true;
+ return false;
}
- startSetWallpaper(cropped);
- }
- if (mCropExtras.getReturnData()) {
- if (cropped == null) {
- cropped = getCroppedImage(mOriginalBitmap);
+ // Do region decoding to get crop bitmap
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inMutable = true;
+ Bitmap crop = decoder.decodeRegion(roundedTrueCrop, options);
+ decoder.recycle();
+
+ if (crop == null) {
+ Log.w(LOGTAG, "cannot region decode file: " + mInUri.toString());
+ failure = true;
+ return false;
+ }
+ if (mRotation > 0) {
+ Matrix m = new Matrix();
+ m.setRotate(mRotation);
+ Bitmap tmp = Bitmap.createBitmap(crop, 0, 0, crop.getWidth(),
+ crop.getHeight(), m, true);
+ if (tmp != null) {
+ crop = tmp;
+ }
}
- int bmapSize = cropped.getRowBytes() * cropped.getHeight();
- if (bmapSize > MAX_BMAP_IN_INTENT) {
- Log.w(LOGTAG, "Bitmap too large to be returned via intent");
+ // Get output compression format
+ CompressFormat cf =
+ convertExtensionToCompressFormat(getFileExtension(mOutputFormat));
+
+ // If we only need to output to a URI, compress straight to file
+ if (mFlags == DO_EXTRA_OUTPUT) {
+ if (mOutStream == null
+ || !crop.compress(cf, DEFAULT_COMPRESS_QUALITY, mOutStream)) {
+ Log.w(LOGTAG, "failed to compress bitmap to file: " + mOutUri.toString());
+ failure = true;
+ } else {
+ mResultIntent.setData(mOutUri);
+ }
} else {
- mResultIntent.putExtra(CropExtras.KEY_DATA, cropped);
+ // Compress to byte array
+ ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
+ if (crop.compress(cf, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
+
+ // If we need to output to a Uri, write compressed
+ // bitmap out
+ if ((mFlags & DO_EXTRA_OUTPUT) != 0) {
+ if (mOutStream == null) {
+ Log.w(LOGTAG,
+ "failed to compress bitmap to file: " + mOutUri.toString());
+ failure = true;
+ } else {
+ try {
+ mOutStream.write(tmpOut.toByteArray());
+ mResultIntent.setData(mOutUri);
+ } catch (IOException e) {
+ Log.w(LOGTAG,
+ "failed to compress bitmap to file: "
+ + mOutUri.toString(), e);
+ failure = true;
+ }
+ }
+ }
+
+ // If we need to set to the wallpaper, set it
+ if ((mFlags & DO_SET_WALLPAPER) != 0 && mWPManager != null) {
+ if (mWPManager == null) {
+ Log.w(LOGTAG, "no wallpaper manager");
+ failure = true;
+ } else {
+ try {
+ mWPManager.setStream(new ByteArrayInputStream(tmpOut
+ .toByteArray()));
+ } catch (IOException e) {
+ Log.w(LOGTAG, "cannot write stream to wallpaper", e);
+ failure = true;
+ }
+ }
+ }
+ } else {
+ Log.w(LOGTAG, "cannot compress bitmap");
+ failure = true;
+ }
}
}
- setResult(RESULT_OK, mResultIntent);
- } else {
- setResult(RESULT_CANCELED, mResultIntent);
+ return !failure; // True if any of the operations failed
}
- done();
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ doneBitmapIO(result.booleanValue(), mResultIntent);
+ }
+
}
private void done() {
- if (mActiveBackgroundIO == 0) {
- finish();
- }
+ finish();
}
- private Bitmap getCroppedImage(Bitmap image) {
+ protected static Bitmap getCroppedImage(Bitmap image, RectF cropBounds, RectF photoBounds) {
RectF imageBounds = new RectF(0, 0, image.getWidth(), image.getHeight());
- RectF crop = getBitmapCrop(imageBounds);
+ RectF crop = CropMath.getScaledCropBounds(cropBounds, photoBounds, imageBounds);
if (crop == null) {
- return image;
+ return null;
}
Rect intCrop = new Rect();
crop.roundOut(intCrop);
@@ -387,29 +553,56 @@ public class CropActivity extends Activity {
intCrop.height());
}
- private RectF getBitmapCrop(RectF imageBounds) {
- RectF crop = new RectF();
- if (!mCropView.getCropBounds(crop, imageBounds)) {
- Log.w(LOGTAG, "could not get crop");
+ protected static Bitmap getDownsampledBitmap(Bitmap image, int max_size) {
+ if (image == null || image.getWidth() == 0 || image.getHeight() == 0 || max_size < 16) {
+ throw new IllegalArgumentException("Bad argument to getDownsampledBitmap()");
+ }
+ int shifts = 0;
+ int size = CropMath.getBitmapSize(image);
+ while (size > max_size) {
+ shifts++;
+ size /= 4;
+ }
+ Bitmap ret = Bitmap.createScaledBitmap(image, image.getWidth() >> shifts,
+ image.getHeight() >> shifts, true);
+ if (ret == null) {
return null;
}
- return crop;
+ // Handle edge case for rounding.
+ if (CropMath.getBitmapSize(ret) > max_size) {
+ return Bitmap.createScaledBitmap(ret, ret.getWidth() >> 1, ret.getHeight() >> 1, true);
+ }
+ return ret;
}
/**
- * Helper method for unit conversions.
+ * Gets the crop extras from the intent, or null if none exist.
*/
- public float getPixelsFromDip(float value) {
- Resources r = getResources();
- return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value,
- r.getDisplayMetrics());
+ protected static CropExtras getExtrasFromIntent(Intent intent) {
+ Bundle extras = intent.getExtras();
+ if (extras != null) {
+ return new CropExtras(extras.getInt(CropExtras.KEY_OUTPUT_X, 0),
+ extras.getInt(CropExtras.KEY_OUTPUT_Y, 0),
+ extras.getBoolean(CropExtras.KEY_SCALE, true) &&
+ extras.getBoolean(CropExtras.KEY_SCALE_UP_IF_NEEDED, false),
+ extras.getInt(CropExtras.KEY_ASPECT_X, 0),
+ extras.getInt(CropExtras.KEY_ASPECT_Y, 0),
+ extras.getBoolean(CropExtras.KEY_SET_AS_WALLPAPER, false),
+ extras.getBoolean(CropExtras.KEY_RETURN_DATA, false),
+ (Uri) extras.getParcelable(MediaStore.EXTRA_OUTPUT),
+ extras.getString(CropExtras.KEY_OUTPUT_FORMAT),
+ extras.getBoolean(CropExtras.KEY_SHOW_WHEN_LOCKED, false),
+ extras.getFloat(CropExtras.KEY_SPOTLIGHT_X),
+ extras.getFloat(CropExtras.KEY_SPOTLIGHT_Y));
+ }
+ return null;
}
- private static CompressFormat convertExtensionToCompressFormat(String extension) {
+ protected static CompressFormat convertExtensionToCompressFormat(String extension) {
return extension.equals("png") ? CompressFormat.PNG : CompressFormat.JPEG;
}
- private static String getFileExtension(String requestFormat) {
+ protected static String getFileExtension(String requestFormat) {
String outputFormat = (requestFormat == null)
? "jpg"
: requestFormat;
@@ -419,4 +612,14 @@ public class CropActivity extends Activity {
: "jpg";
}
+ private RectF getBitmapCrop(RectF imageBounds) {
+ RectF crop = mCropView.getCrop();
+ RectF photo = mCropView.getPhoto();
+ if (crop == null || photo == null) {
+ Log.w(LOGTAG, "could not get crop");
+ return null;
+ }
+ RectF scaledCrop = CropMath.getScaledCropBounds(crop, photo, imageBounds);
+ return scaledCrop;
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java b/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java
index 483cb6372..a3664d764 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropDrawingUtils.java
@@ -113,4 +113,17 @@ public abstract class CropDrawingUtils {
return m.setRectToRect(imageBounds, displayBounds, Matrix.ScaleToFit.CENTER);
}
+ public static boolean setImageToScreenMatrix(Matrix dst, RectF image,
+ RectF screen, int rotation) {
+ RectF rotatedImage = new RectF();
+ dst.setRotate(rotation, image.centerX(), image.centerY());
+ if (!dst.mapRect(rotatedImage, image)) {
+ return false; // fails for rotations that are not multiples of 90
+ // degrees
+ }
+ boolean rToR = dst.setRectToRect(rotatedImage, screen, Matrix.ScaleToFit.CENTER);
+ boolean rot = dst.preRotate(rotation, image.centerX(), image.centerY());
+ return rToR && rot;
+ }
+
}
diff --git a/src/com/android/gallery3d/filtershow/crop/CropLoader.java b/src/com/android/gallery3d/filtershow/crop/CropLoader.java
index 132d6c1dc..fc461f5d0 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropLoader.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropLoader.java
@@ -17,6 +17,7 @@
package com.android.gallery3d.filtershow.crop;
import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
@@ -24,27 +25,32 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.net.Uri;
+import android.os.Environment;
import android.provider.MediaStore;
+import android.provider.MediaStore.Images;
+import android.provider.MediaStore.Images.ImageColumns;
import android.util.Log;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+import java.sql.Date;
+import java.text.SimpleDateFormat;
/**
* This class contains static methods for loading a bitmap and
- * mantains no instance state.
+ * maintains no instance state.
*/
public abstract class CropLoader {
public static final String LOGTAG = "CropLoader";
public static final String JPEG_MIME_TYPE = "image/jpeg";
- public static final int ORI_NORMAL = ExifInterface.Orientation.TOP_LEFT;
- public static final int ORI_ROTATE_90 = ExifInterface.Orientation.RIGHT_TOP;
- public static final int ORI_ROTATE_180 = ExifInterface.Orientation.BOTTOM_LEFT;
- public static final int ORI_ROTATE_270 = ExifInterface.Orientation.RIGHT_BOTTOM;
+
+ private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss";
+ public static final String DEFAULT_SAVE_DIRECTORY = "EditedOnlinePhotos";
/**
* Returns the orientation of image at the given URI as one of 0, 90, 180,
@@ -54,7 +60,7 @@ public abstract class CropLoader {
* @param context context whose ContentResolver to use.
* @return the orientation of the image. Defaults to 0.
*/
- public static int getMetadataOrientation(Uri uri, Context context) {
+ public static int getMetadataRotation(Uri uri, Context context) {
if (uri == null || context == null) {
throw new IllegalArgumentException("bad argument to getScaledBitmap");
}
@@ -78,25 +84,11 @@ public abstract class CropLoader {
Cursor cursor = null;
try {
cursor = context.getContentResolver().query(uri,
- new String[] {
- MediaStore.Images.ImageColumns.ORIENTATION
- },
+ new String[] { MediaStore.Images.ImageColumns.ORIENTATION },
null, null, null);
if (cursor.moveToNext()) {
int ori = cursor.getInt(0);
-
- switch (ori) {
- case 0:
- return ORI_NORMAL;
- case 90:
- return ORI_ROTATE_90;
- case 270:
- return ORI_ROTATE_270;
- case 180:
- return ORI_ROTATE_180;
- default:
- return 0;
- }
+ return (ori < 0) ? 0 : ori;
}
} catch (SQLiteException e) {
return 0;
@@ -199,4 +191,112 @@ public abstract class CropLoader {
return null;
}
+ // TODO: Super gnarly (copied from SaveCopyTask.java), do cleanup.
+
+ public static File getFinalSaveDirectory(Context context, Uri sourceUri) {
+ File saveDirectory = getSaveDirectory(context, sourceUri);
+ if ((saveDirectory == null) || !saveDirectory.canWrite()) {
+ saveDirectory = new File(Environment.getExternalStorageDirectory(),
+ DEFAULT_SAVE_DIRECTORY);
+ }
+ // Create the directory if it doesn't exist
+ if (!saveDirectory.exists())
+ saveDirectory.mkdirs();
+ return saveDirectory;
+ }
+
+
+
+ public static String getNewFileName(long time) {
+ return new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(time));
+ }
+
+ public static File getNewFile(Context context, Uri sourceUri, String filename) {
+ File saveDirectory = getFinalSaveDirectory(context, sourceUri);
+ return new File(saveDirectory, filename + ".JPG");
+ }
+
+ private interface ContentResolverQueryCallback {
+
+ void onCursorResult(Cursor cursor);
+ }
+
+ private static void querySource(Context context, Uri sourceUri, String[] projection,
+ ContentResolverQueryCallback callback) {
+ ContentResolver contentResolver = context.getContentResolver();
+ Cursor cursor = null;
+ try {
+ cursor = contentResolver.query(sourceUri, projection, null, null,
+ null);
+ if ((cursor != null) && cursor.moveToNext()) {
+ callback.onCursorResult(cursor);
+ }
+ } catch (Exception e) {
+ // Ignore error for lacking the data column from the source.
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+
+ private static File getSaveDirectory(Context context, Uri sourceUri) {
+ final File[] dir = new File[1];
+ querySource(context, sourceUri, new String[] {
+ ImageColumns.DATA }, new ContentResolverQueryCallback() {
+ @Override
+ public void onCursorResult(Cursor cursor) {
+ dir[0] = new File(cursor.getString(0)).getParentFile();
+ }
+ });
+ return dir[0];
+ }
+
+ public static Uri insertContent(Context context, Uri sourceUri, File file, String saveFileName,
+ long time) {
+ time /= 1000;
+
+ final ContentValues values = new ContentValues();
+ values.put(Images.Media.TITLE, saveFileName);
+ values.put(Images.Media.DISPLAY_NAME, file.getName());
+ values.put(Images.Media.MIME_TYPE, "image/jpeg");
+ values.put(Images.Media.DATE_TAKEN, time);
+ values.put(Images.Media.DATE_MODIFIED, time);
+ values.put(Images.Media.DATE_ADDED, time);
+ values.put(Images.Media.ORIENTATION, 0);
+ values.put(Images.Media.DATA, file.getAbsolutePath());
+ values.put(Images.Media.SIZE, file.length());
+
+ final String[] projection = new String[] {
+ ImageColumns.DATE_TAKEN,
+ ImageColumns.LATITUDE, ImageColumns.LONGITUDE,
+ };
+ querySource(context, sourceUri, projection,
+ new ContentResolverQueryCallback() {
+
+ @Override
+ public void onCursorResult(Cursor cursor) {
+ values.put(Images.Media.DATE_TAKEN, cursor.getLong(0));
+
+ double latitude = cursor.getDouble(1);
+ double longitude = cursor.getDouble(2);
+ // TODO: Change || to && after the default location
+ // issue is fixed.
+ if ((latitude != 0f) || (longitude != 0f)) {
+ values.put(Images.Media.LATITUDE, latitude);
+ values.put(Images.Media.LONGITUDE, longitude);
+ }
+ }
+ });
+
+ return context.getContentResolver().insert(
+ Images.Media.EXTERNAL_CONTENT_URI, values);
+ }
+
+ public static Uri makeAndInsertUri(Context context, Uri sourceUri) {
+ long time = System.currentTimeMillis();
+ String filename = getNewFileName(time);
+ File file = getNewFile(context, sourceUri, filename);
+ return insertContent(context, sourceUri, file, filename, time);
+ }
}
diff --git a/src/com/android/gallery3d/filtershow/crop/CropMath.java b/src/com/android/gallery3d/filtershow/crop/CropMath.java
index 52b11a56b..ed800c912 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropMath.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropMath.java
@@ -16,6 +16,7 @@
package com.android.gallery3d.filtershow.crop;
+import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.RectF;
@@ -205,6 +206,35 @@ public class CropMath {
r.set(centX - hw, centY - hh, centX + hw, centY + hh);
}
+ /**
+ * Stretches/Scales/Translates photoBounds to match displayBounds, and
+ * and returns an equivalent stretched/scaled/translated cropBounds or null
+ * if the mapping is invalid.
+ * @param cropBounds cropBounds to transform
+ * @param photoBounds original bounds containing crop bounds
+ * @param displayBounds final bounds for crop
+ * @return the stretched/scaled/translated crop bounds that fit within displayBounds
+ */
+ public static RectF getScaledCropBounds(RectF cropBounds, RectF photoBounds,
+ RectF displayBounds) {
+ Matrix m = new Matrix();
+ m.setRectToRect(photoBounds, displayBounds, Matrix.ScaleToFit.FILL);
+ RectF trueCrop = new RectF(cropBounds);
+ if (!m.mapRect(trueCrop)) {
+ return null;
+ }
+ return trueCrop;
+ }
+
+ /**
+ * Returns the size of a bitmap in bytes.
+ * @param bmap bitmap whose size to check
+ * @return bitmap size in bytes
+ */
+ public static int getBitmapSize(Bitmap bmap) {
+ return bmap.getRowBytes() * bmap.getHeight();
+ }
+
private static float getUnrotated(float[] rotatedRect, float[] center, RectF unrotated) {
float dy = rotatedRect[1] - rotatedRect[3];
float dx = rotatedRect[0] - rotatedRect[2];
diff --git a/src/com/android/gallery3d/filtershow/crop/CropObject.java b/src/com/android/gallery3d/filtershow/crop/CropObject.java
index 7999b4878..bea3ffabd 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropObject.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropObject.java
@@ -89,7 +89,7 @@ public class CropObject {
clearSelectState();
}
- public boolean setInnerAspectRatio(int width, int height) {
+ public boolean setInnerAspectRatio(float width, float height) {
if (width <= 0 || height <= 0) {
throw new IllegalArgumentException("Width and Height must be greater than zero");
}
diff --git a/src/com/android/gallery3d/filtershow/crop/CropView.java b/src/com/android/gallery3d/filtershow/crop/CropView.java
index d762ad459..dca752146 100644
--- a/src/com/android/gallery3d/filtershow/crop/CropView.java
+++ b/src/com/android/gallery3d/filtershow/crop/CropView.java
@@ -25,30 +25,46 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.crop.CropDrawingUtils;
+
public class CropView extends View {
private static final String LOGTAG = "CropView";
- Bitmap mImage = null;
- CropObject mCropObj = null;
+ private RectF mImageBounds = new RectF();
+ private RectF mScreenBounds = new RectF();
+ private RectF mScreenImageBounds = new RectF();
+ private RectF mScreenCropBounds = new RectF();
+ private Rect mShadowBounds = new Rect();
+
+ private Bitmap mBitmap;
+ private Paint mPaint = new Paint();
+
+ private NinePatchDrawable mShadow;
+ private CropObject mCropObj = null;
private final Drawable mCropIndicator;
private final int mIndicatorSize;
+ private int mRotation = 0;
+ private boolean mMovingBlock = false;
+ private Matrix mDisplayMatrix = null;
+ private Matrix mDisplayMatrixInverse = null;
+ private boolean mDirty = false;
private float mPrevX = 0;
private float mPrevY = 0;
- private int mMinSideSize = 45;
- private int mTouchTolerance = 20;
- private boolean mMovingBlock = false;
-
- private Matrix mDisplayMatrix = null;
- private Matrix mDisplayMatrixInverse = null;
+ private int mShadowMargin = 15;
+ private int mMargin = 32;
+ private int mOverlayShadowColor = 0xCF000000;
+ private int mMinSideSize = 90;
+ private int mTouchTolerance = 40;
private enum Mode {
NONE, MOVE
@@ -58,57 +74,41 @@ public class CropView extends View {
public CropView(Context context, AttributeSet attrs) {
super(context, attrs);
- Resources resources = context.getResources();
- mCropIndicator = resources.getDrawable(R.drawable.camera_crop);
- mIndicatorSize = (int) resources.getDimension(R.dimen.crop_indicator_size);
- }
-
- // For unchanging parameters
- public void setup(Bitmap image, int minSideSize, int touchTolerance) {
- mImage = image;
- mMinSideSize = minSideSize;
- mTouchTolerance = touchTolerance;
- reset();
+ Resources rsc = context.getResources();
+ mShadow = (NinePatchDrawable) rsc.getDrawable(R.drawable.geometry_shadow);
+ mCropIndicator = rsc.getDrawable(R.drawable.camera_crop);
+ mIndicatorSize = (int) rsc.getDimension(R.dimen.crop_indicator_size);
+ mShadowMargin = (int) rsc.getDimension(R.dimen.shadow_margin);
+ mMargin = (int) rsc.getDimension(R.dimen.preview_margin);
+ mMinSideSize = (int) rsc.getDimension(R.dimen.crop_min_side);
+ mTouchTolerance = (int) rsc.getDimension(R.dimen.crop_touch_tolerance);
+ mOverlayShadowColor = (int) rsc.getColor(R.color.crop_shadow_color);
}
- @Override
- public void onDraw(Canvas canvas) {
- if (mImage == null) {
- return;
- }
- int displayWidth = getWidth();
- int displayHeight = getHeight();
- Rect imageBoundsOriginal = new Rect(0, 0, mImage.getWidth(), mImage.getHeight());
- Rect displayBoundsOriginal = new Rect(0, 0, displayWidth, displayHeight);
- if (mCropObj == null) {
- reset();
- mCropObj = new CropObject(imageBoundsOriginal, imageBoundsOriginal, 0);
- }
-
- RectF imageBounds = mCropObj.getInnerBounds();
- RectF displayBounds = mCropObj.getOuterBounds();
-
- // If display matrix doesn't exist, create it and its dependencies
- if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
- mDisplayMatrix = CropDrawingUtils.getBitmapToDisplayMatrix(displayBounds, new RectF(
- displayBoundsOriginal));
- mDisplayMatrixInverse = new Matrix();
- mDisplayMatrixInverse.reset();
- if (!mDisplayMatrix.invert(mDisplayMatrixInverse)) {
- Log.w(LOGTAG, "could not invert display matrix");
+ public void initialize(Bitmap image, RectF newCropBounds, RectF newPhotoBounds, int rotation) {
+ mBitmap = image;
+ if (mCropObj != null) {
+ RectF crop = mCropObj.getInnerBounds();
+ RectF containing = mCropObj.getOuterBounds();
+ if (crop != newCropBounds || containing != newPhotoBounds
+ || mRotation != rotation) {
+ mRotation = rotation;
+ mCropObj.resetBoundsTo(newCropBounds, newPhotoBounds);
+ clearDisplay();
}
- // Scale min side and tolerance by display matrix scale factor
- mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
- mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
+ } else {
+ mRotation = rotation;
+ mCropObj = new CropObject(newPhotoBounds, newCropBounds, 0);
+ clearDisplay();
}
- canvas.drawBitmap(mImage, mDisplayMatrix, new Paint());
+ }
- if (mDisplayMatrix.mapRect(imageBounds)) {
- CropDrawingUtils.drawCropRect(canvas, imageBounds);
- CropDrawingUtils.drawRuleOfThird(canvas, imageBounds);
- CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize, imageBounds,
- mCropObj.isFixedAspect(), mCropObj.getSelectState());
- }
+ public RectF getCrop() {
+ return mCropObj.getInnerBounds();
+ }
+
+ public RectF getPhoto() {
+ return mCropObj.getOuterBounds();
}
@Override
@@ -133,8 +133,6 @@ public class CropView extends View {
mPrevX = x;
mPrevY = y;
mState = Mode.MOVE;
- } else {
- reset();
}
break;
case (MotionEvent.ACTION_UP):
@@ -144,8 +142,6 @@ public class CropView extends View {
mPrevX = x;
mPrevY = y;
mState = Mode.NONE;
- } else {
- reset();
}
break;
case (MotionEvent.ACTION_MOVE):
@@ -155,41 +151,139 @@ public class CropView extends View {
mCropObj.moveCurrentSelection(dx, dy);
mPrevX = x;
mPrevY = y;
- } else {
- reset();
}
break;
default:
- reset();
break;
}
invalidate();
return true;
}
- public void reset() {
- Log.w(LOGTAG, "reset called");
+ private void reset() {
+ Log.w(LOGTAG, "crop reset called");
mState = Mode.NONE;
mCropObj = null;
+ mRotation = 0;
+ mMovingBlock = false;
+ clearDisplay();
+ }
+
+ private void clearDisplay() {
mDisplayMatrix = null;
mDisplayMatrixInverse = null;
- mMovingBlock = false;
invalidate();
}
- public boolean getCropBounds(RectF out_crop, RectF in_newContaining) {
- Matrix m = new Matrix();
- RectF inner = mCropObj.getInnerBounds();
+ protected void configChanged() {
+ mDirty = true;
+ }
+
+ public void applyFreeAspect() {
+ mCropObj.unsetAspectRatio();
+ invalidate();
+ }
+
+ public void applyOriginalAspect() {
RectF outer = mCropObj.getOuterBounds();
- if (!m.setRectToRect(outer, in_newContaining, Matrix.ScaleToFit.FILL)) {
- Log.w(LOGTAG, "failed to make transform matrix");
- return false;
+ float w = outer.width();
+ float h = outer.height();
+ if (w > 0 && h > 0) {
+ applyAspect(w, h);
+ mCropObj.resetBoundsTo(outer, outer);
+ } else {
+ Log.w(LOGTAG, "failed to set aspect ratio original");
}
- if (!m.mapRect(inner)) {
- Log.w(LOGTAG, "failed to transform crop bounds");
- return false;
+ }
+
+ public void applySquareAspect() {
+ applyAspect(1, 1);
+ }
+
+ public void applyAspect(float x, float y) {
+ if (x <= 0 || y <= 0) {
+ throw new IllegalArgumentException("Bad arguments to applyAspect");
}
- out_crop.set(inner);
- return true;
+ if (!mCropObj.setInnerAspectRatio(x, y)) {
+ Log.w(LOGTAG, "failed to set aspect ratio");
+ }
+ invalidate();
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ if (mBitmap == null) {
+ return;
+ }
+ if (mDirty) {
+ mDirty = false;
+ clearDisplay();
+ }
+
+ mImageBounds = new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+ mScreenBounds = new RectF(0, 0, canvas.getWidth(), canvas.getHeight());
+ mScreenBounds.inset(mMargin, mMargin);
+
+ // If crop object doesn't exist, create it and update it from master
+ // state
+ if (mCropObj == null) {
+ reset();
+ mCropObj = new CropObject(mImageBounds, mImageBounds, 0);
+ }
+
+ // If display matrix doesn't exist, create it and its dependencies
+ if (mDisplayMatrix == null || mDisplayMatrixInverse == null) {
+ mDisplayMatrix = new Matrix();
+ mDisplayMatrix.reset();
+ if (!CropDrawingUtils.setImageToScreenMatrix(mDisplayMatrix, mImageBounds, mScreenBounds,
+ mRotation)) {
+ Log.w(LOGTAG, "failed to get screen matrix");
+ mDisplayMatrix = null;
+ return;
+ }
+ mDisplayMatrixInverse = new Matrix();
+ mDisplayMatrixInverse.reset();
+ if (!mDisplayMatrix.invert(mDisplayMatrixInverse)) {
+ Log.w(LOGTAG, "could not invert display matrix");
+ mDisplayMatrixInverse = null;
+ return;
+ }
+ // Scale min side and tolerance by display matrix scale factor
+ mCropObj.setMinInnerSideSize(mDisplayMatrixInverse.mapRadius(mMinSideSize));
+ mCropObj.setTouchTolerance(mDisplayMatrixInverse.mapRadius(mTouchTolerance));
+ }
+
+ mScreenImageBounds.set(mImageBounds);
+
+ // Draw background shadow
+ if (mDisplayMatrix.mapRect(mScreenImageBounds)) {
+ int margin = (int) mDisplayMatrix.mapRadius(mShadowMargin);
+ mScreenImageBounds.roundOut(mShadowBounds);
+ mShadowBounds.set(mShadowBounds.left - margin, mShadowBounds.top -
+ margin, mShadowBounds.right + margin, mShadowBounds.bottom + margin);
+ mShadow.setBounds(mShadowBounds);
+ mShadow.draw(canvas);
+ }
+
+ // Draw actual bitmap
+ canvas.drawBitmap(mBitmap, mDisplayMatrix, mPaint);
+
+ mCropObj.getInnerBounds(mScreenCropBounds);
+
+ if (mDisplayMatrix.mapRect(mScreenCropBounds)) {
+
+ // Draw overlay shadows
+ Paint p = new Paint();
+ p.setColor(mOverlayShadowColor);
+ p.setStyle(Paint.Style.FILL);
+ CropDrawingUtils.drawShadows(canvas, p, mScreenCropBounds, mScreenImageBounds);
+
+ // Draw crop rect and markers
+ CropDrawingUtils.drawCropRect(canvas, mScreenCropBounds);
+ CropDrawingUtils.drawRuleOfThird(canvas, mScreenCropBounds);
+ CropDrawingUtils.drawIndicators(canvas, mCropIndicator, mIndicatorSize,
+ mScreenCropBounds, mCropObj.isFixedAspect(), mCropObj.getSelectState());
+ }
+
}
}
diff --git a/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java b/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java
index a3fc5aab4..d4e66edf8 100644
--- a/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java
+++ b/src/com/android/gallery3d/filtershow/editors/ImageOnlyEditor.java
@@ -26,7 +26,7 @@ import com.android.gallery3d.filtershow.imageshow.ImageShow;
* The editor with no slider for filters without UI
*/
public class ImageOnlyEditor extends Editor {
- public static int ID = R.id.imageOnlyEditor;
+ public final static int ID = R.id.imageOnlyEditor;
private final String LOGTAG = "ImageOnlyEditor";
public ImageOnlyEditor() {
@@ -37,6 +37,10 @@ public class ImageOnlyEditor extends Editor {
super(id);
}
+ public boolean useUtilityPanel() {
+ return false;
+ }
+
@Override
public void createEditor(Context context, FrameLayout frameLayout) {
super.createEditor(context, frameLayout);
diff --git a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
index 66ad10640..ec9a4368c 100644
--- a/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
+++ b/src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
@@ -15,7 +15,10 @@
*/
package com.android.gallery3d.filtershow.filters;
+import android.content.Context;
import android.content.res.Resources;
+
+import com.android.gallery3d.R;
import com.android.gallery3d.filtershow.presets.ImagePreset;
import java.util.HashMap;
@@ -86,7 +89,7 @@ public abstract class BaseFiltersManager {
protected void addFilterClasses(Vector<Class> filters) {
filters.add(ImageFilterTinyPlanet.class);
- filters.add(ImageFilterRedEye.class);
+ //filters.add(ImageFilterRedEye.class);
filters.add(ImageFilterWBalance.class);
filters.add(ImageFilterExposure.class);
filters.add(ImageFilterVignette.class);
@@ -109,12 +112,40 @@ public abstract class BaseFiltersManager {
filters.add(ImageFilterGeometry.class);
}
- public void addBorders(Vector<FilterRepresentation> representations) {
- // Override
+ public void addBorders(Context context, Vector<FilterRepresentation> representations) {
+
}
- public void addLooks(Vector<FilterRepresentation> representations) {
- // Override
+ public void addLooks(Context context, Vector<FilterRepresentation> representations) {
+ int[] drawid = {
+ R.drawable.filtershow_fx_0005_punch,
+ R.drawable.filtershow_fx_0000_vintage,
+ R.drawable.filtershow_fx_0004_bw_contrast,
+ R.drawable.filtershow_fx_0002_bleach,
+ R.drawable.filtershow_fx_0001_instant,
+ R.drawable.filtershow_fx_0007_washout,
+ R.drawable.filtershow_fx_0003_blue_crush,
+ R.drawable.filtershow_fx_0008_washout_color,
+ R.drawable.filtershow_fx_0006_x_process
+ };
+
+ int[] fxNameid = {
+ R.string.ffx_punch,
+ R.string.ffx_vintage,
+ R.string.ffx_bw_contrast,
+ R.string.ffx_bleach,
+ R.string.ffx_instant,
+ R.string.ffx_washout,
+ R.string.ffx_blue_crush,
+ R.string.ffx_washout_color,
+ R.string.ffx_x_process
+ };
+
+ for (int i = 0; i < drawid.length; i++) {
+ FilterFxRepresentation fx = new FilterFxRepresentation(
+ context.getString(fxNameid[i]), drawid[i], fxNameid[i]);
+ representations.add(fx);
+ }
}
public void addEffects(Vector<FilterRepresentation> representations) {
@@ -137,7 +168,7 @@ public abstract class BaseFiltersManager {
}
public void addTools(Vector<FilterRepresentation> representations) {
- representations.add(getRepresentation(ImageFilterRedEye.class));
+ //representations.add(getRepresentation(ImageFilterRedEye.class));
representations.add(getRepresentation(ImageFilterDraw.class));
}
diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
index 65fe523c0..045c1a538 100644
--- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
+++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
@@ -28,6 +28,7 @@ import android.graphics.RectF;
import android.net.Uri;
import android.os.Handler;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.GestureDetector;
import android.view.GestureDetector.OnDoubleTapListener;
import android.view.GestureDetector.OnGestureListener;
@@ -83,14 +84,22 @@ public class ImageShow extends View implements OnGestureListener,
private static int mOriginalTextSize = 26;
private static String mOriginalText = "Original";
private boolean mZoomIn = false;
-
- protected GeometryMetadata getGeometry() {
- return new GeometryMetadata(getImagePreset().mGeoData);
+ Point mOriginalTranslation = new Point();
+ float mOriginalScale;
+ float mStartFocusX, mStartFocusY;
+ private enum InteractionMode {
+ NONE,
+ SCALE,
+ MOVE
}
-
private String mToast = null;
private boolean mShowToast = false;
private boolean mImportantToast = false;
+ InteractionMode mInteractionMode = InteractionMode.NONE;
+
+ protected GeometryMetadata getGeometry() {
+ return new GeometryMetadata(getImagePreset().mGeoData);
+ }
private PanelController mController = null;
@@ -559,9 +568,15 @@ public class ImageShow extends View implements OnGestureListener,
@Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
+ int action = event.getAction();
+ action = action & MotionEvent.ACTION_MASK;
+
mGestureDetector.onTouchEvent(event);
boolean scaleInProgress = scaleInProgress();
mScaleGestureDetector.onTouchEvent(event);
+ if (mInteractionMode == InteractionMode.SCALE) {
+ return true;
+ }
if (!scaleInProgress() && scaleInProgress) {
// If we were scaling, the scale will stop but we will
// still issue an ACTION_UP. Let the subclasses know.
@@ -570,7 +585,8 @@ public class ImageShow extends View implements OnGestureListener,
int ex = (int) event.getX();
int ey = (int) event.getY();
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (action == MotionEvent.ACTION_DOWN) {
+ mInteractionMode = InteractionMode.MOVE;
mTouchDown.x = ex;
mTouchDown.y = ey;
mTouchShowOriginalDate = System.currentTimeMillis();
@@ -578,7 +594,7 @@ public class ImageShow extends View implements OnGestureListener,
MasterImage.getImage().setOriginalTranslation(MasterImage.getImage().getTranslation());
}
- if (event.getAction() == MotionEvent.ACTION_MOVE) {
+ if (action == MotionEvent.ACTION_MOVE && mInteractionMode == InteractionMode.MOVE) {
mTouch.x = ex;
mTouch.y = ey;
@@ -600,7 +616,8 @@ public class ImageShow extends View implements OnGestureListener,
}
}
- if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (action == MotionEvent.ACTION_UP) {
+ mInteractionMode = InteractionMode.NONE;
mTouchShowOriginal = false;
mTouchDown.x = 0;
mTouchDown.y = 0;
@@ -699,7 +716,10 @@ public class ImageShow extends View implements OnGestureListener,
@Override
public boolean onScale(ScaleGestureDetector detector) {
- float scaleFactor = MasterImage.getImage().getScaleFactor();
+ MasterImage img = MasterImage.getImage();
+ float scaleFactor = img.getScaleFactor();
+ Point pos = img.getTranslation();
+
scaleFactor = scaleFactor * detector.getScaleFactor();
if (scaleFactor > MasterImage.getImage().getMaxScaleFactor()) {
scaleFactor = MasterImage.getImage().getMaxScaleFactor();
@@ -708,16 +728,36 @@ public class ImageShow extends View implements OnGestureListener,
scaleFactor = 0.5f;
}
MasterImage.getImage().setScaleFactor(scaleFactor);
+ scaleFactor = img.getScaleFactor();
+ pos = img.getTranslation();
+ float focusx = detector.getFocusX();
+ float focusy = detector.getFocusY();
+ float translateX = (focusx - mStartFocusX) / scaleFactor;
+ float translateY = (focusy - mStartFocusY) / scaleFactor;
+ Point translation = MasterImage.getImage().getTranslation();
+ translation.x = (int) (mOriginalTranslation.x + translateX);
+ translation.y = (int) (mOriginalTranslation.y + translateY);
+ MasterImage.getImage().setTranslation(translation);
+
+ invalidate();
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
+ Point pos = MasterImage.getImage().getTranslation();
+ mOriginalTranslation.x = pos.x;
+ mOriginalTranslation.y = pos.y;
+ mOriginalScale = MasterImage.getImage().getScaleFactor();
+ mStartFocusX = detector.getFocusX();
+ mStartFocusY = detector.getFocusY();
+ mInteractionMode = InteractionMode.SCALE;
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
+ mInteractionMode = InteractionMode.NONE;
if (MasterImage.getImage().getScaleFactor() < 1) {
MasterImage.getImage().setScaleFactor(1);
invalidate();
diff --git a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java
index 7b881c7f5..528746db1 100644
--- a/src/com/android/gallery3d/filtershow/ui/ImageCurves.java
+++ b/src/com/android/gallery3d/filtershow/ui/ImageCurves.java
@@ -250,8 +250,6 @@ public class ImageCurves extends ImageShow {
@Override
public synchronized boolean onTouchEvent(MotionEvent e) {
- super.onTouchEvent(e);
-
if (e.getPointerCount() != 1) {
return true;
}