summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/app
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/app')
-rw-r--r--src/com/android/gallery3d/app/AbstractGalleryActivity.java50
-rw-r--r--src/com/android/gallery3d/app/ActivityState.java37
-rw-r--r--src/com/android/gallery3d/app/AlbumDataLoader.java3
-rw-r--r--src/com/android/gallery3d/app/AlbumPage.java29
-rw-r--r--src/com/android/gallery3d/app/AlbumSetPage.java19
-rw-r--r--src/com/android/gallery3d/app/BatchService.java45
-rw-r--r--src/com/android/gallery3d/app/CommonControllerOverlay.java2
-rw-r--r--src/com/android/gallery3d/app/CropImage.java1040
-rw-r--r--src/com/android/gallery3d/app/FilmstripPage.java21
-rw-r--r--src/com/android/gallery3d/app/Gallery.java11
-rw-r--r--src/com/android/gallery3d/app/GalleryActionBar.java16
-rw-r--r--src/com/android/gallery3d/app/MovieActivity.java22
-rw-r--r--src/com/android/gallery3d/app/MoviePlayer.java3
-rw-r--r--src/com/android/gallery3d/app/MuteVideo.java104
-rw-r--r--src/com/android/gallery3d/app/PhotoPage.java28
-rw-r--r--src/com/android/gallery3d/app/PickerActivity.java10
-rw-r--r--src/com/android/gallery3d/app/SinglePhotoDataAdapter.java1
-rw-r--r--src/com/android/gallery3d/app/SinglePhotoPage.java21
-rw-r--r--src/com/android/gallery3d/app/StateManager.java4
-rw-r--r--src/com/android/gallery3d/app/TrimControllerOverlay.java51
-rw-r--r--src/com/android/gallery3d/app/TrimVideo.java150
-rw-r--r--src/com/android/gallery3d/app/VideoUtils.java (renamed from src/com/android/gallery3d/app/TrimVideoUtils.java)58
-rw-r--r--src/com/android/gallery3d/app/Wallpaper.java23
23 files changed, 437 insertions, 1311 deletions
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
index 88ac028e1..081b87aee 100644
--- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java
+++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java
@@ -17,22 +17,25 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.ServiceConnection;
import android.content.res.Configuration;
import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
+import android.os.IBinder;
import android.view.Window;
import android.view.WindowManager;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.data.BitmapPool;
@@ -40,10 +43,10 @@ import com.android.gallery3d.data.DataManager;
import com.android.gallery3d.data.MediaItem;
import com.android.gallery3d.ui.GLRoot;
import com.android.gallery3d.ui.GLRootView;
-import com.android.gallery3d.util.ThreadPool;
import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper;
+import com.android.gallery3d.util.ThreadPool;
-public class AbstractGalleryActivity extends Activity implements GalleryContext {
+public class AbstractGalleryActivity extends SherlockActivity implements GalleryContext {
@SuppressWarnings("unused")
private static final String TAG = "AbstractGalleryActivity";
private GLRootView mGLRootView;
@@ -71,6 +74,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
getWindow().setBackgroundDrawable(null);
mPanoramaViewHelper = new PanoramaViewHelper(this);
mPanoramaViewHelper.onCreate();
+ doBindBatchService();
}
@Override
@@ -237,6 +241,7 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
} finally {
mGLRootView.unlockRenderThread();
}
+ doUnbindBatchService();
}
@Override
@@ -308,4 +313,39 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
return (getWindow().getAttributes().flags
& WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
}
+
+ private BatchService mBatchService;
+ private boolean mBatchServiceIsBound = false;
+ private ServiceConnection mBatchServiceConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ mBatchService = ((BatchService.LocalBinder)service).getService();
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ mBatchService = null;
+ }
+ };
+
+ private void doBindBatchService() {
+ bindService(new Intent(this, BatchService.class), mBatchServiceConnection, Context.BIND_AUTO_CREATE);
+ mBatchServiceIsBound = true;
+ }
+
+ private void doUnbindBatchService() {
+ if (mBatchServiceIsBound) {
+ // Detach our existing connection.
+ unbindService(mBatchServiceConnection);
+ mBatchServiceIsBound = false;
+ }
+ }
+
+ public ThreadPool getBatchServiceThreadPoolIfAvailable() {
+ if (mBatchServiceIsBound && mBatchService != null) {
+ return mBatchService.getThreadPool();
+ } else {
+ // Fall back on the old behavior if for some reason the
+ // service is not available.
+ return getThreadPool();
+ }
+ }
}
diff --git a/src/com/android/gallery3d/app/ActivityState.java b/src/com/android/gallery3d/app/ActivityState.java
index cdd91ff4d..b2e39b1cb 100644
--- a/src/com/android/gallery3d/app/ActivityState.java
+++ b/src/com/android/gallery3d/app/ActivityState.java
@@ -16,24 +16,23 @@
package com.android.gallery3d.app;
-import android.app.ActionBar;
import android.app.Activity;
import android.content.BroadcastReceiver;
-import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.os.BatteryManager;
import android.os.Bundle;
-import android.provider.Settings;
-import android.provider.Settings.SettingNotFoundException;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.HapticFeedbackConstants;
import android.view.Window;
import android.view.WindowManager;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.anim.StateTransitionAnimation;
import com.android.gallery3d.ui.GLView;
@@ -62,9 +61,6 @@ abstract public class ActivityState {
public Intent resultData;
}
- protected boolean mHapticsEnabled;
- private ContentResolver mContentResolver;
-
private boolean mDestroyed = false;
private boolean mPlugged = false;
boolean mIsFinishing = false;
@@ -92,7 +88,6 @@ abstract public class ActivityState {
void initialize(AbstractGalleryActivity activity, Bundle data) {
mActivity = activity;
mData = data;
- mContentResolver = activity.getAndroidContext().getContentResolver();
}
public Bundle getData() {
@@ -175,15 +170,20 @@ abstract public class ActivityState {
protected void transitionOnNextPause(Class<? extends ActivityState> outgoing,
Class<? extends ActivityState> incoming, StateTransitionAnimation.Transition hint) {
- if (outgoing == PhotoPage.class && incoming == AlbumPage.class) {
+ if (outgoing == SinglePhotoPage.class && incoming == AlbumPage.class) {
mNextTransition = StateTransitionAnimation.Transition.Outgoing;
- } else if (outgoing == AlbumPage.class && incoming == PhotoPage.class) {
+ } else if (outgoing == AlbumPage.class && incoming == SinglePhotoPage.class) {
mNextTransition = StateTransitionAnimation.Transition.PhotoIncoming;
} else {
mNextTransition = hint;
}
}
+ protected void performHapticFeedback(int feedbackConstant) {
+ mActivity.getWindow().getDecorView().performHapticFeedback(feedbackConstant,
+ HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
+ }
+
protected void onPause() {
if (0 != (mFlags & FLAG_SCREEN_ON_WHEN_PLUGGED)) {
((Activity) mActivity).unregisterReceiver(mPowerIntentReceiver);
@@ -198,7 +198,7 @@ abstract public class ActivityState {
// should only be called by StateManager
void resume() {
AbstractGalleryActivity activity = mActivity;
- ActionBar actionBar = activity.getActionBar();
+ ActionBar actionBar = ((SherlockActivity) activity).getSupportActionBar();
if (actionBar != null) {
if ((mFlags & FLAG_HIDE_ACTION_BAR) != 0) {
actionBar.hide();
@@ -231,13 +231,6 @@ abstract public class ActivityState {
activity.registerReceiver(mPowerIntentReceiver, filter);
}
- try {
- mHapticsEnabled = Settings.System.getInt(mContentResolver,
- Settings.System.HAPTIC_FEEDBACK_ENABLED) != 0;
- } catch (SettingNotFoundException e) {
- mHapticsEnabled = false;
- }
-
onResume();
// the transition store should be cleared after resume;
@@ -279,6 +272,6 @@ abstract public class ActivityState {
}
protected MenuInflater getSupportMenuInflater() {
- return mActivity.getMenuInflater();
+ return ((SherlockActivity) mActivity).getSupportMenuInflater();
}
}
diff --git a/src/com/android/gallery3d/app/AlbumDataLoader.java b/src/com/android/gallery3d/app/AlbumDataLoader.java
index 0ee1b03af..28a822830 100644
--- a/src/com/android/gallery3d/app/AlbumDataLoader.java
+++ b/src/com/android/gallery3d/app/AlbumDataLoader.java
@@ -120,8 +120,7 @@ public class AlbumDataLoader {
public MediaItem get(int index) {
if (!isActive(index)) {
- throw new IllegalArgumentException(String.format(
- "%s not in (%s, %s)", index, mActiveStart, mActiveEnd));
+ return mSource.getMediaItem(index, 1).get(0);
}
return mData[index % mData.length];
}
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index ee7a107fd..60000903d 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -24,13 +24,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.Vibrator;
import android.provider.MediaStore;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.HapticFeedbackConstants;
import android.widget.Toast;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
@@ -57,6 +57,9 @@ import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.GalleryUtils;
import com.android.gallery3d.util.MediaSetUtils;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.CropExtras;
+
public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner,
SelectionManager.SelectionListener, MediaSet.SyncListener, GalleryActionBar.OnAlbumModeSelectedListener {
@@ -89,7 +92,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
private AlbumDataLoader mAlbumDataAdapter;
protected SelectionManager mSelectionManager;
- private Vibrator mVibrator;
private boolean mGetContent;
private boolean mShowClusterMenu;
@@ -304,10 +306,10 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
startInFilmstrip);
data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, mMediaSet.isCameraRoll());
if (startInFilmstrip) {
- mActivity.getStateManager().switchState(this, PhotoPage.class, data);
+ mActivity.getStateManager().switchState(this, FilmstripPage.class, data);
} else {
mActivity.getStateManager().startStateForResult(
- PhotoPage.class, REQUEST_PHOTO, data);
+ SinglePhotoPage.class, REQUEST_PHOTO, data);
}
}
}
@@ -318,11 +320,11 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
if (mData.getString(Gallery.EXTRA_CROP) != null) {
// TODO: Handle MtpImagew
Uri uri = dm.getContentUri(item.getPath());
- Intent intent = new Intent(CropImage.ACTION_CROP, uri)
+ Intent intent = new Intent(FilterShowActivity.CROP_ACTION, uri)
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
.putExtras(getData());
if (mData.getParcelable(MediaStore.EXTRA_OUTPUT) == null) {
- intent.putExtra(CropImage.KEY_RETURN_DATA, true);
+ intent.putExtra(CropExtras.KEY_RETURN_DATA, true);
}
activity.startActivity(intent);
activity.finish();
@@ -371,7 +373,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mShowClusterMenu = data.getBoolean(KEY_SHOW_CLUSTER_MENU, false);
mDetailsSource = new MyDetailsSource();
Context context = mActivity.getAndroidContext();
- mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
// Enable auto-select-all for mtp album
if (data.getBoolean(KEY_AUTO_SELECT_ALL)) {
@@ -379,7 +380,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
}
mLaunchedFromPhotoPage =
- mActivity.getStateManager().hasStateClass(PhotoPage.class);
+ mActivity.getStateManager().hasStateClass(FilmstripPage.class);
mInCameraApp = data.getBoolean(PhotoPage.KEY_APP_BRIDGE, false);
mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
@@ -443,7 +444,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mSelectionManager.leaveSelectionMode();
}
mAlbumView.setSlotFilter(null);
-
+ mActionModeHandler.pause();
mAlbumDataAdapter.pause();
mAlbumView.pause();
DetailsHelper.pause();
@@ -456,7 +457,6 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
mSyncTask = null;
clearLoadingBit(BIT_LOADING_SYNC);
}
- mActionModeHandler.pause();
}
@Override
@@ -465,6 +465,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
if (mAlbumDataAdapter != null) {
mAlbumDataAdapter.setLoadingListener(null);
}
+ mActionModeHandler.destroy();
}
private void initializeViews() {
@@ -662,7 +663,7 @@ public class AlbumPage extends ActivityState implements GalleryActionBar.Cluster
switch (mode) {
case SelectionManager.ENTER_SELECTION_MODE: {
mActionModeHandler.startActionMode();
- if (mHapticsEnabled) mVibrator.vibrate(100);
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
}
case SelectionManager.LEAVE_SELECTION_MODE: {
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index cae606be1..df68ffbaa 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -23,16 +23,16 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.os.Vibrator;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
+import android.view.HapticFeedbackConstants;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout;
import android.widget.Toast;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
@@ -92,7 +92,6 @@ public class AlbumSetPage extends ActivityState implements
private boolean mShowClusterMenu;
private GalleryActionBar mActionBar;
private int mSelectedAction;
- private Vibrator mVibrator;
protected SelectionManager mSelectionManager;
private AlbumSetDataLoader mAlbumSetDataAdapter;
@@ -275,7 +274,7 @@ public class AlbumSetPage extends ActivityState implements
data.putBoolean(PhotoPage.KEY_START_IN_FILMSTRIP, true);
data.putBoolean(PhotoPage.KEY_IN_CAMERA_ROLL, targetSet.isCameraRoll());
mActivity.getStateManager().startStateForResult(
- PhotoPage.class, AlbumPage.REQUEST_PHOTO, data);
+ FilmstripPage.class, AlbumPage.REQUEST_PHOTO, data);
return;
}
data.putString(AlbumPage.KEY_MEDIA_PATH, mediaPath);
@@ -332,7 +331,6 @@ public class AlbumSetPage extends ActivityState implements
mSubtitle = data.getString(AlbumSetPage.KEY_SET_SUBTITLE);
mEyePosition = new EyePosition(context, this);
mDetailsSource = new MyDetailsSource();
- mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
mActionBar = mActivity.getGalleryActionBar();
mSelectedAction = data.getInt(AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE,
FilterUtils.CLUSTER_BY_ALBUM);
@@ -353,8 +351,9 @@ public class AlbumSetPage extends ActivityState implements
@Override
public void onDestroy() {
- cleanupCameraButton();
super.onDestroy();
+ cleanupCameraButton();
+ mActionModeHandler.destroy();
}
private boolean setupCameraButton() {
@@ -439,9 +438,9 @@ public class AlbumSetPage extends ActivityState implements
public void onPause() {
super.onPause();
mIsActive = false;
- mActionModeHandler.pause();
mAlbumSetDataAdapter.pause();
mAlbumSetView.pause();
+ mActionModeHandler.pause();
mEyePosition.pause();
DetailsHelper.pause();
// Call disableClusterMenu to avoid receiving callback after paused.
@@ -655,7 +654,7 @@ public class AlbumSetPage extends ActivityState implements
case SelectionManager.ENTER_SELECTION_MODE: {
mActionBar.disableClusterMenu(true);
mActionModeHandler.startActionMode();
- if (mHapticsEnabled) mVibrator.vibrate(100);
+ performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
break;
}
case SelectionManager.LEAVE_SELECTION_MODE: {
diff --git a/src/com/android/gallery3d/app/BatchService.java b/src/com/android/gallery3d/app/BatchService.java
new file mode 100644
index 000000000..98a1d8215
--- /dev/null
+++ b/src/com/android/gallery3d/app/BatchService.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+
+import com.android.gallery3d.util.ThreadPool;
+
+public class BatchService extends Service {
+
+ public class LocalBinder extends Binder {
+ BatchService getService() {
+ return BatchService.this;
+ }
+ }
+
+ private final IBinder mBinder = new LocalBinder();
+ private ThreadPool mThreadPool = new ThreadPool(1, 1);
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ public ThreadPool getThreadPool() {
+ return mThreadPool;
+ }
+}
diff --git a/src/com/android/gallery3d/app/CommonControllerOverlay.java b/src/com/android/gallery3d/app/CommonControllerOverlay.java
index ab43dada5..089872fa5 100644
--- a/src/com/android/gallery3d/app/CommonControllerOverlay.java
+++ b/src/com/android/gallery3d/app/CommonControllerOverlay.java
@@ -154,7 +154,7 @@ public abstract class CommonControllerOverlay extends FrameLayout implements
@Override
public void showEnded() {
mState = State.ENDED;
- showMainView(mPlayPauseReplayView);
+ if (mCanReplay) showMainView(mPlayPauseReplayView);
}
@Override
diff --git a/src/com/android/gallery3d/app/CropImage.java b/src/com/android/gallery3d/app/CropImage.java
deleted file mode 100644
index 89ca63d44..000000000
--- a/src/com/android/gallery3d/app/CropImage.java
+++ /dev/null
@@ -1,1040 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.app;
-
-import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.ProgressDialog;
-import android.app.WallpaperManager;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.CompressFormat;
-import android.graphics.Bitmap.Config;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapRegionDecoder;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.Message;
-import android.provider.MediaStore;
-import android.provider.MediaStore.Images;
-import android.util.FloatMath;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.Window;
-import android.view.WindowManager;
-import android.widget.Toast;
-
-import com.android.camera.Util;
-import com.android.gallery3d.R;
-import com.android.gallery3d.common.ApiHelper;
-import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.DataManager;
-import com.android.gallery3d.data.LocalImage;
-import com.android.gallery3d.data.MediaItem;
-import com.android.gallery3d.data.MediaObject;
-import com.android.gallery3d.data.Path;
-import com.android.gallery3d.exif.ExifData;
-import com.android.gallery3d.exif.ExifOutputStream;
-import com.android.gallery3d.exif.ExifReader;
-import com.android.gallery3d.exif.ExifTag;
-import com.android.gallery3d.picasasource.PicasaSource;
-import com.android.gallery3d.ui.BitmapScreenNail;
-import com.android.gallery3d.ui.BitmapTileProvider;
-import com.android.gallery3d.ui.CropView;
-import com.android.gallery3d.ui.GLRoot;
-import com.android.gallery3d.ui.SynchronizedHandler;
-import com.android.gallery3d.ui.TileImageViewAdapter;
-import com.android.gallery3d.util.BucketNames;
-import com.android.gallery3d.util.Future;
-import com.android.gallery3d.util.FutureListener;
-import com.android.gallery3d.util.GalleryUtils;
-import com.android.gallery3d.util.InterruptableOutputStream;
-import com.android.gallery3d.util.ThreadPool.CancelListener;
-import com.android.gallery3d.util.ThreadPool.Job;
-import com.android.gallery3d.util.ThreadPool.JobContext;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteOrder;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-/**
- * The activity can crop specific region of interest from an image.
- */
-public class CropImage extends AbstractGalleryActivity {
- private static final String TAG = "CropImage";
- public static final String ACTION_CROP = "com.android.camera.action.CROP";
-
- private static final int MAX_PIXEL_COUNT = 5 * 1000000; // 5M pixels
- private static final int MAX_FILE_INDEX = 1000;
- private static final int TILE_SIZE = 512;
- private static final int BACKUP_PIXEL_COUNT = 480000; // around 800x600
-
- private static final int MSG_LARGE_BITMAP = 1;
- private static final int MSG_BITMAP = 2;
- private static final int MSG_SAVE_COMPLETE = 3;
- private static final int MSG_SHOW_SAVE_ERROR = 4;
- private static final int MSG_CANCEL_DIALOG = 5;
-
- private static final int MAX_BACKUP_IMAGE_SIZE = 320;
- private static final int DEFAULT_COMPRESS_QUALITY = 90;
- private static final String TIME_STAMP_NAME = "'IMG'_yyyyMMdd_HHmmss";
-
- public static final String KEY_RETURN_DATA = "return-data";
- public static final String KEY_CROPPED_RECT = "cropped-rect";
- public static final String KEY_ASPECT_X = "aspectX";
- public static final String KEY_ASPECT_Y = "aspectY";
- public static final String KEY_SPOTLIGHT_X = "spotlightX";
- public static final String KEY_SPOTLIGHT_Y = "spotlightY";
- public static final String KEY_OUTPUT_X = "outputX";
- public static final String KEY_OUTPUT_Y = "outputY";
- public static final String KEY_SCALE = "scale";
- public static final String KEY_DATA = "data";
- public static final String KEY_SCALE_UP_IF_NEEDED = "scaleUpIfNeeded";
- public static final String KEY_OUTPUT_FORMAT = "outputFormat";
- public static final String KEY_SET_AS_WALLPAPER = "set-as-wallpaper";
- public static final String KEY_NO_FACE_DETECTION = "noFaceDetection";
- public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked";
-
- private static final String KEY_STATE = "state";
-
- private static final int STATE_INIT = 0;
- private static final int STATE_LOADED = 1;
- private static final int STATE_SAVING = 2;
-
- public static final File DOWNLOAD_BUCKET = new File(
- Environment.getExternalStorageDirectory(), BucketNames.DOWNLOAD);
-
- public static final String CROP_ACTION = "com.android.camera.action.CROP";
-
- private int mState = STATE_INIT;
-
- private CropView mCropView;
-
- private boolean mDoFaceDetection = true;
-
- private Handler mMainHandler;
-
- // We keep the following members so that we can free them
-
- // mBitmap is the unrotated bitmap we pass in to mCropView for detect faces.
- // mCropView is responsible for rotating it to the way that it is viewed by users.
- private Bitmap mBitmap;
- private BitmapTileProvider mBitmapTileProvider;
- private BitmapRegionDecoder mRegionDecoder;
- private Bitmap mBitmapInIntent;
- private boolean mUseRegionDecoder = false;
- private BitmapScreenNail mBitmapScreenNail;
-
- private ProgressDialog mProgressDialog;
- private Future<BitmapRegionDecoder> mLoadTask;
- private Future<Bitmap> mLoadBitmapTask;
- private Future<Intent> mSaveTask;
-
- private MediaItem mMediaItem;
-
- @Override
- public void onCreate(Bundle bundle) {
- super.onCreate(bundle);
- requestWindowFeature(Window.FEATURE_ACTION_BAR);
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
-
- // Initialize UI
- setContentView(R.layout.cropimage);
- mCropView = new CropView(this);
- getGLRoot().setContentPane(mCropView);
-
- ActionBar actionBar = getActionBar();
- int displayOptions = ActionBar.DISPLAY_HOME_AS_UP
- | ActionBar.DISPLAY_SHOW_TITLE;
- actionBar.setDisplayOptions(displayOptions, displayOptions);
-
- Bundle extra = getIntent().getExtras();
- if (extra != null) {
- if (extra.getBoolean(KEY_SET_AS_WALLPAPER, false)) {
- actionBar.setTitle(getString(R.string.set_wallpaper));
- }
- if (extra.getBoolean(KEY_SHOW_WHEN_LOCKED, false)) {
- getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
- }
- }
-
- mMainHandler = new SynchronizedHandler(getGLRoot()) {
- @Override
- public void handleMessage(Message message) {
- switch (message.what) {
- case MSG_LARGE_BITMAP: {
- dismissProgressDialogIfShown();
- onBitmapRegionDecoderAvailable((BitmapRegionDecoder) message.obj);
- break;
- }
- case MSG_BITMAP: {
- dismissProgressDialogIfShown();
- onBitmapAvailable((Bitmap) message.obj);
- break;
- }
- case MSG_SHOW_SAVE_ERROR: {
- dismissProgressDialogIfShown();
- setResult(RESULT_CANCELED);
- Toast.makeText(CropImage.this,
- CropImage.this.getString(R.string.save_error),
- Toast.LENGTH_LONG).show();
- finish();
- }
- case MSG_SAVE_COMPLETE: {
- dismissProgressDialogIfShown();
- setResult(RESULT_OK, (Intent) message.obj);
- finish();
- break;
- }
- case MSG_CANCEL_DIALOG: {
- setResult(RESULT_CANCELED);
- finish();
- break;
- }
- }
- }
- };
-
- setCropParameters();
- }
-
- @Override
- protected void onSaveInstanceState(Bundle saveState) {
- saveState.putInt(KEY_STATE, mState);
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.crop, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home: {
- finish();
- break;
- }
- case R.id.cancel: {
- setResult(RESULT_CANCELED);
- finish();
- break;
- }
- case R.id.save: {
- onSaveClicked();
- break;
- }
- }
- return true;
- }
-
- @Override
- public void onBackPressed() {
- finish();
- }
-
- private class SaveOutput implements Job<Intent> {
- private final RectF mCropRect;
-
- public SaveOutput(RectF cropRect) {
- mCropRect = cropRect;
- }
-
- @Override
- public Intent run(JobContext jc) {
- RectF cropRect = mCropRect;
- Bundle extra = getIntent().getExtras();
-
- Rect rect = new Rect(
- Math.round(cropRect.left), Math.round(cropRect.top),
- Math.round(cropRect.right), Math.round(cropRect.bottom));
-
- Intent result = new Intent();
- result.putExtra(KEY_CROPPED_RECT, rect);
- Bitmap cropped = null;
- boolean outputted = false;
- if (extra != null) {
- Uri uri = (Uri) extra.getParcelable(MediaStore.EXTRA_OUTPUT);
- if (uri != null) {
- if (jc.isCancelled()) return null;
- outputted = true;
- cropped = getCroppedImage(rect);
- if (!saveBitmapToUri(jc, cropped, uri)) return null;
- }
- if (extra.getBoolean(KEY_RETURN_DATA, false)) {
- if (jc.isCancelled()) return null;
- outputted = true;
- if (cropped == null) cropped = getCroppedImage(rect);
- result.putExtra(KEY_DATA, cropped);
- }
- if (extra.getBoolean(KEY_SET_AS_WALLPAPER, false)) {
- if (jc.isCancelled()) return null;
- outputted = true;
- if (cropped == null) cropped = getCroppedImage(rect);
- if (!setAsWallpaper(jc, cropped)) return null;
- }
- }
- if (!outputted) {
- if (jc.isCancelled()) return null;
- if (cropped == null) cropped = getCroppedImage(rect);
- Uri data = saveToMediaProvider(jc, cropped);
- if (data != null) result.setData(data);
- }
- return result;
- }
- }
-
- public static String determineCompressFormat(MediaObject obj) {
- String compressFormat = "JPEG";
- if (obj instanceof MediaItem) {
- String mime = ((MediaItem) obj).getMimeType();
- if (mime.contains("png") || mime.contains("gif")) {
- // Set the compress format to PNG for png and gif images
- // because they may contain alpha values.
- compressFormat = "PNG";
- }
- }
- return compressFormat;
- }
-
- private boolean setAsWallpaper(JobContext jc, Bitmap wallpaper) {
- try {
- WallpaperManager.getInstance(this).setBitmap(wallpaper);
- } catch (IOException e) {
- Log.w(TAG, "fail to set wall paper", e);
- }
- return true;
- }
-
- private File saveMedia(
- JobContext jc, Bitmap cropped, File directory, String filename, ExifData exifData) {
- // Try file-1.jpg, file-2.jpg, ... until we find a filename
- // which does not exist yet.
- File candidate = null;
- String fileExtension = getFileExtension();
- for (int i = 1; i < MAX_FILE_INDEX; ++i) {
- candidate = new File(directory, filename + "-" + i + "."
- + fileExtension);
- try {
- if (candidate.createNewFile()) break;
- } catch (IOException e) {
- Log.e(TAG, "fail to create new file: "
- + candidate.getAbsolutePath(), e);
- return null;
- }
- }
- if (!candidate.exists() || !candidate.isFile()) {
- throw new RuntimeException("cannot create file: " + filename);
- }
-
- candidate.setReadable(true, false);
- candidate.setWritable(true, false);
-
- try {
- FileOutputStream fos = new FileOutputStream(candidate);
- try {
- if (exifData != null) {
- ExifOutputStream eos = new ExifOutputStream(fos);
- eos.setExifData(exifData);
- saveBitmapToOutputStream(jc, cropped,
- convertExtensionToCompressFormat(fileExtension), eos);
- } else {
- saveBitmapToOutputStream(jc, cropped,
- convertExtensionToCompressFormat(fileExtension), fos);
- }
- } finally {
- fos.close();
- }
- } catch (IOException e) {
- Log.e(TAG, "fail to save image: "
- + candidate.getAbsolutePath(), e);
- candidate.delete();
- return null;
- }
-
- if (jc.isCancelled()) {
- candidate.delete();
- return null;
- }
-
- return candidate;
- }
-
- private ExifData getExifData(String path) {
- FileInputStream is = null;
- try {
- is = new FileInputStream(path);
- ExifReader reader = new ExifReader();
- ExifData data = reader.read(is);
- return data;
- } catch (Throwable t) {
- Log.w(TAG, "Cannot read EXIF data", t);
- return null;
- } finally {
- Util.closeSilently(is);
- }
- }
-
- private static final String EXIF_SOFTWARE_VALUE = "Android Gallery";
-
- private void changeExifData(ExifData data, int width, int height) {
- data.addTag(ExifTag.TAG_IMAGE_WIDTH).setValue(width);
- data.addTag(ExifTag.TAG_IMAGE_LENGTH).setValue(height);
- data.addTag(ExifTag.TAG_SOFTWARE).setValue(EXIF_SOFTWARE_VALUE);
- data.addTag(ExifTag.TAG_DATE_TIME).setTimeValue(System.currentTimeMillis());
- // Remove the original thumbnail
- // TODO: generate a new thumbnail for the cropped image.
- data.removeThumbnailData();
- }
-
- private Uri saveToMediaProvider(JobContext jc, Bitmap cropped) {
- if (PicasaSource.isPicasaImage(mMediaItem)) {
- return savePicasaImage(jc, cropped);
- } else if (mMediaItem instanceof LocalImage) {
- return saveLocalImage(jc, cropped);
- } else {
- return saveGenericImage(jc, cropped);
- }
- }
-
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
- private static void setImageSize(ContentValues values, int width, int height) {
- // The two fields are available since ICS but got published in JB
- if (ApiHelper.HAS_MEDIA_COLUMNS_WIDTH_AND_HEIGHT) {
- values.put(Images.Media.WIDTH, width);
- values.put(Images.Media.HEIGHT, height);
- }
- }
-
- private Uri savePicasaImage(JobContext jc, Bitmap cropped) {
- if (!DOWNLOAD_BUCKET.isDirectory() && !DOWNLOAD_BUCKET.mkdirs()) {
- throw new RuntimeException("cannot create download folder");
- }
- String filename = PicasaSource.getImageTitle(mMediaItem);
- int pos = filename.lastIndexOf('.');
- if (pos >= 0) filename = filename.substring(0, pos);
- ExifData exifData = new ExifData(ByteOrder.BIG_ENDIAN);
- PicasaSource.extractExifValues(mMediaItem, exifData);
- changeExifData(exifData, cropped.getWidth(), cropped.getHeight());
- File output = saveMedia(jc, cropped, DOWNLOAD_BUCKET, filename, exifData);
- if (output == null) return null;
-
- long now = System.currentTimeMillis() / 1000;
- ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, PicasaSource.getImageTitle(mMediaItem));
- values.put(Images.Media.DISPLAY_NAME, output.getName());
- values.put(Images.Media.DATE_TAKEN, PicasaSource.getDateTaken(mMediaItem));
- values.put(Images.Media.DATE_MODIFIED, now);
- values.put(Images.Media.DATE_ADDED, now);
- values.put(Images.Media.MIME_TYPE, getOutputMimeType());
- values.put(Images.Media.ORIENTATION, 0);
- values.put(Images.Media.DATA, output.getAbsolutePath());
- values.put(Images.Media.SIZE, output.length());
- setImageSize(values, cropped.getWidth(), cropped.getHeight());
-
- double latitude = PicasaSource.getLatitude(mMediaItem);
- double longitude = PicasaSource.getLongitude(mMediaItem);
- if (GalleryUtils.isValidLocation(latitude, longitude)) {
- values.put(Images.Media.LATITUDE, latitude);
- values.put(Images.Media.LONGITUDE, longitude);
- }
- return getContentResolver().insert(
- Images.Media.EXTERNAL_CONTENT_URI, values);
- }
-
- private Uri saveLocalImage(JobContext jc, Bitmap cropped) {
- LocalImage localImage = (LocalImage) mMediaItem;
-
- File oldPath = new File(localImage.filePath);
- File directory = new File(oldPath.getParent());
-
- String filename = oldPath.getName();
- int pos = filename.lastIndexOf('.');
- if (pos >= 0) filename = filename.substring(0, pos);
- File output = null;
-
- ExifData exifData = null;
- if (convertExtensionToCompressFormat(getFileExtension()) == CompressFormat.JPEG) {
- exifData = getExifData(oldPath.getAbsolutePath());
- if (exifData != null) {
- changeExifData(exifData, cropped.getWidth(), cropped.getHeight());
- }
- }
- output = saveMedia(jc, cropped, directory, filename, exifData);
- if (output == null) return null;
-
- long now = System.currentTimeMillis() / 1000;
- ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, localImage.caption);
- values.put(Images.Media.DISPLAY_NAME, output.getName());
- values.put(Images.Media.DATE_TAKEN, localImage.dateTakenInMs);
- values.put(Images.Media.DATE_MODIFIED, now);
- values.put(Images.Media.DATE_ADDED, now);
- values.put(Images.Media.MIME_TYPE, getOutputMimeType());
- values.put(Images.Media.ORIENTATION, 0);
- values.put(Images.Media.DATA, output.getAbsolutePath());
- values.put(Images.Media.SIZE, output.length());
-
- setImageSize(values, cropped.getWidth(), cropped.getHeight());
-
- if (GalleryUtils.isValidLocation(localImage.latitude, localImage.longitude)) {
- values.put(Images.Media.LATITUDE, localImage.latitude);
- values.put(Images.Media.LONGITUDE, localImage.longitude);
- }
- return getContentResolver().insert(
- Images.Media.EXTERNAL_CONTENT_URI, values);
- }
-
- private Uri saveGenericImage(JobContext jc, Bitmap cropped) {
- if (!DOWNLOAD_BUCKET.isDirectory() && !DOWNLOAD_BUCKET.mkdirs()) {
- throw new RuntimeException("cannot create download folder");
- }
-
- long now = System.currentTimeMillis();
- String filename = new SimpleDateFormat(TIME_STAMP_NAME).
- format(new Date(now));
-
- File output = saveMedia(jc, cropped, DOWNLOAD_BUCKET, filename, null);
- if (output == null) return null;
-
- ContentValues values = new ContentValues();
- values.put(Images.Media.TITLE, filename);
- values.put(Images.Media.DISPLAY_NAME, output.getName());
- values.put(Images.Media.DATE_TAKEN, now);
- values.put(Images.Media.DATE_MODIFIED, now / 1000);
- values.put(Images.Media.DATE_ADDED, now / 1000);
- values.put(Images.Media.MIME_TYPE, getOutputMimeType());
- values.put(Images.Media.ORIENTATION, 0);
- values.put(Images.Media.DATA, output.getAbsolutePath());
- values.put(Images.Media.SIZE, output.length());
-
- setImageSize(values, cropped.getWidth(), cropped.getHeight());
-
- return getContentResolver().insert(
- Images.Media.EXTERNAL_CONTENT_URI, values);
- }
-
- private boolean saveBitmapToOutputStream(
- JobContext jc, Bitmap bitmap, CompressFormat format, OutputStream os) {
- // We wrap the OutputStream so that it can be interrupted.
- final InterruptableOutputStream ios = new InterruptableOutputStream(os);
- jc.setCancelListener(new CancelListener() {
- @Override
- public void onCancel() {
- ios.interrupt();
- }
- });
- try {
- bitmap.compress(format, DEFAULT_COMPRESS_QUALITY, ios);
- return !jc.isCancelled();
- } finally {
- jc.setCancelListener(null);
- Utils.closeSilently(ios);
- }
- }
-
- private boolean saveBitmapToUri(JobContext jc, Bitmap bitmap, Uri uri) {
- try {
- OutputStream out = getContentResolver().openOutputStream(uri);
- try {
- return saveBitmapToOutputStream(jc, bitmap,
- convertExtensionToCompressFormat(getFileExtension()), out);
- } finally {
- Utils.closeSilently(out);
- }
- } catch (FileNotFoundException e) {
- Log.w(TAG, "cannot write output", e);
- }
- return true;
- }
-
- private CompressFormat convertExtensionToCompressFormat(String extension) {
- return extension.equals("png")
- ? CompressFormat.PNG
- : CompressFormat.JPEG;
- }
-
- private String getOutputMimeType() {
- return getFileExtension().equals("png") ? "image/png" : "image/jpeg";
- }
-
- private String getFileExtension() {
- String requestFormat = getIntent().getStringExtra(KEY_OUTPUT_FORMAT);
- String outputFormat = (requestFormat == null)
- ? determineCompressFormat(mMediaItem)
- : requestFormat;
-
- outputFormat = outputFormat.toLowerCase();
- return (outputFormat.equals("png") || outputFormat.equals("gif"))
- ? "png" // We don't support gif compression.
- : "jpg";
- }
-
- private void onSaveClicked() {
- Bundle extra = getIntent().getExtras();
- RectF cropRect = mCropView.getCropRectangle();
- if (cropRect == null) return;
- mState = STATE_SAVING;
- int messageId = extra != null && extra.getBoolean(KEY_SET_AS_WALLPAPER)
- ? R.string.wallpaper
- : R.string.saving_image;
- mProgressDialog = ProgressDialog.show(
- this, null, getString(messageId), true, false);
- mSaveTask = getThreadPool().submit(new SaveOutput(cropRect),
- new FutureListener<Intent>() {
- @Override
- public void onFutureDone(Future<Intent> future) {
- mSaveTask = null;
- if (future.isCancelled()) return;
- Intent intent = future.get();
- if (intent != null) {
- mMainHandler.sendMessage(mMainHandler.obtainMessage(
- MSG_SAVE_COMPLETE, intent));
- } else {
- mMainHandler.sendEmptyMessage(MSG_SHOW_SAVE_ERROR);
- }
- }
- });
- }
-
- private Bitmap getCroppedImage(Rect rect) {
- Utils.assertTrue(rect.width() > 0 && rect.height() > 0);
-
- Bundle extras = getIntent().getExtras();
- // (outputX, outputY) = the width and height of the returning bitmap.
- int outputX = rect.width();
- int outputY = rect.height();
- if (extras != null) {
- outputX = extras.getInt(KEY_OUTPUT_X, outputX);
- outputY = extras.getInt(KEY_OUTPUT_Y, outputY);
- }
-
- if (outputX * outputY > MAX_PIXEL_COUNT) {
- float scale = FloatMath.sqrt((float) MAX_PIXEL_COUNT / outputX / outputY);
- Log.w(TAG, "scale down the cropped image: " + scale);
- outputX = Math.round(scale * outputX);
- outputY = Math.round(scale * outputY);
- }
-
- // (rect.width() * scaleX, rect.height() * scaleY) =
- // the size of drawing area in output bitmap
- float scaleX = 1;
- float scaleY = 1;
- Rect dest = new Rect(0, 0, outputX, outputY);
- if (extras == null || extras.getBoolean(KEY_SCALE, true)) {
- scaleX = (float) outputX / rect.width();
- scaleY = (float) outputY / rect.height();
- if (extras == null || !extras.getBoolean(
- KEY_SCALE_UP_IF_NEEDED, false)) {
- if (scaleX > 1f) scaleX = 1;
- if (scaleY > 1f) scaleY = 1;
- }
- }
-
- // Keep the content in the center (or crop the content)
- int rectWidth = Math.round(rect.width() * scaleX);
- int rectHeight = Math.round(rect.height() * scaleY);
- dest.set(Math.round((outputX - rectWidth) / 2f),
- Math.round((outputY - rectHeight) / 2f),
- Math.round((outputX + rectWidth) / 2f),
- Math.round((outputY + rectHeight) / 2f));
-
- if (mBitmapInIntent != null) {
- Bitmap source = mBitmapInIntent;
- Bitmap result = Bitmap.createBitmap(
- outputX, outputY, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- canvas.drawBitmap(source, rect, dest, null);
- return result;
- }
-
- if (mUseRegionDecoder) {
- int rotation = mMediaItem.getFullImageRotation();
- rotateRectangle(rect, mCropView.getImageWidth(),
- mCropView.getImageHeight(), 360 - rotation);
- rotateRectangle(dest, outputX, outputY, 360 - rotation);
-
- BitmapFactory.Options options = new BitmapFactory.Options();
- int sample = BitmapUtils.computeSampleSizeLarger(
- Math.max(scaleX, scaleY));
- options.inSampleSize = sample;
-
- // The decoding result is what we want if
- // 1. The size of the decoded bitmap match the destination's size
- // 2. The destination covers the whole output bitmap
- // 3. No rotation
- if ((rect.width() / sample) == dest.width()
- && (rect.height() / sample) == dest.height()
- && (outputX == dest.width()) && (outputY == dest.height())
- && rotation == 0) {
- // To prevent concurrent access in GLThread
- synchronized (mRegionDecoder) {
- return mRegionDecoder.decodeRegion(rect, options);
- }
- }
- Bitmap result = Bitmap.createBitmap(
- outputX, outputY, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- rotateCanvas(canvas, outputX, outputY, rotation);
- drawInTiles(canvas, mRegionDecoder, rect, dest, sample);
- return result;
- } else {
- int rotation = mMediaItem.getRotation();
- rotateRectangle(rect, mCropView.getImageWidth(),
- mCropView.getImageHeight(), 360 - rotation);
- rotateRectangle(dest, outputX, outputY, 360 - rotation);
- Bitmap result = Bitmap.createBitmap(outputX, outputY, Config.ARGB_8888);
- Canvas canvas = new Canvas(result);
- rotateCanvas(canvas, outputX, outputY, rotation);
- canvas.drawBitmap(mBitmap,
- rect, dest, new Paint(Paint.FILTER_BITMAP_FLAG));
- return result;
- }
- }
-
- private static void rotateCanvas(
- Canvas canvas, int width, int height, int rotation) {
- canvas.translate(width / 2, height / 2);
- canvas.rotate(rotation);
- if (((rotation / 90) & 0x01) == 0) {
- canvas.translate(-width / 2, -height / 2);
- } else {
- canvas.translate(-height / 2, -width / 2);
- }
- }
-
- private static void rotateRectangle(
- Rect rect, int width, int height, int rotation) {
- if (rotation == 0 || rotation == 360) return;
-
- int w = rect.width();
- int h = rect.height();
- switch (rotation) {
- case 90: {
- rect.top = rect.left;
- rect.left = height - rect.bottom;
- rect.right = rect.left + h;
- rect.bottom = rect.top + w;
- return;
- }
- case 180: {
- rect.left = width - rect.right;
- rect.top = height - rect.bottom;
- rect.right = rect.left + w;
- rect.bottom = rect.top + h;
- return;
- }
- case 270: {
- rect.left = rect.top;
- rect.top = width - rect.right;
- rect.right = rect.left + h;
- rect.bottom = rect.top + w;
- return;
- }
- default: throw new AssertionError();
- }
- }
-
- private void drawInTiles(Canvas canvas,
- BitmapRegionDecoder decoder, Rect rect, Rect dest, int sample) {
- int tileSize = TILE_SIZE * sample;
- Rect tileRect = new Rect();
- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inPreferredConfig = Config.ARGB_8888;
- options.inSampleSize = sample;
- canvas.translate(dest.left, dest.top);
- canvas.scale((float) sample * dest.width() / rect.width(),
- (float) sample * dest.height() / rect.height());
- Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
- for (int tx = rect.left, x = 0;
- tx < rect.right; tx += tileSize, x += TILE_SIZE) {
- for (int ty = rect.top, y = 0;
- ty < rect.bottom; ty += tileSize, y += TILE_SIZE) {
- tileRect.set(tx, ty, tx + tileSize, ty + tileSize);
- if (tileRect.intersect(rect)) {
- Bitmap bitmap;
-
- // To prevent concurrent access in GLThread
- synchronized (decoder) {
- bitmap = decoder.decodeRegion(tileRect, options);
- }
- canvas.drawBitmap(bitmap, x, y, paint);
- bitmap.recycle();
- }
- }
- }
- }
-
- private void onBitmapRegionDecoderAvailable(
- BitmapRegionDecoder regionDecoder) {
-
- if (regionDecoder == null) {
- Toast.makeText(this, R.string.fail_to_load_image, Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
- mRegionDecoder = regionDecoder;
- mUseRegionDecoder = true;
- mState = STATE_LOADED;
-
- BitmapFactory.Options options = new BitmapFactory.Options();
- int width = regionDecoder.getWidth();
- int height = regionDecoder.getHeight();
- options.inSampleSize = BitmapUtils.computeSampleSize(width, height,
- BitmapUtils.UNCONSTRAINED, BACKUP_PIXEL_COUNT);
- mBitmap = regionDecoder.decodeRegion(
- new Rect(0, 0, width, height), options);
-
- mBitmapScreenNail = new BitmapScreenNail(mBitmap);
-
- TileImageViewAdapter adapter = new TileImageViewAdapter();
- adapter.setScreenNail(mBitmapScreenNail, width, height);
- adapter.setRegionDecoder(regionDecoder);
-
- mCropView.setDataModel(adapter, mMediaItem.getFullImageRotation());
- if (mDoFaceDetection) {
- mCropView.detectFaces(mBitmap);
- } else {
- mCropView.initializeHighlightRectangle();
- }
- }
-
- private void onBitmapAvailable(Bitmap bitmap) {
- if (bitmap == null) {
- Toast.makeText(this, R.string.fail_to_load_image, Toast.LENGTH_SHORT).show();
- finish();
- return;
- }
- mUseRegionDecoder = false;
- mState = STATE_LOADED;
-
- mBitmap = bitmap;
- BitmapFactory.Options options = new BitmapFactory.Options();
- mCropView.setDataModel(new BitmapTileProvider(bitmap, 512),
- mMediaItem.getRotation());
- if (mDoFaceDetection) {
- mCropView.detectFaces(bitmap);
- } else {
- mCropView.initializeHighlightRectangle();
- }
- }
-
- private void setCropParameters() {
- Bundle extras = getIntent().getExtras();
- if (extras == null)
- return;
- int aspectX = extras.getInt(KEY_ASPECT_X, 0);
- int aspectY = extras.getInt(KEY_ASPECT_Y, 0);
- if (aspectX != 0 && aspectY != 0) {
- mCropView.setAspectRatio((float) aspectX / aspectY);
- }
-
- float spotlightX = extras.getFloat(KEY_SPOTLIGHT_X, 0);
- float spotlightY = extras.getFloat(KEY_SPOTLIGHT_Y, 0);
- if (spotlightX != 0 && spotlightY != 0) {
- mCropView.setSpotlightRatio(spotlightX, spotlightY);
- }
- }
-
- private void initializeData() {
- Bundle extras = getIntent().getExtras();
-
- if (extras != null) {
- if (extras.containsKey(KEY_NO_FACE_DETECTION)) {
- mDoFaceDetection = !extras.getBoolean(KEY_NO_FACE_DETECTION);
- }
-
- mBitmapInIntent = extras.getParcelable(KEY_DATA);
-
- if (mBitmapInIntent != null) {
- mBitmapTileProvider =
- new BitmapTileProvider(mBitmapInIntent, MAX_BACKUP_IMAGE_SIZE);
- mCropView.setDataModel(mBitmapTileProvider, 0);
- if (mDoFaceDetection) {
- mCropView.detectFaces(mBitmapInIntent);
- } else {
- mCropView.initializeHighlightRectangle();
- }
- mState = STATE_LOADED;
- return;
- }
- }
-
- mProgressDialog = ProgressDialog.show(
- this, null, getString(R.string.loading_image), true, true);
- mProgressDialog.setCanceledOnTouchOutside(false);
- mProgressDialog.setCancelMessage(mMainHandler.obtainMessage(MSG_CANCEL_DIALOG));
-
- mMediaItem = getMediaItemFromIntentData();
- if (mMediaItem == null) return;
-
- boolean supportedByBitmapRegionDecoder =
- (mMediaItem.getSupportedOperations() & MediaItem.SUPPORT_FULL_IMAGE) != 0;
- if (supportedByBitmapRegionDecoder) {
- mLoadTask = getThreadPool().submit(new LoadDataTask(mMediaItem),
- new FutureListener<BitmapRegionDecoder>() {
- @Override
- public void onFutureDone(Future<BitmapRegionDecoder> future) {
- mLoadTask = null;
- BitmapRegionDecoder decoder = future.get();
- if (future.isCancelled()) {
- if (decoder != null) decoder.recycle();
- return;
- }
- mMainHandler.sendMessage(mMainHandler.obtainMessage(
- MSG_LARGE_BITMAP, decoder));
- }
- });
- } else {
- mLoadBitmapTask = getThreadPool().submit(new LoadBitmapDataTask(mMediaItem),
- new FutureListener<Bitmap>() {
- @Override
- public void onFutureDone(Future<Bitmap> future) {
- mLoadBitmapTask = null;
- Bitmap bitmap = future.get();
- if (future.isCancelled()) {
- if (bitmap != null) bitmap.recycle();
- return;
- }
- mMainHandler.sendMessage(mMainHandler.obtainMessage(
- MSG_BITMAP, bitmap));
- }
- });
- }
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- if (mState == STATE_INIT) initializeData();
- if (mState == STATE_SAVING) onSaveClicked();
-
- // TODO: consider to do it in GLView system
- GLRoot root = getGLRoot();
- root.lockRenderThread();
- try {
- mCropView.resume();
- } finally {
- root.unlockRenderThread();
- }
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- dismissProgressDialogIfShown();
-
- Future<BitmapRegionDecoder> loadTask = mLoadTask;
- if (loadTask != null && !loadTask.isDone()) {
- // load in progress, try to cancel it
- loadTask.cancel();
- loadTask.waitDone();
- }
-
- Future<Bitmap> loadBitmapTask = mLoadBitmapTask;
- if (loadBitmapTask != null && !loadBitmapTask.isDone()) {
- // load in progress, try to cancel it
- loadBitmapTask.cancel();
- loadBitmapTask.waitDone();
- }
-
- Future<Intent> saveTask = mSaveTask;
- if (saveTask != null && !saveTask.isDone()) {
- // save in progress, try to cancel it
- saveTask.cancel();
- saveTask.waitDone();
- }
- GLRoot root = getGLRoot();
- root.lockRenderThread();
- try {
- mCropView.pause();
- } finally {
- root.unlockRenderThread();
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (mBitmapScreenNail != null) {
- mBitmapScreenNail.recycle();
- mBitmapScreenNail = null;
- }
- }
-
- private void dismissProgressDialogIfShown() {
- if (mProgressDialog != null) {
- mProgressDialog.dismiss();
- mProgressDialog = null;
- }
- }
-
- private MediaItem getMediaItemFromIntentData() {
- Uri uri = getIntent().getData();
- DataManager manager = getDataManager();
- Path path = manager.findPathByUri(uri, getIntent().getType());
- if (path == null) {
- Log.w(TAG, "cannot get path for: " + uri + ", or no data given");
- return null;
- }
- return (MediaItem) manager.getMediaObject(path);
- }
-
- private class LoadDataTask implements Job<BitmapRegionDecoder> {
- MediaItem mItem;
-
- public LoadDataTask(MediaItem item) {
- mItem = item;
- }
-
- @Override
- public BitmapRegionDecoder run(JobContext jc) {
- return mItem == null ? null : mItem.requestLargeImage().run(jc);
- }
- }
-
- private class LoadBitmapDataTask implements Job<Bitmap> {
- MediaItem mItem;
-
- public LoadBitmapDataTask(MediaItem item) {
- mItem = item;
- }
- @Override
- public Bitmap run(JobContext jc) {
- return mItem == null
- ? null
- : mItem.requestImage(MediaItem.TYPE_THUMBNAIL).run(jc);
- }
- }
-}
diff --git a/src/com/android/gallery3d/app/FilmstripPage.java b/src/com/android/gallery3d/app/FilmstripPage.java
new file mode 100644
index 000000000..a9726cdc9
--- /dev/null
+++ b/src/com/android/gallery3d/app/FilmstripPage.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+public class FilmstripPage extends PhotoPage {
+
+}
diff --git a/src/com/android/gallery3d/app/Gallery.java b/src/com/android/gallery3d/app/Gallery.java
index e28404fac..354e325d4 100644
--- a/src/com/android/gallery3d/app/Gallery.java
+++ b/src/com/android/gallery3d/app/Gallery.java
@@ -26,10 +26,9 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.OpenableColumns;
-import android.view.Window;
-import android.view.WindowManager;
import android.widget.Toast;
+import com.actionbarsherlock.view.Window;
import com.android.gallery3d.R;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.DataManager;
@@ -49,7 +48,6 @@ public final class Gallery extends AbstractGalleryActivity implements OnCancelLi
public static final String KEY_GET_ALBUM = "get-album";
public static final String KEY_TYPE_BITS = "type-bits";
public static final String KEY_MEDIA_TYPES = "mediaTypes";
- public static final String KEY_DISMISS_KEYGUARD = "dismiss-keyguard";
private static final String TAG = "Gallery";
private Dialog mVersionCheckDialog;
@@ -60,11 +58,6 @@ public final class Gallery extends AbstractGalleryActivity implements OnCancelLi
requestWindowFeature(Window.FEATURE_ACTION_BAR);
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
- if (getIntent().getBooleanExtra(KEY_DISMISS_KEYGUARD, false)) {
- getWindow().addFlags(
- WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
- }
-
setContentView(R.layout.main);
if (savedInstanceState != null) {
@@ -222,7 +215,7 @@ public final class Gallery extends AbstractGalleryActivity implements OnCancelLi
}
}
- getStateManager().startState(PhotoPage.class, data);
+ getStateManager().startState(SinglePhotoPage.class, data);
}
}
}
diff --git a/src/com/android/gallery3d/app/GalleryActionBar.java b/src/com/android/gallery3d/app/GalleryActionBar.java
index 0fb5e51b4..49f4186e8 100644
--- a/src/com/android/gallery3d/app/GalleryActionBar.java
+++ b/src/com/android/gallery3d/app/GalleryActionBar.java
@@ -17,9 +17,6 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.ActionBar.OnMenuVisibilityListener;
-import android.app.ActionBar.OnNavigationListener;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
@@ -27,15 +24,18 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
-import android.widget.ShareActionProvider;
import android.widget.TextView;
import android.widget.TwoLineListItem;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.ActionBar.OnMenuVisibilityListener;
+import com.actionbarsherlock.app.ActionBar.OnNavigationListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.widget.ShareActionProvider;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
@@ -186,7 +186,7 @@ public class GalleryActionBar implements OnNavigationListener {
}
public GalleryActionBar(AbstractGalleryActivity activity) {
- mActionBar = activity.getActionBar();
+ mActionBar = activity.getSupportActionBar();
mContext = activity.getAndroidContext();
mActivity = activity;
mInflater = ((Activity) mActivity).getLayoutInflater();
@@ -396,7 +396,7 @@ public class GalleryActionBar implements OnNavigationListener {
private Intent mShareIntent;
public void createActionBarMenu(int menuRes, Menu menu) {
- mActivity.getMenuInflater().inflate(menuRes, menu);
+ mActivity.getSupportMenuInflater().inflate(menuRes, menu);
mActionBarMenu = menu;
MenuItem item = menu.findItem(R.id.action_share_panorama);
diff --git a/src/com/android/gallery3d/app/MovieActivity.java b/src/com/android/gallery3d/app/MovieActivity.java
index 3123644c7..d725e6d12 100644
--- a/src/com/android/gallery3d/app/MovieActivity.java
+++ b/src/com/android/gallery3d/app/MovieActivity.java
@@ -17,8 +17,6 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.ActionBar;
-import android.app.Activity;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Intent;
@@ -33,13 +31,15 @@ import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.OpenableColumns;
import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
-import android.widget.ShareActionProvider;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.widget.ShareActionProvider;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.common.Utils;
@@ -51,7 +51,7 @@ import com.android.gallery3d.common.Utils;
* to set the action bar logo so the playback process looks more seamlessly integrated with
* the original activity.
*/
-public class MovieActivity extends Activity {
+public class MovieActivity extends SherlockActivity {
@SuppressWarnings("unused")
private static final String TAG = "MovieActivity";
public static final String KEY_LOGO_BITMAP = "logo-bitmap";
@@ -75,8 +75,8 @@ public class MovieActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- requestWindowFeature(Window.FEATURE_ACTION_BAR);
- requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
+ getSherlock().requestFeature(Window.FEATURE_ACTION_BAR);
+ getSherlock().requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
setContentView(R.layout.movie_view);
View rootView = findViewById(R.id.movie_view_root);
@@ -119,14 +119,14 @@ public class MovieActivity extends Activity {
private void setActionBarLogoFromIntent(Intent intent) {
Bitmap logo = intent.getParcelableExtra(KEY_LOGO_BITMAP);
if (logo != null) {
- getActionBar().setLogo(
+ getSupportActionBar().setLogo(
new BitmapDrawable(getResources(), logo));
}
}
private void initializeActionBar(Intent intent) {
mUri = intent.getData();
- final ActionBar actionBar = getActionBar();
+ final ActionBar actionBar = getSupportActionBar();
setActionBarLogoFromIntent(intent);
actionBar.setDisplayOptions(
ActionBar.DISPLAY_HOME_AS_UP,
@@ -166,7 +166,7 @@ public class MovieActivity extends Activity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
- getMenuInflater().inflate(R.menu.movie, menu);
+ getSupportMenuInflater().inflate(R.menu.movie, menu);
// Document says EXTRA_STREAM should be a content: Uri
// So, we only share the video if it's "content:".
diff --git a/src/com/android/gallery3d/app/MoviePlayer.java b/src/com/android/gallery3d/app/MoviePlayer.java
index 85dc4427e..00e4cd63b 100644
--- a/src/com/android/gallery3d/app/MoviePlayer.java
+++ b/src/com/android/gallery3d/app/MoviePlayer.java
@@ -74,8 +74,8 @@ public class MoviePlayer implements
private static final long RESUMEABLE_TIMEOUT = 3 * 60 * 1000; // 3 mins
private Context mContext;
- private final View mRootView;
private final VideoView mVideoView;
+ private final View mRootView;
private final Bookmarker mBookmarker;
private final Uri mUri;
private final Handler mHandler = new Handler();
@@ -191,7 +191,6 @@ public class MoviePlayer implements
if ((diff & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
&& (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
mController.show();
- mRootView.setBackgroundColor(Color.BLACK);
}
}
});
diff --git a/src/com/android/gallery3d/app/MuteVideo.java b/src/com/android/gallery3d/app/MuteVideo.java
new file mode 100644
index 000000000..012b682ef
--- /dev/null
+++ b/src/com/android/gallery3d/app/MuteVideo.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.widget.Toast;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.util.SaveVideoFileInfo;
+import com.android.gallery3d.util.SaveVideoFileUtils;
+
+import java.io.IOException;
+
+public class MuteVideo {
+
+ private ProgressDialog mMuteProgress;
+
+ private MediaItem mCurrentItem = null;
+ private Uri mUri = null;
+ private SaveVideoFileInfo mDstFileInfo = null;
+ private Activity mActivity = null;
+ private final Handler mHandler = new Handler();
+
+ final String TIME_STAMP_NAME = "'MUTE'_yyyyMMdd_HHmmss";
+
+ public MuteVideo(MediaItem current, Uri uri, Activity activity) {
+ mUri = uri;
+ mCurrentItem = current;
+ mActivity = activity;
+ }
+
+ public void muteInBackground() {
+ mDstFileInfo = SaveVideoFileUtils.getDstMp4FileInfo(TIME_STAMP_NAME,
+ mActivity.getContentResolver(), mUri,
+ mActivity.getString(R.string.folder_download));
+
+ showProgressDialog();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ VideoUtils.startMute(mCurrentItem.getFilePath(), mDstFileInfo);
+ SaveVideoFileUtils.insertContent(
+ mDstFileInfo, mActivity.getContentResolver(), mUri);
+ } catch (IOException e) {
+ Toast.makeText(mActivity, mActivity.getString(R.string.video_mute_err),
+ Toast.LENGTH_SHORT).show();
+ }
+ // After muting is done, trigger the UI changed.
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(mActivity.getApplicationContext(),
+ mActivity.getString(R.string.save_into,
+ mDstFileInfo.mFolderName),
+ Toast.LENGTH_SHORT)
+ .show();
+
+ if (mMuteProgress != null) {
+ mMuteProgress.dismiss();
+ mMuteProgress = null;
+
+ // Show the result only when the activity not
+ // stopped.
+ Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
+ intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*");
+ intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false);
+ mActivity.startActivity(intent);
+ }
+ }
+ });
+ }
+ }).start();
+ }
+
+ private void showProgressDialog() {
+ mMuteProgress = new ProgressDialog(mActivity);
+ mMuteProgress.setTitle(mActivity.getString(R.string.muting));
+ mMuteProgress.setMessage(mActivity.getString(R.string.please_wait));
+ mMuteProgress.setCancelable(false);
+ mMuteProgress.setCanceledOnTouchOutside(false);
+ mMuteProgress.show();
+ }
+}
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 506d1ca6f..3c0c4330b 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -17,7 +17,6 @@
package com.android.gallery3d.app;
import android.annotation.TargetApi;
-import android.app.ActionBar.OnMenuVisibilityListener;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
@@ -32,13 +31,15 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
-import android.view.Menu;
-import android.view.MenuItem;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.android.camera.CameraActivity;
import com.android.camera.ProxyLauncher;
+
+import com.actionbarsherlock.app.ActionBar.OnMenuVisibilityListener;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.R;
import com.android.gallery3d.common.ApiHelper;
import com.android.gallery3d.data.ComboAlbum;
@@ -71,7 +72,7 @@ import com.android.gallery3d.ui.SelectionManager;
import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.util.GalleryUtils;
-public class PhotoPage extends ActivityState implements
+public abstract class PhotoPage extends ActivityState implements
PhotoView.Listener, AppBridge.Server,
PhotoPageBottomControls.Delegate, GalleryActionBar.OnAlbumModeSelectedListener {
private static final String TAG = "PhotoPage";
@@ -1019,11 +1020,16 @@ public class PhotoPage extends ActivityState implements
refreshHidingMessage();
MediaItem current = mModel.getMediaItem(0);
+ // This is a shield for monkey when it clicks the action bar
+ // menu when transitioning from filmstrip to camera
+ if (current instanceof SnailItem) return true;
+ // TODO: We should check the current photo against the MediaItem
+ // that the menu was initially created for. We need to fix this
+ // after PhotoPage being refactored.
if (current == null) {
// item is not ready, ignore
return true;
}
-
int currentIndex = mModel.getCurrentIndex();
Path path = current.getPath();
@@ -1064,6 +1070,12 @@ public class PhotoPage extends ActivityState implements
mActivity.startActivityForResult(intent, REQUEST_TRIM);
return true;
}
+ case R.id.action_mute: {
+ MuteVideo muteVideo = new MuteVideo(current,
+ manager.getContentUri(path), mActivity);
+ muteVideo.muteInBackground();
+ return true;
+ }
case R.id.action_edit: {
launchPhotoEditor();
return true;
@@ -1156,9 +1168,7 @@ public class PhotoPage extends ActivityState implements
} else if (goBack) {
onBackPressed();
} else if (unlock) {
- Intent intent = new Intent(mActivity, Gallery.class);
- intent.putExtra(Gallery.KEY_DISMISS_KEYGUARD, true);
- mActivity.startActivity(intent);
+ mActivity.getStateManager().finishState(this);
} else if (launchCamera) {
launchCamera();
} else {
@@ -1242,7 +1252,7 @@ public class PhotoPage extends ActivityState implements
Bundle data = new Bundle(getData());
data.putString(KEY_MEDIA_SET_PATH, albumPath.toString());
data.putString(PhotoPage.KEY_MEDIA_ITEM_PATH, path.toString());
- mActivity.getStateManager().startState(PhotoPage.class, data);
+ mActivity.getStateManager().startState(SinglePhotoPage.class, data);
return;
}
mModel.setCurrentPhoto(path, mCurrentIndex);
diff --git a/src/com/android/gallery3d/app/PickerActivity.java b/src/com/android/gallery3d/app/PickerActivity.java
index d5bb218ea..1eb95d0c6 100644
--- a/src/com/android/gallery3d/app/PickerActivity.java
+++ b/src/com/android/gallery3d/app/PickerActivity.java
@@ -17,13 +17,13 @@
package com.android.gallery3d.app;
import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
-import android.view.Window;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuInflater;
+import com.actionbarsherlock.view.MenuItem;
+import com.actionbarsherlock.view.Window;
import com.android.gallery3d.R;
import com.android.gallery3d.ui.GLRootView;
@@ -62,7 +62,7 @@ public class PickerActivity extends AbstractGalleryActivity
@Override
public boolean onCreateOptionsMenu(Menu menu) {
- MenuInflater inflater = getMenuInflater();
+ MenuInflater inflater = getSupportMenuInflater();
inflater.inflate(R.menu.pickup, menu);
return true;
}
diff --git a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
index 00f2fe78f..f0848ad22 100644
--- a/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
+++ b/src/com/android/gallery3d/app/SinglePhotoDataAdapter.java
@@ -34,6 +34,7 @@ import com.android.gallery3d.ui.SynchronizedHandler;
import com.android.gallery3d.ui.TileImageViewAdapter;
import com.android.gallery3d.util.Future;
import com.android.gallery3d.util.FutureListener;
+import com.android.gallery3d.util.LightCycleHelper;
import com.android.gallery3d.util.ThreadPool;
public class SinglePhotoDataAdapter extends TileImageViewAdapter
diff --git a/src/com/android/gallery3d/app/SinglePhotoPage.java b/src/com/android/gallery3d/app/SinglePhotoPage.java
new file mode 100644
index 000000000..beb87d358
--- /dev/null
+++ b/src/com/android/gallery3d/app/SinglePhotoPage.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+public class SinglePhotoPage extends PhotoPage {
+
+}
diff --git a/src/com/android/gallery3d/app/StateManager.java b/src/com/android/gallery3d/app/StateManager.java
index d77279f78..64daa6afe 100644
--- a/src/com/android/gallery3d/app/StateManager.java
+++ b/src/com/android/gallery3d/app/StateManager.java
@@ -21,10 +21,10 @@ import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Parcelable;
-import android.view.Menu;
-import android.view.MenuItem;
import com.android.gallery3d.anim.StateTransitionAnimation;
+import com.actionbarsherlock.view.Menu;
+import com.actionbarsherlock.view.MenuItem;
import com.android.gallery3d.common.Utils;
import java.util.Stack;
diff --git a/src/com/android/gallery3d/app/TrimControllerOverlay.java b/src/com/android/gallery3d/app/TrimControllerOverlay.java
index 9127ad159..cae016626 100644
--- a/src/com/android/gallery3d/app/TrimControllerOverlay.java
+++ b/src/com/android/gallery3d/app/TrimControllerOverlay.java
@@ -23,6 +23,8 @@ import android.content.Context;
import android.view.MotionEvent;
import android.view.View;
+import com.android.gallery3d.common.ApiHelper;
+
/**
* The controller for the Trimming Video.
*/
@@ -41,36 +43,41 @@ public class TrimControllerOverlay extends CommonControllerOverlay {
if (mState == State.PLAYING) {
mPlayPauseReplayView.setVisibility(View.INVISIBLE);
}
- mPlayPauseReplayView.setAlpha(1f);
+ if (ApiHelper.HAS_OBJECT_ANIMATION) {
+ mPlayPauseReplayView.setAlpha(1f);
+ }
}
@Override
public void showPlaying() {
super.showPlaying();
+ if (ApiHelper.HAS_OBJECT_ANIMATION) {
+ // Add animation to hide the play button while playing.
+ ObjectAnimator anim = ObjectAnimator.ofFloat(mPlayPauseReplayView, "alpha", 1f, 0f);
+ anim.setDuration(200);
+ anim.start();
+ anim.addListener(new AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ }
- // Add animation to hide the play button while playing.
- ObjectAnimator anim = ObjectAnimator.ofFloat(mPlayPauseReplayView, "alpha", 1f, 0f);
- anim.setDuration(200);
- anim.start();
- anim.addListener(new AnimatorListener() {
- @Override
- public void onAnimationStart(Animator animation) {
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- hidePlayButtonIfPlaying();
- }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ hidePlayButtonIfPlaying();
+ }
- @Override
- public void onAnimationCancel(Animator animation) {
- hidePlayButtonIfPlaying();
- }
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ hidePlayButtonIfPlaying();
+ }
- @Override
- public void onAnimationRepeat(Animator animation) {
- }
- });
+ @Override
+ public void onAnimationRepeat(Animator animation) {
+ }
+ });
+ } else {
+ hidePlayButtonIfPlaying();
+ }
}
@Override
diff --git a/src/com/android/gallery3d/app/TrimVideo.java b/src/com/android/gallery3d/app/TrimVideo.java
index 38b403b10..7a76be5dc 100644
--- a/src/com/android/gallery3d/app/TrimVideo.java
+++ b/src/com/android/gallery3d/app/TrimVideo.java
@@ -16,8 +16,6 @@
package com.android.gallery3d.app;
-import android.app.ActionBar;
-import android.app.Activity;
import android.app.ProgressDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
@@ -39,20 +37,25 @@ import android.widget.TextView;
import android.widget.Toast;
import android.widget.VideoView;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.app.SherlockActivity;
import com.android.gallery3d.R;
import com.android.gallery3d.util.BucketNames;
+import com.android.gallery3d.util.SaveVideoFileInfo;
+import com.android.gallery3d.util.SaveVideoFileUtils;
import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.text.SimpleDateFormat;
-public class TrimVideo extends Activity implements
+public class TrimVideo extends SherlockActivity implements
MediaPlayer.OnErrorListener,
MediaPlayer.OnCompletionListener,
ControllerOverlay.Listener {
private VideoView mVideoView;
+ private TextView mSaveVideoTextView;
private TrimControllerOverlay mController;
private Context mContext;
private Uri mUri;
@@ -70,13 +73,8 @@ public class TrimVideo extends Activity implements
private boolean mHasPaused = false;
private String mSrcVideoPath = null;
- private String mSaveFileName = null;
private static final String TIME_STAMP_NAME = "'TRIM'_yyyyMMdd_HHmmss";
- private File mSrcFile = null;
- private File mDstFile = null;
- private File mSaveDirectory = null;
- // For showing the result.
- private String saveFolderName = null;
+ private SaveVideoFileInfo mDstFileInfo = null;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -86,20 +84,21 @@ public class TrimVideo extends Activity implements
requestWindowFeature(Window.FEATURE_ACTION_BAR);
requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
- ActionBar actionBar = getActionBar();
+ ActionBar actionBar = getSupportActionBar();
int displayOptions = ActionBar.DISPLAY_SHOW_HOME;
actionBar.setDisplayOptions(0, displayOptions);
displayOptions = ActionBar.DISPLAY_SHOW_CUSTOM;
actionBar.setDisplayOptions(displayOptions, displayOptions);
actionBar.setCustomView(R.layout.trim_menu);
- TextView mSaveVideoTextView = (TextView) findViewById(R.id.start_trim);
+ mSaveVideoTextView = (TextView) findViewById(R.id.start_trim);
mSaveVideoTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View arg0) {
trimVideo();
}
});
+ mSaveVideoTextView.setEnabled(false);
Intent intent = getIntent();
mUri = intent.getData();
@@ -221,72 +220,24 @@ public class TrimVideo extends Activity implements
mController.showPaused();
}
- // Copy from SaveCopyTask.java in terms of how to handle the destination
- // path and filename : querySource() and getSaveDirectory().
- private interface ContentResolverQueryCallback {
- void onCursorResult(Cursor cursor);
- }
-
- private void querySource(String[] projection, ContentResolverQueryCallback callback) {
- ContentResolver contentResolver = getContentResolver();
- Cursor cursor = null;
- try {
- cursor = contentResolver.query(mUri, 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 File getSaveDirectory() {
- final File[] dir = new File[1];
- querySource(new String[] {
- VideoColumns.DATA }, new ContentResolverQueryCallback() {
- @Override
- public void onCursorResult(Cursor cursor) {
- dir[0] = new File(cursor.getString(0)).getParentFile();
- }
- });
- return dir[0];
- }
-
- private void trimVideo() {
+ private boolean isModified() {
int delta = mTrimEndTime - mTrimStartTime;
+
// Considering that we only trim at sync frame, we don't want to trim
// when the time interval is too short or too close to the origin.
- if (delta < 100 ) {
- Toast.makeText(getApplicationContext(),
- getString(R.string.trim_too_short),
- Toast.LENGTH_SHORT).show();
- return;
- }
- if (Math.abs(mVideoView.getDuration() - delta) < 100) {
- // If no change has been made, go back
- onBackPressed();
- return;
- }
- // Use the default save directory if the source directory cannot be
- // saved.
- mSaveDirectory = getSaveDirectory();
- if ((mSaveDirectory == null) || !mSaveDirectory.canWrite()) {
- mSaveDirectory = new File(Environment.getExternalStorageDirectory(),
- BucketNames.DOWNLOAD);
- saveFolderName = getString(R.string.folder_download);
+ if (delta < 100 || Math.abs(mVideoView.getDuration() - delta) < 100) {
+ return false;
} else {
- saveFolderName = mSaveDirectory.getName();
+ return true;
}
- mSaveFileName = new SimpleDateFormat(TIME_STAMP_NAME).format(
- new Date(System.currentTimeMillis()));
+ }
- mDstFile = new File(mSaveDirectory, mSaveFileName + ".mp4");
- mSrcFile = new File(mSrcVideoPath);
+ private void trimVideo() {
+
+ mDstFileInfo = SaveVideoFileUtils.getDstMp4FileInfo(TIME_STAMP_NAME,
+ getContentResolver(), mUri, getString(R.string.folder_download));
+ final File mSrcFile = new File(mSrcVideoPath);
showProgressDialog();
@@ -294,9 +245,11 @@ public class TrimVideo extends Activity implements
@Override
public void run() {
try {
- TrimVideoUtils.startTrim(mSrcFile, mDstFile, mTrimStartTime, mTrimEndTime);
+ VideoUtils.startTrim(mSrcFile, mDstFileInfo.mFile,
+ mTrimStartTime, mTrimEndTime, mVideoView.getDuration());
// Update the database for adding a new video file.
- insertContent(mDstFile);
+ SaveVideoFileUtils.insertContent(mDstFileInfo,
+ getContentResolver(), mUri);
} catch (IOException e) {
e.printStackTrace();
}
@@ -305,7 +258,7 @@ public class TrimVideo extends Activity implements
@Override
public void run() {
Toast.makeText(getApplicationContext(),
- getString(R.string.save_into) + " " + saveFolderName,
+ getString(R.string.save_into, mDstFileInfo.mFolderName),
Toast.LENGTH_SHORT)
.show();
// TODO: change trimming into a service to avoid
@@ -315,7 +268,7 @@ public class TrimVideo extends Activity implements
mProgress = null;
// Show the result only when the activity not stopped.
Intent intent = new Intent(android.content.Intent.ACTION_VIEW);
- intent.setDataAndTypeAndNormalize(Uri.fromFile(mDstFile), "video/*");
+ intent.setDataAndType(Uri.fromFile(mDstFileInfo.mFile), "video/*");
intent.putExtra(MediaStore.EXTRA_FINISH_ON_COMPLETION, false);
startActivity(intent);
finish();
@@ -338,53 +291,6 @@ public class TrimVideo extends Activity implements
mProgress.show();
}
- /**
- * Insert the content (saved file) with proper video properties.
- */
- private Uri insertContent(File file) {
- long nowInMs = System.currentTimeMillis();
- long nowInSec = nowInMs / 1000;
- final ContentValues values = new ContentValues(12);
- values.put(Video.Media.TITLE, mSaveFileName);
- values.put(Video.Media.DISPLAY_NAME, file.getName());
- values.put(Video.Media.MIME_TYPE, "video/mp4");
- values.put(Video.Media.DATE_TAKEN, nowInMs);
- values.put(Video.Media.DATE_MODIFIED, nowInSec);
- values.put(Video.Media.DATE_ADDED, nowInSec);
- values.put(Video.Media.DATA, file.getAbsolutePath());
- values.put(Video.Media.SIZE, file.length());
- // Copy the data taken and location info from src.
- String[] projection = new String[] {
- VideoColumns.DATE_TAKEN,
- VideoColumns.LATITUDE,
- VideoColumns.LONGITUDE,
- VideoColumns.RESOLUTION,
- };
-
- // Copy some info from the source file.
- querySource(projection, new ContentResolverQueryCallback() {
- @Override
- public void onCursorResult(Cursor cursor) {
- long timeTaken = cursor.getLong(0);
- if (timeTaken > 0) {
- values.put(Video.Media.DATE_TAKEN, timeTaken);
- }
- 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(Video.Media.LATITUDE, latitude);
- values.put(Video.Media.LONGITUDE, longitude);
- }
- values.put(Video.Media.RESOLUTION, cursor.getString(3));
-
- }
- });
-
- return getContentResolver().insert(Video.Media.EXTERNAL_CONTENT_URI, values);
- }
-
@Override
public void onPlayPause() {
if (mVideoView.isPlaying()) {
@@ -410,6 +316,8 @@ public class TrimVideo extends Activity implements
mTrimStartTime = start;
mTrimEndTime = end;
setProgress();
+ // Enable save if there's modifications
+ mSaveVideoTextView.setEnabled(isModified());
}
@Override
diff --git a/src/com/android/gallery3d/app/TrimVideoUtils.java b/src/com/android/gallery3d/app/VideoUtils.java
index ae9b1e9ce..8ffc3d5eb 100644
--- a/src/com/android/gallery3d/app/TrimVideoUtils.java
+++ b/src/com/android/gallery3d/app/VideoUtils.java
@@ -19,6 +19,7 @@
package com.android.gallery3d.app;
+import com.android.gallery3d.util.SaveVideoFileInfo;
import com.coremedia.iso.IsoFile;
import com.coremedia.iso.boxes.TimeToSampleBox;
import com.googlecode.mp4parser.authoring.Movie;
@@ -36,12 +37,46 @@ import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
-/**
- * Shortens/Crops a track
- */
-public class TrimVideoUtils {
+public class VideoUtils {
- public static void startTrim(File src, File dst, int startMs, int endMs) throws IOException {
+ public static void startMute(String filePath, SaveVideoFileInfo dstFileInfo) throws IOException {
+ File dst = dstFileInfo.mFile;
+ File src = new File(filePath);
+ RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
+ Movie movie = MovieCreator.build(randomAccessFile.getChannel());
+
+ // remove all tracks we will create new tracks from the old
+ List<Track> tracks = movie.getTracks();
+ movie.setTracks(new LinkedList<Track>());
+
+ for (Track track : tracks) {
+ if (track.getHandler().equals("vide")) {
+ movie.addTrack(track);
+ }
+ }
+ writeMovieIntoFile(dst, movie);
+ randomAccessFile.close();
+ }
+
+ private static void writeMovieIntoFile(File dst, Movie movie)
+ throws IOException {
+ if (!dst.exists()) {
+ dst.createNewFile();
+ }
+
+ IsoFile out = new DefaultMp4Builder().build(movie);
+ FileOutputStream fos = new FileOutputStream(dst);
+ FileChannel fc = fos.getChannel();
+ out.getBox(fc); // This one build up the memory.
+
+ fc.close();
+ fos.close();
+ }
+
+ /**
+ * Shortens/Crops a track
+ */
+ public static void startTrim(File src, File dst, int startMs, int endMs, int totalMs) throws IOException {
RandomAccessFile randomAccessFile = new RandomAccessFile(src, "r");
Movie movie = MovieCreator.build(randomAccessFile.getChannel());
@@ -100,18 +135,7 @@ public class TrimVideoUtils {
}
movie.addTrack(new CroppedTrack(track, startSample, endSample));
}
- IsoFile out = new DefaultMp4Builder().build(movie);
-
- if (!dst.exists()) {
- dst.createNewFile();
- }
-
- FileOutputStream fos = new FileOutputStream(dst);
- FileChannel fc = fos.getChannel();
- out.getBox(fc); // This one build up the memory.
-
- fc.close();
- fos.close();
+ writeMovieIntoFile(dst, movie);
randomAccessFile.close();
}
diff --git a/src/com/android/gallery3d/app/Wallpaper.java b/src/com/android/gallery3d/app/Wallpaper.java
index 996d3f080..1bbe8d2c6 100644
--- a/src/com/android/gallery3d/app/Wallpaper.java
+++ b/src/com/android/gallery3d/app/Wallpaper.java
@@ -26,6 +26,8 @@ import android.os.Bundle;
import android.view.Display;
import com.android.gallery3d.common.ApiHelper;
+import com.android.gallery3d.filtershow.FilterShowActivity;
+import com.android.gallery3d.filtershow.CropExtras;
/**
* Wallpaper picker for the gallery application. This just redirects to the
@@ -98,19 +100,18 @@ public class Wallpaper extends Activity {
Point size = getDefaultDisplaySize(new Point());
float spotlightX = (float) size.x / width;
float spotlightY = (float) size.y / height;
- Intent request = new Intent(CropImage.ACTION_CROP)
+ Intent request = new Intent(FilterShowActivity.CROP_ACTION)
.setDataAndType(mPickedItem, IMAGE_TYPE)
.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT)
- .putExtra(CropImage.KEY_OUTPUT_X, width)
- .putExtra(CropImage.KEY_OUTPUT_Y, height)
- .putExtra(CropImage.KEY_ASPECT_X, width)
- .putExtra(CropImage.KEY_ASPECT_Y, height)
- .putExtra(CropImage.KEY_SPOTLIGHT_X, spotlightX)
- .putExtra(CropImage.KEY_SPOTLIGHT_Y, spotlightY)
- .putExtra(CropImage.KEY_SCALE, true)
- .putExtra(CropImage.KEY_SCALE_UP_IF_NEEDED, true)
- .putExtra(CropImage.KEY_NO_FACE_DETECTION, true)
- .putExtra(CropImage.KEY_SET_AS_WALLPAPER, true);
+ .putExtra(CropExtras.KEY_OUTPUT_X, width)
+ .putExtra(CropExtras.KEY_OUTPUT_Y, height)
+ .putExtra(CropExtras.KEY_ASPECT_X, width)
+ .putExtra(CropExtras.KEY_ASPECT_Y, height)
+ .putExtra(CropExtras.KEY_SPOTLIGHT_X, spotlightX)
+ .putExtra(CropExtras.KEY_SPOTLIGHT_Y, spotlightY)
+ .putExtra(CropExtras.KEY_SCALE, true)
+ .putExtra(CropExtras.KEY_SCALE_UP_IF_NEEDED, true)
+ .putExtra(CropExtras.KEY_SET_AS_WALLPAPER, true);
startActivity(request);
finish();
}