diff options
Diffstat (limited to 'src')
36 files changed, 702 insertions, 425 deletions
diff --git a/src/com/android/gallery3d/app/AbstractGalleryActivity.java b/src/com/android/gallery3d/app/AbstractGalleryActivity.java index 88ac028e1..8f824cfd2 100644 --- a/src/com/android/gallery3d/app/AbstractGalleryActivity.java +++ b/src/com/android/gallery3d/app/AbstractGalleryActivity.java @@ -17,7 +17,6 @@ package com.android.gallery3d.app; import android.annotation.TargetApi; -import android.app.Activity; import android.app.AlertDialog; import android.content.BroadcastReceiver; import android.content.Context; @@ -28,11 +27,12 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; 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; @@ -43,7 +43,7 @@ import com.android.gallery3d.ui.GLRootView; import com.android.gallery3d.util.ThreadPool; import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper; -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; 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/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java index ee7a107fd..e1c061318 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; @@ -89,7 +89,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 +303,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); } } } @@ -371,7 +370,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 +377,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()) { @@ -662,7 +660,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..ed06f46ae 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); @@ -655,7 +653,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/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 index 89ca63d44..e7e95fdaa 100644 --- a/src/com/android/gallery3d/app/CropImage.java +++ b/src/com/android/gallery3d/app/CropImage.java @@ -17,7 +17,6 @@ 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; @@ -40,12 +39,13 @@ 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.actionbarsherlock.app.ActionBar; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.view.Window; import com.android.camera.Util; import com.android.gallery3d.R; import com.android.gallery3d.common.ApiHelper; @@ -172,7 +172,7 @@ public class CropImage extends AbstractGalleryActivity { mCropView = new CropView(this); getGLRoot().setContentPane(mCropView); - ActionBar actionBar = getActionBar(); + ActionBar actionBar = getSupportActionBar(); int displayOptions = ActionBar.DISPLAY_HOME_AS_UP | ActionBar.DISPLAY_SHOW_TITLE; actionBar.setDisplayOptions(displayOptions, displayOptions); @@ -235,7 +235,7 @@ public class CropImage extends AbstractGalleryActivity { @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); - getMenuInflater().inflate(R.menu.crop, menu); + getSupportMenuInflater().inflate(R.menu.crop, menu); return true; } 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 b43cf2a70..76e050a75 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"; @@ -1017,11 +1018,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(); @@ -1062,6 +1068,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; @@ -1154,9 +1166,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 { @@ -1240,7 +1250,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/data/Exif.java b/src/com/android/gallery3d/data/Exif.java index ba5862a86..30aba7e97 100644 --- a/src/com/android/gallery3d/data/Exif.java +++ b/src/com/android/gallery3d/data/Exif.java @@ -18,144 +18,55 @@ package com.android.gallery3d.data; import android.util.Log; +import com.android.gallery3d.exif.ExifInvalidFormatException; +import com.android.gallery3d.exif.ExifParser; +import com.android.gallery3d.exif.ExifTag; + import java.io.IOException; import java.io.InputStream; public class Exif { - private static final String TAG = "CameraExif"; + private static final String TAG = "GalleryExif"; public static int getOrientation(InputStream is) { if (is == null) { return 0; } - byte[] buf = new byte[8]; - int length = 0; - - // ISO/IEC 10918-1:1993(E) - while (read(is, buf, 2) && (buf[0] & 0xFF) == 0xFF) { - int marker = buf[1] & 0xFF; - - // Check if the marker is a padding. - if (marker == 0xFF) { - continue; - } - - // Check if the marker is SOI or TEM. - if (marker == 0xD8 || marker == 0x01) { - continue; - } - // Check if the marker is EOI or SOS. - if (marker == 0xD9 || marker == 0xDA) { - return 0; - } - - // Get the length and check if it is reasonable. - if (!read(is, buf, 2)) { - return 0; - } - length = pack(buf, 0, 2, false); - if (length < 2) { - Log.e(TAG, "Invalid length"); - return 0; - } - length -= 2; - - // Break if the marker is EXIF in APP1. - if (marker == 0xE1 && length >= 6) { - if (!read(is, buf, 6)) return 0; - length -= 6; - if (pack(buf, 0, 4, false) == 0x45786966 && - pack(buf, 4, 2, false) == 0) { - break; - } - } - - // Skip other markers. - try { - is.skip(length); - } catch (IOException ex) { - return 0; - } - length = 0; - } - - // JEITA CP-3451 Exif Version 2.2 - if (length > 8) { - int offset = 0; - byte[] jpeg = new byte[length]; - if (!read(is, jpeg, length)) { - return 0; - } - - // Identify the byte order. - int tag = pack(jpeg, offset, 4, false); - if (tag != 0x49492A00 && tag != 0x4D4D002A) { - Log.e(TAG, "Invalid byte order"); - return 0; - } - boolean littleEndian = (tag == 0x49492A00); - - // Get the offset and check if it is reasonable. - int count = pack(jpeg, offset + 4, 4, littleEndian) + 2; - if (count < 10 || count > length) { - Log.e(TAG, "Invalid offset"); - return 0; - } - offset += count; - length -= count; - - // Get the count and go through all the elements. - count = pack(jpeg, offset - 2, 2, littleEndian); - while (count-- > 0 && length >= 12) { - // Get the tag and check if it is orientation. - tag = pack(jpeg, offset, 2, littleEndian); - if (tag == 0x0112) { - // We do not really care about type and count, do we? - int orientation = pack(jpeg, offset + 8, 2, littleEndian); - switch (orientation) { - case 1: - return 0; - case 3: - return 180; - case 6: - return 90; - case 8: - return 270; + try { + ExifParser parser = ExifParser.parse(is, ExifParser.OPTION_IFD_0); + int event = parser.next(); + while (event != ExifParser.EVENT_END) { + if (event == ExifParser.EVENT_NEW_TAG) { + ExifTag tag = parser.getTag(); + if (tag.getTagId() == ExifTag.TAG_ORIENTATION && + tag.hasValue()) { + int orient = (int) tag.getValueAt(0); + switch (orient) { + case ExifTag.Orientation.TOP_LEFT: + return 0; + case ExifTag.Orientation.BOTTOM_LEFT: + return 180; + case ExifTag.Orientation.RIGHT_TOP: + return 90; + case ExifTag.Orientation.RIGHT_BOTTOM: + return 270; + default: + Log.i(TAG, "Unsupported orientation"); + return 0; + } } - Log.i(TAG, "Unsupported orientation"); - return 0; } - offset += 12; - length -= 12; + event = parser.next(); } - } - - Log.i(TAG, "Orientation not found"); - return 0; - } - - private static int pack(byte[] bytes, int offset, int length, - boolean littleEndian) { - int step = 1; - if (littleEndian) { - offset += length - 1; - step = -1; - } - - int value = 0; - while (length-- > 0) { - value = (value << 8) | (bytes[offset] & 0xFF); - offset += step; - } - return value; - } - - private static boolean read(InputStream is, byte[] buf, int length) { - try { - return is.read(buf, 0, length) == length; - } catch (IOException ex) { - return false; + Log.i(TAG, "Orientation not found"); + return 0; + } catch (IOException e) { + Log.w(TAG, "Failed to read EXIF orientation", e); + return 0; + } catch (ExifInvalidFormatException e) { + Log.w(TAG, "Failed to read EXIF orientation", e); + return 0; } } } diff --git a/src/com/android/gallery3d/data/LocalAlbum.java b/src/com/android/gallery3d/data/LocalAlbum.java index e05aac01b..6c5feb5c8 100644 --- a/src/com/android/gallery3d/data/LocalAlbum.java +++ b/src/com/android/gallery3d/data/LocalAlbum.java @@ -61,7 +61,7 @@ public class LocalAlbum extends MediaSet { mApplication = application; mResolver = application.getContentResolver(); mBucketId = bucketId; - mName = getLocalizedName(application.getResources(), bucketId, name); + mName = name; mIsImage = isImage; if (isImage) { @@ -245,7 +245,7 @@ public class LocalAlbum extends MediaSet { @Override public String getName() { - return mName; + return getLocalizedName(mApplication.getResources(), mBucketId, mName); } @Override diff --git a/src/com/android/gallery3d/data/LocalMergeAlbum.java b/src/com/android/gallery3d/data/LocalMergeAlbum.java index cbaf82fff..f0b5e5726 100644 --- a/src/com/android/gallery3d/data/LocalMergeAlbum.java +++ b/src/com/android/gallery3d/data/LocalMergeAlbum.java @@ -41,7 +41,6 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener { private final Comparator<MediaItem> mComparator; private final MediaSet[] mSources; - private String mName; private FetchCache[] mFetcher; private int mSupportedOperation; private int mBucketId; @@ -54,7 +53,6 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener { super(path, INVALID_DATA_VERSION); mComparator = comparator; mSources = sources; - mName = sources.length == 0 ? "" : sources[0].getName(); mBucketId = bucketId; for (MediaSet set : mSources) { set.addContentListener(this); @@ -82,7 +80,6 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener { mSupportedOperation = supported; mIndex.clear(); mIndex.put(0, new int[mSources.length]); - mName = mSources.length == 0 ? "" : mSources[0].getName(); } private void invalidateCache() { @@ -111,7 +108,7 @@ public class LocalMergeAlbum extends MediaSet implements ContentListener { @Override public String getName() { - return mName; + return mSources.length == 0 ? "" : mSources[0].getName(); } @Override diff --git a/src/com/android/gallery3d/data/LocalVideo.java b/src/com/android/gallery3d/data/LocalVideo.java index 44b853901..b1e1cb3aa 100644 --- a/src/com/android/gallery3d/data/LocalVideo.java +++ b/src/com/android/gallery3d/data/LocalVideo.java @@ -180,7 +180,7 @@ public class LocalVideo extends LocalMediaItem { @Override public int getSupportedOperations() { - return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM; + return SUPPORT_DELETE | SUPPORT_SHARE | SUPPORT_PLAY | SUPPORT_INFO | SUPPORT_TRIM | SUPPORT_MUTE; } @Override diff --git a/src/com/android/gallery3d/data/MediaDetails.java b/src/com/android/gallery3d/data/MediaDetails.java index 298224729..16716dae4 100644 --- a/src/com/android/gallery3d/data/MediaDetails.java +++ b/src/com/android/gallery3d/data/MediaDetails.java @@ -19,9 +19,15 @@ package com.android.gallery3d.data; import android.media.ExifInterface; import com.android.gallery3d.R; -import com.android.gallery3d.common.ExifTags; +import com.android.gallery3d.common.Utils; +import com.android.gallery3d.exif.ExifData; +import com.android.gallery3d.exif.ExifInvalidFormatException; +import com.android.gallery3d.exif.ExifReader; +import com.android.gallery3d.exif.ExifTag; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; @@ -105,10 +111,18 @@ public class MediaDetails implements Iterable<Entry<Integer, Object>> { return mUnits.get(index); } - private static void setExifData(MediaDetails details, ExifInterface exif, String tag, + private static void setExifData(MediaDetails details, ExifTag tag, int key) { - String value = exif.getAttribute(tag); - if (value != null) { + if (tag != null) { + String value = null; + int type = tag.getDataType(); + if (type == ExifTag.TYPE_UNSIGNED_RATIONAL || type == ExifTag.TYPE_RATIONAL) { + value = String.valueOf(tag.getRational(0).toDouble()); + } else if (type == ExifTag.TYPE_ASCII) { + value = tag.getString(); + } else { + value = String.valueOf(tag.getValueAt(0)); + } if (key == MediaDetails.INDEX_FLASH) { MediaDetails.FlashState state = new MediaDetails.FlashState( Integer.valueOf(value.toString())); @@ -120,29 +134,37 @@ public class MediaDetails implements Iterable<Entry<Integer, Object>> { } public static void extractExifInfo(MediaDetails details, String filePath) { + InputStream is = null; try { - ExifInterface exif = new ExifInterface(filePath); - setExifData(details, exif, ExifInterface.TAG_FLASH, MediaDetails.INDEX_FLASH); - setExifData(details, exif, ExifInterface.TAG_IMAGE_WIDTH, MediaDetails.INDEX_WIDTH); - setExifData(details, exif, ExifInterface.TAG_IMAGE_LENGTH, - MediaDetails.INDEX_HEIGHT); - setExifData(details, exif, ExifInterface.TAG_MAKE, MediaDetails.INDEX_MAKE); - setExifData(details, exif, ExifInterface.TAG_MODEL, MediaDetails.INDEX_MODEL); - setExifData(details, exif, ExifTags.TAG_APERTURE, MediaDetails.INDEX_APERTURE); - setExifData(details, exif, ExifTags.TAG_ISO, MediaDetails.INDEX_ISO); - setExifData(details, exif, ExifInterface.TAG_WHITE_BALANCE, + is = new FileInputStream(filePath); + ExifData data = new ExifReader().read(is); + setExifData(details, data.getTag(ExifTag.TAG_FLASH), MediaDetails.INDEX_FLASH); + setExifData(details, data.getTag(ExifTag.TAG_IMAGE_WIDTH), MediaDetails.INDEX_WIDTH); + setExifData(details, data.getTag(ExifTag.TAG_IMAGE_LENGTH), MediaDetails.INDEX_HEIGHT); + setExifData(details, data.getTag(ExifTag.TAG_MAKE), MediaDetails.INDEX_MAKE); + setExifData(details, data.getTag(ExifTag.TAG_MODEL),MediaDetails.INDEX_MODEL); + setExifData(details, data.getTag(ExifTag.TAG_APERTURE_VALUE), + MediaDetails.INDEX_APERTURE); + setExifData(details, data.getTag(ExifTag.TAG_ISO_SPEED_RATINGS), + MediaDetails.INDEX_ISO); + setExifData(details, data.getTag(ExifTag.TAG_WHITE_BALANCE), MediaDetails.INDEX_WHITE_BALANCE); - setExifData(details, exif, ExifTags.TAG_EXPOSURE_TIME, + setExifData(details, data.getTag(ExifTag.TAG_EXPOSURE_TIME), MediaDetails.INDEX_EXPOSURE_TIME); - - double data = exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, 0); - if (data != 0f) { - details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH, data); + ExifTag focalTag = data.getTag(ExifTag.TAG_FOCAL_LENGTH); + if (focalTag != null) { + details.addDetail(MediaDetails.INDEX_FOCAL_LENGTH, + focalTag.getRational(0).toDouble()); details.setUnit(MediaDetails.INDEX_FOCAL_LENGTH, R.string.unit_mm); } } catch (IOException ex) { // ignore it. Log.w(TAG, "", ex); + } catch (ExifInvalidFormatException ex) { + // ignore it. + Log.w(TAG, "", ex); + } finally { + Utils.closeSilently(is); } } } diff --git a/src/com/android/gallery3d/data/MediaObject.java b/src/com/android/gallery3d/data/MediaObject.java index a41b275fb..9c82661f6 100644 --- a/src/com/android/gallery3d/data/MediaObject.java +++ b/src/com/android/gallery3d/data/MediaObject.java @@ -41,6 +41,7 @@ public abstract class MediaObject { public static final int SUPPORT_BACK = 1 << 14; public static final int SUPPORT_ACTION = 1 << 15; public static final int SUPPORT_CAMERA_SHORTCUT = 1 << 16; + public static final int SUPPORT_MUTE = 1 << 17; public static final int SUPPORT_ALL = 0xffffffff; // These are the bits returned from getMediaType(): diff --git a/src/com/android/gallery3d/filtershow/FilterShowActivity.java b/src/com/android/gallery3d/filtershow/FilterShowActivity.java index ef0415f94..94d318558 100644 --- a/src/com/android/gallery3d/filtershow/FilterShowActivity.java +++ b/src/com/android/gallery3d/filtershow/FilterShowActivity.java @@ -117,6 +117,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, private View mListGeometry = null; private View mListColors = null; private View mListFilterButtons = null; + private View mSaveButton = null; private ImageButton mFxButton = null; private ImageButton mBorderButton = null; @@ -178,7 +179,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM); actionBar.setCustomView(R.layout.filtershow_actionbar); - actionBar.getCustomView().setOnClickListener(new OnClickListener() { + mSaveButton = actionBar.getCustomView(); + mSaveButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View view) { saveImage(); @@ -633,6 +635,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener, return false; } + public void enableSave(boolean enable) { + if (mSaveButton != null) + mSaveButton.setEnabled(enable); + } + private void fillListImages(LinearLayout listFilters) { // TODO: use listview // TODO: load the filters straight from the filesystem diff --git a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java index a89199602..7874881fa 100644 --- a/src/com/android/gallery3d/filtershow/cache/ImageLoader.java +++ b/src/com/android/gallery3d/filtershow/cache/ImageLoader.java @@ -36,6 +36,9 @@ import com.adobe.xmp.XMPMeta; import com.android.gallery3d.R; import com.android.gallery3d.common.Utils; +import com.android.gallery3d.exif.ExifInvalidFormatException; +import com.android.gallery3d.exif.ExifParser; +import com.android.gallery3d.exif.ExifTag; import com.android.gallery3d.filtershow.FilterShowActivity; import com.android.gallery3d.filtershow.HistoryAdapter; import com.android.gallery3d.filtershow.imageshow.ImageCrop; @@ -46,6 +49,7 @@ import com.android.gallery3d.util.XmpUtilHelper; import java.io.Closeable; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -153,12 +157,27 @@ public class ImageLoader { static int getOrientationFromPath(String path) { int orientation = -1; + InputStream is = null; try { - ExifInterface EXIF = new ExifInterface(path); - orientation = EXIF.getAttributeInt(ExifInterface.TAG_ORIENTATION, - 1); + is = new FileInputStream(path); + ExifParser parser = ExifParser.parse(is, ExifParser.OPTION_IFD_0); + int event = parser.next(); + while (event != ExifParser.EVENT_END) { + if (event == ExifParser.EVENT_NEW_TAG) { + ExifTag tag = parser.getTag(); + if (tag.getTagId() == ExifTag.TAG_ORIENTATION) { + orientation = (int) tag.getValueAt(0); + break; + } + } + event = parser.next(); + } } catch (IOException e) { e.printStackTrace(); + } catch (ExifInvalidFormatException e) { + e.printStackTrace(); + } finally { + Utils.closeSilently(is); } return orientation; } diff --git a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java index 0145c24dc..358d5b795 100644 --- a/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java +++ b/src/com/android/gallery3d/filtershow/imageshow/ImageShow.java @@ -230,6 +230,7 @@ public class ImageShow extends View implements OnGestureListener, } updateSeekBar(parameter, minp, maxp); invalidate(); + mActivity.enableSave(hasModifications()); } @Override @@ -396,6 +397,7 @@ public class ImageShow extends View implements OnGestureListener, public void updateImagePresets(boolean force) { ImagePreset preset = getImagePreset(); if (preset == null) { + mActivity.enableSave(false); return; } if (force) { @@ -419,6 +421,7 @@ public class ImageShow extends View implements OnGestureListener, mFiltersOnlyImage = null; } } + mActivity.enableSave(hasModifications()); } public void requestFilteredImages() { diff --git a/src/com/android/gallery3d/ui/ActionModeHandler.java b/src/com/android/gallery3d/ui/ActionModeHandler.java index 7191599ad..0a3318d8d 100644 --- a/src/com/android/gallery3d/ui/ActionModeHandler.java +++ b/src/com/android/gallery3d/ui/ActionModeHandler.java @@ -17,21 +17,21 @@ package com.android.gallery3d.ui; import android.annotation.TargetApi; -import android.app.Activity; import android.content.Intent; import android.net.Uri; import android.nfc.NfcAdapter; import android.os.Handler; -import android.view.ActionMode; -import android.view.ActionMode.Callback; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.widget.Button; -import android.widget.ShareActionProvider; -import android.widget.ShareActionProvider.OnShareTargetSelectedListener; +import com.actionbarsherlock.app.SherlockActivity; +import com.actionbarsherlock.view.ActionMode; +import com.actionbarsherlock.view.ActionMode.Callback; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; +import com.actionbarsherlock.widget.ShareActionProvider; +import com.actionbarsherlock.widget.ShareActionProvider.OnShareTargetSelectedListener; import com.android.gallery3d.R; import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.common.ApiHelper; @@ -129,7 +129,7 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi } public void startActionMode() { - Activity a = mActivity; + SherlockActivity a = mActivity; mActionMode = a.startActionMode(this); View customView = LayoutInflater.from(a).inflate( R.layout.action_mode, null); @@ -408,6 +408,15 @@ public class ActionModeHandler implements Callback, PopupList.OnPopupItemClickLi // Pass1: Deal with unexpanded media object list for menu operation. ArrayList<MediaObject> selected = getSelectedMediaObjects(jc); if (selected == null) { + mMainHandler.post(new Runnable() { + @Override + public void run() { + mMenuTask = null; + if (jc.isCancelled()) return; + // Disable all the operations when no item is selected + MenuExecutor.updateMenuOperation(mMenu, 0); + } + }); return null; } final int operation = computeMenuOptions(selected); diff --git a/src/com/android/gallery3d/ui/MenuExecutor.java b/src/com/android/gallery3d/ui/MenuExecutor.java index f432333ce..3d088d18a 100644 --- a/src/com/android/gallery3d/ui/MenuExecutor.java +++ b/src/com/android/gallery3d/ui/MenuExecutor.java @@ -26,12 +26,13 @@ import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.os.Handler; import android.os.Message; -import android.view.Menu; -import android.view.MenuItem; +import com.actionbarsherlock.view.Menu; +import com.actionbarsherlock.view.MenuItem; import com.android.gallery3d.R; import com.android.gallery3d.app.AbstractGalleryActivity; import com.android.gallery3d.app.CropImage; +import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.common.Utils; import com.android.gallery3d.data.DataManager; import com.android.gallery3d.data.MediaItem; @@ -161,6 +162,7 @@ public class MenuExecutor { boolean supportRotate = (supported & MediaObject.SUPPORT_ROTATE) != 0; boolean supportCrop = (supported & MediaObject.SUPPORT_CROP) != 0; boolean supportTrim = (supported & MediaObject.SUPPORT_TRIM) != 0; + boolean supportMute = (supported & MediaObject.SUPPORT_MUTE) != 0; boolean supportShare = (supported & MediaObject.SUPPORT_SHARE) != 0; boolean supportSetAs = (supported & MediaObject.SUPPORT_SETAS) != 0; boolean supportShowOnMap = (supported & MediaObject.SUPPORT_SHOW_ON_MAP) != 0; @@ -174,6 +176,7 @@ public class MenuExecutor { setMenuItemVisible(menu, R.id.action_rotate_cw, supportRotate); setMenuItemVisible(menu, R.id.action_crop, supportCrop); setMenuItemVisible(menu, R.id.action_trim, supportTrim); + setMenuItemVisible(menu, R.id.action_mute, supportMute); // Hide panorama until call to updateMenuForPanorama corrects it setMenuItemVisible(menu, R.id.action_share_panorama, false); setMenuItemVisible(menu, R.id.action_share, supportShare); diff --git a/src/com/android/gallery3d/ui/PopupList.java b/src/com/android/gallery3d/ui/PopupList.java index 248f50b25..dd6269380 100644 --- a/src/com/android/gallery3d/ui/PopupList.java +++ b/src/com/android/gallery3d/ui/PopupList.java @@ -159,7 +159,7 @@ public class PopupList { R.drawable.menu_dropdown_panel_holo_dark)); mContentList = new ListView(mContext, null, - android.R.attr.dropDownListViewStyle); + com.actionbarsherlock.R.attr.dropDownListViewStyle); mContentList.setAdapter(new ItemDataAdapter()); mContentList.setOnItemClickListener(mOnItemClickListener); popup.setContentView(mContentList); diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java index 6a4bcea87..9069d5da2 100644 --- a/src/com/android/gallery3d/ui/PositionController.java +++ b/src/com/android/gallery3d/ui/PositionController.java @@ -18,10 +18,10 @@ package com.android.gallery3d.ui; import android.content.Context; import android.graphics.Rect; +import android.os.Build; import android.util.Log; import android.widget.Scroller; -import com.android.gallery3d.app.PhotoPage; import com.android.gallery3d.common.Utils; import com.android.gallery3d.ui.PhotoView.Size; import com.android.gallery3d.util.GalleryUtils; @@ -211,7 +211,11 @@ class PositionController { public PositionController(Context context, Listener listener) { mListener = listener; mPageScroller = new FlingScroller(); - mFilmScroller = new Scroller(context, null, false); + if (Build.VERSION.SDK_INT >= 11) { + mFilmScroller = new Scroller(context, null, false); + } else { + mFilmScroller = new Scroller(context, null); + } // Initialize the areas. initPlatform(); diff --git a/src/com/android/gallery3d/util/AccessibilityUtils.java b/src/com/android/gallery3d/util/AccessibilityUtils.java new file mode 100644 index 000000000..9df8e4ece --- /dev/null +++ b/src/com/android/gallery3d/util/AccessibilityUtils.java @@ -0,0 +1,54 @@ +/* + * 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.util; + +import android.content.Context; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityManager; + +import com.android.gallery3d.common.ApiHelper; + +/** + * AccessibilityUtils provides functions needed in accessibility mode. All the functions + * in this class are made compatible with gingerbread and later API's +*/ +public class AccessibilityUtils { + public static void makeAnnouncement(View view, CharSequence announcement) { + if (view == null) + return; + if (ApiHelper.HAS_ANNOUNCE_FOR_ACCESSIBILITY) { + view.announceForAccessibility(announcement); + } else { + // For API 15 and earlier, we need to construct an accessibility event + Context ctx = view.getContext(); + AccessibilityManager am = (AccessibilityManager) ctx.getSystemService( + Context.ACCESSIBILITY_SERVICE); + if (!am.isEnabled()) return; + AccessibilityEvent event = AccessibilityEvent.obtain( + AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED); + AccessibilityRecordCompat arc = new AccessibilityRecordCompat(event); + arc.setSource(view); + event.setClassName(view.getClass().getName()); + event.setPackageName(view.getContext().getPackageName()); + event.setEnabled(view.isEnabled()); + event.getText().add(announcement); + am.sendAccessibilityEvent(event); + } + } +}
\ No newline at end of file diff --git a/src/com/android/gallery3d/util/SaveVideoFileInfo.java b/src/com/android/gallery3d/util/SaveVideoFileInfo.java new file mode 100644 index 000000000..c7e5e8568 --- /dev/null +++ b/src/com/android/gallery3d/util/SaveVideoFileInfo.java @@ -0,0 +1,29 @@ +/* + * 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.util; + +import java.io.File; + +public class SaveVideoFileInfo { + public File mFile = null; + public String mFileName = null; + // This the full directory path. + public File mDirectory = null; + // This is just the folder's name. + public String mFolderName = null; + +} diff --git a/src/com/android/gallery3d/util/SaveVideoFileUtils.java b/src/com/android/gallery3d/util/SaveVideoFileUtils.java new file mode 100644 index 000000000..c281dd3e7 --- /dev/null +++ b/src/com/android/gallery3d/util/SaveVideoFileUtils.java @@ -0,0 +1,141 @@ +/* + * 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.util; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore.Video; +import android.provider.MediaStore.Video.VideoColumns; + +import java.io.File; +import java.sql.Date; +import java.text.SimpleDateFormat; + +public class SaveVideoFileUtils { + // Copy from SaveCopyTask.java in terms of how to handle the destination + // path and filename : querySource() and getSaveDirectory(). + public interface ContentResolverQueryCallback { + void onCursorResult(Cursor cursor); + } + + // This function can decide which folder to save the video file, and generate + // the needed information for the video file including filename. + public static SaveVideoFileInfo getDstMp4FileInfo(String fileNameFormat, + ContentResolver contentResolver, Uri uri, String defaultFolderName) { + SaveVideoFileInfo dstFileInfo = new SaveVideoFileInfo(); + // Use the default save directory if the source directory cannot be + // saved. + dstFileInfo.mDirectory = getSaveDirectory(contentResolver, uri); + if ((dstFileInfo.mDirectory == null) || !dstFileInfo.mDirectory.canWrite()) { + dstFileInfo.mDirectory = new File(Environment.getExternalStorageDirectory(), + BucketNames.DOWNLOAD); + dstFileInfo.mFolderName = defaultFolderName; + } else { + dstFileInfo.mFolderName = dstFileInfo.mDirectory.getName(); + } + dstFileInfo.mFileName = new SimpleDateFormat(fileNameFormat).format( + new Date(System.currentTimeMillis())); + + dstFileInfo.mFile = new File(dstFileInfo.mDirectory, dstFileInfo.mFileName + ".mp4"); + return dstFileInfo; + } + + private static void querySource(ContentResolver contentResolver, Uri uri, + String[] projection, ContentResolverQueryCallback callback) { + Cursor cursor = null; + try { + cursor = contentResolver.query(uri, 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(ContentResolver contentResolver, Uri uri) { + final File[] dir = new File[1]; + querySource(contentResolver, uri, + new String[] { VideoColumns.DATA }, + new ContentResolverQueryCallback() { + @Override + public void onCursorResult(Cursor cursor) { + dir[0] = new File(cursor.getString(0)).getParentFile(); + } + }); + return dir[0]; + } + + + /** + * Insert the content (saved file) with proper video properties. + */ + public static Uri insertContent(SaveVideoFileInfo mDstFileInfo, + ContentResolver contentResolver, Uri uri ) { + long nowInMs = System.currentTimeMillis(); + long nowInSec = nowInMs / 1000; + final ContentValues values = new ContentValues(12); + values.put(Video.Media.TITLE, mDstFileInfo.mFileName); + values.put(Video.Media.DISPLAY_NAME, mDstFileInfo.mFile.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, mDstFileInfo.mFile.getAbsolutePath()); + values.put(Video.Media.SIZE, mDstFileInfo.mFile.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(contentResolver, uri, 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 contentResolver.insert(Video.Media.EXTERNAL_CONTENT_URI, values); + } + +} |