blob: b9233957e2d8e350882f2f56f1c2bbf59afbea66 [file] [log] [blame]
Michael Kolb8872c232013-01-29 10:33:22 -08001/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.camera;
18
Doris Liu742cd5b2013-09-12 16:17:43 -070019import android.animation.Animator;
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -070020import android.annotation.TargetApi;
ztenghuifa9e2cc2013-08-09 17:37:15 -070021import android.app.ActionBar;
Doris Liu6432cd62013-06-13 17:20:31 -070022import android.app.Activity;
Mangesh Ghiware30968d02013-10-02 14:38:12 -070023import android.content.ActivityNotFoundException;
Doris Liub84b9732013-06-18 17:14:26 -070024import android.content.BroadcastReceiver;
Angus Kong86d36312013-01-31 18:22:44 -080025import android.content.ComponentName;
Doris Liu6432cd62013-06-13 17:20:31 -070026import android.content.ContentResolver;
Michael Kolb08650182013-02-25 19:43:56 -080027import android.content.Context;
Michael Kolb8872c232013-01-29 10:33:22 -080028import android.content.Intent;
Doris Liub84b9732013-06-18 17:14:26 -070029import android.content.IntentFilter;
Angus Kong86d36312013-01-31 18:22:44 -080030import android.content.ServiceConnection;
Sascha Haeberling2654dd92013-08-28 15:28:57 -070031import android.content.SharedPreferences;
Doris Liu3cf565c2013-02-15 10:55:37 -080032import android.content.pm.ActivityInfo;
Michael Kolb8872c232013-01-29 10:33:22 -080033import android.content.res.Configuration;
Doris Liu6432cd62013-06-13 17:20:31 -070034import android.graphics.drawable.ColorDrawable;
35import android.net.Uri;
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -070036import android.nfc.NfcAdapter;
37import android.nfc.NfcAdapter.CreateBeamUrisCallback;
38import android.nfc.NfcEvent;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070039import android.os.AsyncTask;
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -070040import android.os.Build;
Michael Kolb8872c232013-01-29 10:33:22 -080041import android.os.Bundle;
Doris Liu6432cd62013-06-13 17:20:31 -070042import android.os.Handler;
Angus Kong86d36312013-01-31 18:22:44 -080043import android.os.IBinder;
Doris Liuaa874422013-09-18 19:43:12 -070044import android.os.Looper;
45import android.os.Message;
Sascha Haeberling2654dd92013-08-28 15:28:57 -070046import android.preference.PreferenceManager;
Doris Liu2a7f44c2013-08-12 15:18:53 -070047import android.provider.MediaStore;
Doris Liu3cf565c2013-02-15 10:55:37 -080048import android.provider.Settings;
Sascha Haeberling37f36112013-08-06 14:31:52 -070049import android.util.Log;
Michael Kolb8872c232013-01-29 10:33:22 -080050import android.view.KeyEvent;
Doris Liu6432cd62013-06-13 17:20:31 -070051import android.view.LayoutInflater;
ztenghui0353ca22013-08-13 13:53:16 -070052import android.view.Menu;
53import android.view.MenuInflater;
54import android.view.MenuItem;
Doris Liu742cd5b2013-09-12 16:17:43 -070055import android.view.MotionEvent;
Michael Kolb8872c232013-01-29 10:33:22 -080056import android.view.OrientationEventListener;
57import android.view.View;
Doris Liu6432cd62013-06-13 17:20:31 -070058import android.view.ViewGroup;
Michael Kolb08650182013-02-25 19:43:56 -080059import android.view.Window;
60import android.view.WindowManager;
Angus Kong653c43b2013-08-21 18:28:43 -070061import android.widget.FrameLayout;
Doris Liu6432cd62013-06-13 17:20:31 -070062import android.widget.ImageView;
Sascha Haeberling37f36112013-08-06 14:31:52 -070063import android.widget.ProgressBar;
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -070064import android.widget.ShareActionProvider;
Michael Kolb8872c232013-01-29 10:33:22 -080065
Sascha Haeberling03e01452013-10-07 13:21:33 -070066import com.android.camera.CameraManager.CameraOpenErrorCallback;
Angus Konged15d1a2013-08-19 15:06:12 -070067import com.android.camera.app.AppManagerFactory;
Angus Kongfd4fc0e2013-11-07 15:38:09 -080068import com.android.camera.app.MediaSaver;
Ruben Brunk7cfcafd2013-10-17 15:41:44 -070069import com.android.camera.app.PlaceholderManager;
Angus Konged15d1a2013-08-19 15:06:12 -070070import com.android.camera.app.PanoramaStitchingManager;
nicolasroard19ab7252013-09-18 16:54:05 -070071import com.android.camera.crop.CropActivity;
Doris Liu6432cd62013-06-13 17:20:31 -070072import com.android.camera.data.CameraDataAdapter;
Angus Kong8e5e4ee2013-07-30 11:36:00 -070073import com.android.camera.data.CameraPreviewData;
74import com.android.camera.data.FixedFirstDataAdapter;
75import com.android.camera.data.FixedLastDataAdapter;
Angus Kong32509872013-10-02 16:53:40 -070076import com.android.camera.data.InProgressDataWrapper;
Doris Liu6432cd62013-06-13 17:20:31 -070077import com.android.camera.data.LocalData;
Angus Kong8e5e4ee2013-07-30 11:36:00 -070078import com.android.camera.data.LocalDataAdapter;
ztenghui064d6002013-09-05 15:47:58 -070079import com.android.camera.data.LocalMediaObserver;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070080import com.android.camera.data.MediaDetails;
Angus Kongbd260692013-08-07 14:52:56 -070081import com.android.camera.data.SimpleViewData;
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -070082import com.android.camera.tinyplanet.TinyPlanetFragment;
Sascha Haeberling6f64b502013-08-14 16:23:18 -070083import com.android.camera.ui.DetailsDialog;
Doris Liu6432cd62013-06-13 17:20:31 -070084import com.android.camera.ui.FilmStripView;
Sascha Haeberling03e01452013-10-07 13:21:33 -070085import com.android.camera.ui.ModuleSwitcher;
Sascha Haeberling88ef7662013-08-15 17:19:22 -070086import com.android.camera.util.ApiHelper;
Angus Kongb50b5cb2013-08-09 14:55:20 -070087import com.android.camera.util.CameraUtil;
Ruben Brunka9d66bd2013-09-06 11:56:32 -070088import com.android.camera.util.GcamHelper;
Alan Newberger761306f2013-10-30 12:51:21 -070089import com.android.camera.util.IntentHelper;
Sascha Haeberling37f36112013-08-06 14:31:52 -070090import com.android.camera.util.PhotoSphereHelper;
Sascha Haeberling88ef7662013-08-15 17:19:22 -070091import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper;
Sascha Haeberling03e01452013-10-07 13:21:33 -070092import com.android.camera.util.RefocusHelper;
Sascha Haeberling8e963a52013-08-06 11:43:02 -070093import com.android.camera2.R;
Michael Kolb8872c232013-01-29 10:33:22 -080094
Doris Liu6432cd62013-06-13 17:20:31 -070095public class CameraActivity extends Activity
Doris Liuaa874422013-09-18 19:43:12 -070096 implements ModuleSwitcher.ModuleSwitchListener,
97 ActionBar.OnMenuVisibilityListener {
Doris Liu6432cd62013-06-13 17:20:31 -070098
99 private static final String TAG = "CAM_Activity";
100
Doris Liu6432cd62013-06-13 17:20:31 -0700101 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE =
102 "android.media.action.STILL_IMAGE_CAMERA_SECURE";
103 public static final String ACTION_IMAGE_CAPTURE_SECURE =
104 "android.media.action.IMAGE_CAPTURE_SECURE";
ztenghui6b920322013-08-16 16:21:22 -0700105 public static final String ACTION_TRIM_VIDEO =
106 "com.android.camera.action.TRIM";
107 public static final String MEDIA_ITEM_PATH = "media-item-path";
Doris Liu6432cd62013-06-13 17:20:31 -0700108
109 // The intent extra for camera from secure lock screen. True if the gallery
110 // should only show newly captured pictures. sSecureAlbumId does not
111 // increment. This is used when switching between camera, camcorder, and
112 // panorama. If the extra is not set, it is in the normal camera mode.
113 public static final String SECURE_CAMERA_EXTRA = "secure_camera";
114
Sascha Haeberlingb7639c62013-09-09 14:42:43 -0700115 /**
116 * Request code from an activity we started that indicated that we do not
117 * want to reset the view to the preview in onResume.
118 */
119 public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142;
120
Ruben Brunkd217ed02013-10-08 23:31:13 -0700121 public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999;
122
Doris Liuaa874422013-09-18 19:43:12 -0700123 private static final int HIDE_ACTION_BAR = 1;
124 private static final long SHOW_ACTION_BAR_TIMEOUT_MS = 3000;
125
Sascha Haeberlingb7639c62013-09-09 14:42:43 -0700126 /** Whether onResume should reset the view to the preview. */
127 private boolean mResetToPreviewOnResume = true;
128
ztenghui0353ca22013-08-13 13:53:16 -0700129 // Supported operations at FilmStripView. Different data has different
130 // set of supported operations.
131 private static final int SUPPORT_DELETE = 1 << 0;
132 private static final int SUPPORT_ROTATE = 1 << 1;
133 private static final int SUPPORT_INFO = 1 << 2;
134 private static final int SUPPORT_CROP = 1 << 3;
135 private static final int SUPPORT_SETAS = 1 << 4;
136 private static final int SUPPORT_EDIT = 1 << 5;
137 private static final int SUPPORT_TRIM = 1 << 6;
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700138 private static final int SUPPORT_SHARE = 1 << 7;
139 private static final int SUPPORT_SHARE_PANORAMA360 = 1 << 8;
140 private static final int SUPPORT_SHOW_ON_MAP = 1 << 9;
ztenghui0353ca22013-08-13 13:53:16 -0700141 private static final int SUPPORT_ALL = 0xffffffff;
142
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700143 /** This data adapter is used by FilmStripView. */
Angus Kong8e5e4ee2013-07-30 11:36:00 -0700144 private LocalDataAdapter mDataAdapter;
145 /** This data adapter represents the real local camera data. */
146 private LocalDataAdapter mWrappedDataAdapter;
147
Angus Kong6798c342013-07-16 15:14:58 -0700148 private PanoramaStitchingManager mPanoramaManager;
Ruben Brunk7cfcafd2013-10-17 15:41:44 -0700149 private PlaceholderManager mPlaceholderManager;
Michael Kolb8872c232013-01-29 10:33:22 -0800150 private int mCurrentModuleIndex;
Doris Liu6432cd62013-06-13 17:20:31 -0700151 private CameraModule mCurrentModule;
Angus Kong653c43b2013-08-21 18:28:43 -0700152 private FrameLayout mAboveFilmstripControlLayout;
153 private View mCameraModuleRootView;
Doris Liu6432cd62013-06-13 17:20:31 -0700154 private FilmStripView mFilmStripView;
Sascha Haeberling37f36112013-08-06 14:31:52 -0700155 private ProgressBar mBottomProgress;
156 private View mPanoStitchingPanel;
Doris Liu6432cd62013-06-13 17:20:31 -0700157 private int mResultCodeForTesting;
158 private Intent mResultDataForTesting;
159 private OnScreenHint mStorageHint;
Angus Kong2dcc0a92013-09-25 13:00:08 -0700160 private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES;
Doris Liu3cf565c2013-02-15 10:55:37 -0800161 private boolean mAutoRotateScreen;
Doris Liu6432cd62013-06-13 17:20:31 -0700162 private boolean mSecureCamera;
Angus Kong6a8e8a12013-07-19 14:55:07 -0700163 // This is a hack to speed up the start of SecureCamera.
164 private static boolean sFirstStartAfterScreenOn = true;
Doris Liu6432cd62013-06-13 17:20:31 -0700165 private int mLastRawOrientation;
Michael Kolb8872c232013-01-29 10:33:22 -0800166 private MyOrientationEventListener mOrientationListener;
Doris Liu6432cd62013-06-13 17:20:31 -0700167 private Handler mMainHandler;
Sascha Haeberlingf1f51862013-07-31 11:28:21 -0700168 private PanoramaViewHelper mPanoramaViewHelper;
Angus Kong8e5e4ee2013-07-30 11:36:00 -0700169 private CameraPreviewData mCameraPreviewData;
ztenghuifa9e2cc2013-08-09 17:37:15 -0700170 private ActionBar mActionBar;
Doris Liuaa874422013-09-18 19:43:12 -0700171 private OnActionBarVisibilityListener mOnActionBarVisibilityListener = null;
ztenghui0353ca22013-08-13 13:53:16 -0700172 private Menu mActionBarMenu;
Angus Kong653c43b2013-08-21 18:28:43 -0700173 private ViewGroup mUndoDeletionBar;
Doris Liu742cd5b2013-09-12 16:17:43 -0700174 private boolean mIsUndoingDeletion = false;
Doris Liu3cf565c2013-02-15 10:55:37 -0800175
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -0700176 private Uri[] mNfcPushUris = new Uri[1];
177
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700178 private ShareActionProvider mStandardShareActionProvider;
179 private Intent mStandardShareIntent;
180 private ShareActionProvider mPanoramaShareActionProvider;
181 private Intent mPanoramaShareIntent;
ztenghui064d6002013-09-05 15:47:58 -0700182 private LocalMediaObserver mLocalImagesObserver;
183 private LocalMediaObserver mLocalVideosObserver;
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700184
Sascha Haeberling5199c202013-09-05 17:10:19 -0700185 private final int DEFAULT_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
186 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
Doris Liu742cd5b2013-09-12 16:17:43 -0700187 private boolean mPendingDeletion = false;
Sascha Haeberling5199c202013-09-05 17:10:19 -0700188
ztenghui05804752013-09-17 11:33:02 -0700189 private Intent mVideoShareIntent;
190 private Intent mImageShareIntent;
191
Michael Kolb8872c232013-01-29 10:33:22 -0800192 private class MyOrientationEventListener
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700193 extends OrientationEventListener {
Michael Kolb8872c232013-01-29 10:33:22 -0800194 public MyOrientationEventListener(Context context) {
195 super(context);
196 }
197
198 @Override
199 public void onOrientationChanged(int orientation) {
200 // We keep the last known orientation. So if the user first orient
201 // the camera then point the camera to floor or sky, we still have
202 // the correct orientation.
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700203 if (orientation == ORIENTATION_UNKNOWN) {
204 return;
205 }
Michael Kolb8872c232013-01-29 10:33:22 -0800206 mLastRawOrientation = orientation;
207 mCurrentModule.onOrientationChanged(orientation);
208 }
209 }
210
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800211 private MediaSaver mMediaSaver;
Doris Liu6432cd62013-06-13 17:20:31 -0700212 private ServiceConnection mConnection = new ServiceConnection() {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700213 @Override
214 public void onServiceConnected(ComponentName className, IBinder b) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800215 mMediaSaver = ((MediaSaveService.LocalBinder) b).getService();
216 mCurrentModule.onMediaSaverAvailable(mMediaSaver);
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700217 }
218
219 @Override
220 public void onServiceDisconnected(ComponentName className) {
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800221 if (mMediaSaver != null) {
222 mMediaSaver.setQueueListener(null);
223 mMediaSaver = null;
Michael Kolb8872c232013-01-29 10:33:22 -0800224 }
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700225 }
226 };
Doris Liu6432cd62013-06-13 17:20:31 -0700227
Angus Kong4f795b82013-09-16 14:25:35 -0700228 private CameraOpenErrorCallback mCameraOpenErrorCallback =
229 new CameraOpenErrorCallback() {
230 @Override
231 public void onCameraDisabled(int cameraId) {
232 CameraUtil.showErrorAndFinish(CameraActivity.this,
233 R.string.camera_disabled);
234 }
235
236 @Override
237 public void onDeviceOpenFailure(int cameraId) {
238 CameraUtil.showErrorAndFinish(CameraActivity.this,
239 R.string.cannot_connect_camera);
240 }
241
242 @Override
243 public void onReconnectionFailure(CameraManager mgr) {
244 CameraUtil.showErrorAndFinish(CameraActivity.this,
245 R.string.cannot_connect_camera);
246 }
247 };
248
Doris Liub84b9732013-06-18 17:14:26 -0700249 // close activity when screen turns off
250 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
251 @Override
252 public void onReceive(Context context, Intent intent) {
253 finish();
254 }
255 };
256
Angus Kong6a8e8a12013-07-19 14:55:07 -0700257 private static BroadcastReceiver sScreenOffReceiver;
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700258
Angus Kong6a8e8a12013-07-19 14:55:07 -0700259 private static class ScreenOffReceiver extends BroadcastReceiver {
260 @Override
261 public void onReceive(Context context, Intent intent) {
262 sFirstStartAfterScreenOn = true;
263 }
264 }
265
Doris Liuaa874422013-09-18 19:43:12 -0700266 private class MainHandler extends Handler {
267 public MainHandler(Looper looper) {
268 super(looper);
269 }
270
271 @Override
272 public void handleMessage(Message msg) {
273 if (msg.what == HIDE_ACTION_BAR) {
274 removeMessages(HIDE_ACTION_BAR);
275 CameraActivity.this.setSystemBarsVisibility(false);
276 }
277 }
278 }
279
280 public interface OnActionBarVisibilityListener {
281 public void onActionBarVisibilityChanged(boolean isVisible);
282 }
283
284 public void setOnActionBarVisibilityListener(OnActionBarVisibilityListener listener) {
285 mOnActionBarVisibilityListener = listener;
286 }
287
Angus Kong6a8e8a12013-07-19 14:55:07 -0700288 public static boolean isFirstStartAfterScreenOn() {
289 return sFirstStartAfterScreenOn;
290 }
291
292 public static void resetFirstStartAfterScreenOn() {
293 sFirstStartAfterScreenOn = false;
294 }
295
Sascha Haeberling37f36112013-08-06 14:31:52 -0700296 private FilmStripView.Listener mFilmStripListener =
297 new FilmStripView.Listener() {
298 @Override
299 public void onDataPromoted(int dataID) {
300 removeData(dataID);
301 }
Doris Liu6432cd62013-06-13 17:20:31 -0700302
Sascha Haeberling37f36112013-08-06 14:31:52 -0700303 @Override
304 public void onDataDemoted(int dataID) {
305 removeData(dataID);
306 }
Doris Liu6432cd62013-06-13 17:20:31 -0700307
Sascha Haeberling37f36112013-08-06 14:31:52 -0700308 @Override
309 public void onDataFullScreenChange(int dataID, boolean full) {
ztenghui8566dd72013-09-12 14:56:56 -0700310 boolean isCameraID = isCameraPreview(dataID);
ztenghui7b265a62013-09-09 14:58:44 -0700311 if (!isCameraID) {
Doris Liuaa874422013-09-18 19:43:12 -0700312 if (!full) {
313 // Always show action bar in filmstrip mode
314 CameraActivity.this.setSystemBarsVisibility(true, false);
315 } else if (mActionBar.isShowing()) {
316 // Hide action bar after time out in full screen mode
317 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR,
318 SHOW_ACTION_BAR_TIMEOUT_MS);
319 }
ztenghuifa9e2cc2013-08-09 17:37:15 -0700320 }
Sascha Haeberling37f36112013-08-06 14:31:52 -0700321 }
322
ztenghui8566dd72013-09-12 14:56:56 -0700323 /**
324 * Check if the local data corresponding to dataID is the camera
325 * preview.
326 *
327 * @param dataID the ID of the local data
328 * @return true if the local data is not null and it is the
329 * camera preview.
330 */
331 private boolean isCameraPreview(int dataID) {
332 LocalData localData = mDataAdapter.getLocalData(dataID);
333 if (localData == null) {
334 Log.w(TAG, "Current data ID not found.");
335 return false;
336 }
337 return localData.getLocalDataType() == LocalData.LOCAL_CAMERA_PREVIEW;
338 }
339
Sascha Haeberling37f36112013-08-06 14:31:52 -0700340 @Override
Erin Dahlgren3044d8c2013-10-10 18:23:45 -0700341 public void onReload() {
342 setPreviewControlsVisibility(true);
ztenghui17f1e652013-11-06 14:20:31 -0800343 CameraActivity.this.setSystemBarsVisibility(false);
Erin Dahlgren3044d8c2013-10-10 18:23:45 -0700344 }
345
346 @Override
347 public void onCurrentDataCentered(int dataID) {
348 if (dataID != 0 && !mFilmStripView.isCameraPreview()) {
349 // For now, We ignore all items that are not the camera preview.
350 return;
351 }
352
353 if(!arePreviewControlsVisible()) {
354 setPreviewControlsVisibility(true);
Erin Dahlgrenfd7f0a92013-10-21 10:08:54 -0700355 CameraActivity.this.setSystemBarsVisibility(false);
Erin Dahlgren3044d8c2013-10-10 18:23:45 -0700356 }
357 }
358
359 @Override
360 public void onCurrentDataOffCentered(int dataID) {
361 if (dataID != 0 && !mFilmStripView.isCameraPreview()) {
362 // For now, We ignore all items that are not the camera preview.
363 return;
364 }
365
366 if (arePreviewControlsVisible()) {
367 setPreviewControlsVisibility(false);
368 }
369 }
370
371 @Override
Angus Kong02cafdf2013-10-13 19:26:02 -0700372 public void onDataFocusChanged(final int dataID, final boolean focused) {
Doris Liuaa874422013-09-18 19:43:12 -0700373 // Delay hiding action bar if there is any user interaction
374 if (mMainHandler.hasMessages(HIDE_ACTION_BAR)) {
375 mMainHandler.removeMessages(HIDE_ACTION_BAR);
376 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR,
377 SHOW_ACTION_BAR_TIMEOUT_MS);
378 }
Angus Kong02cafdf2013-10-13 19:26:02 -0700379 // TODO: This callback is UI event callback, should always
380 // happen on UI thread. Find the reason for this
381 // runOnUiThread() and fix it.
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700382 runOnUiThread(new Runnable() {
383 @Override
384 public void run() {
ztenghui7b265a62013-09-09 14:58:44 -0700385 LocalData currentData = mDataAdapter.getLocalData(dataID);
386 if (currentData == null) {
387 Log.w(TAG, "Current data ID not found.");
388 hidePanoStitchingProgress();
389 return;
390 }
391 boolean isCameraID = currentData.getLocalDataType() ==
392 LocalData.LOCAL_CAMERA_PREVIEW;
Angus Kong02cafdf2013-10-13 19:26:02 -0700393 if (!focused) {
ztenghui7b265a62013-09-09 14:58:44 -0700394 if (isCameraID) {
395 mCurrentModule.onPreviewFocusChanged(false);
Doris Liuaa874422013-09-18 19:43:12 -0700396 CameraActivity.this.setSystemBarsVisibility(true);
ztenghui7b265a62013-09-09 14:58:44 -0700397 }
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700398 hidePanoStitchingProgress();
399 } else {
ztenghui7b265a62013-09-09 14:58:44 -0700400 if (isCameraID) {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700401 // Don't show the action bar in Camera
402 // preview.
Doris Liuaa874422013-09-18 19:43:12 -0700403 CameraActivity.this.setSystemBarsVisibility(false);
Erin Dahlgren3044d8c2013-10-10 18:23:45 -0700404
Doris Liu742cd5b2013-09-12 16:17:43 -0700405 if (mPendingDeletion) {
406 performDeletion();
407 }
Alan Newberger1ef76d42013-09-04 19:01:59 -0700408 } else {
409 updateActionBarMenu(dataID);
410 }
ztenghui2c3d9a52013-09-03 11:27:21 -0700411
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700412 Uri contentUri = currentData.getContentUri();
413 if (contentUri == null) {
414 hidePanoStitchingProgress();
415 return;
416 }
417 int panoStitchingProgress = mPanoramaManager.getTaskProgress(
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700418 contentUri);
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700419 if (panoStitchingProgress < 0) {
420 hidePanoStitchingProgress();
421 return;
422 }
423 showPanoStitchingProgress();
424 updateStitchingProgress(panoStitchingProgress);
425 }
ztenghui2c3d9a52013-09-03 11:27:21 -0700426 }
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700427 });
Sascha Haeberling37f36112013-08-06 14:31:52 -0700428 }
Sascha Haeberling394023f2013-08-15 15:57:29 -0700429
430 @Override
Doris Liuaa874422013-09-18 19:43:12 -0700431 public void onToggleSystemDecorsVisibility(int dataID) {
432 // If action bar is showing, hide it immediately, otherwise
433 // show action bar and hide it later
Sascha Haeberling394023f2013-08-15 15:57:29 -0700434 if (mActionBar.isShowing()) {
Doris Liuaa874422013-09-18 19:43:12 -0700435 CameraActivity.this.setSystemBarsVisibility(false);
Sascha Haeberling394023f2013-08-15 15:57:29 -0700436 } else {
ztenghui8566dd72013-09-12 14:56:56 -0700437 // Don't show the action bar if that is the camera preview.
438 boolean isCameraID = isCameraPreview(dataID);
439 if (!isCameraID) {
Doris Liuaa874422013-09-18 19:43:12 -0700440 CameraActivity.this.setSystemBarsVisibility(true, true);
ztenghuifd43e3b2013-09-03 11:30:11 -0700441 }
Sascha Haeberling394023f2013-08-15 15:57:29 -0700442 }
443 }
Doris Liuaa874422013-09-18 19:43:12 -0700444
445 @Override
446 public void setSystemDecorsVisibility(boolean visible) {
447 CameraActivity.this.setSystemBarsVisibility(visible);
448 }
Sascha Haeberling37f36112013-08-06 14:31:52 -0700449 };
450
Sascha Haeberling4ed20592013-09-13 11:58:33 -0700451 public void gotoGallery() {
452 mFilmStripView.getController().goToNextItem();
453 }
454
Sascha Haeberling5199c202013-09-05 17:10:19 -0700455 /**
Doris Liuaa874422013-09-18 19:43:12 -0700456 * If {@param visible} is false, this hides the action bar and switches the system UI
457 * to lights-out mode.
Sascha Haeberling5199c202013-09-05 17:10:19 -0700458 */
Doris Liu97cb1ea2013-10-11 16:54:51 -0700459 // TODO: This should not be called outside of the activity.
Sascha Haeberling4ec139d2013-10-09 23:46:20 -0700460 public void setSystemBarsVisibility(boolean visible) {
Doris Liuaa874422013-09-18 19:43:12 -0700461 setSystemBarsVisibility(visible, false);
462 }
463
464 /**
465 * If {@param visible} is false, this hides the action bar and switches the
466 * system UI to lights-out mode. If {@param hideLater} is true, a delayed message
467 * will be sent after a timeout to hide the action bar.
468 */
469 private void setSystemBarsVisibility(boolean visible, boolean hideLater) {
470 mMainHandler.removeMessages(HIDE_ACTION_BAR);
Doris Liuaa874422013-09-18 19:43:12 -0700471
ztenghui17f1e652013-11-06 14:20:31 -0800472 int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility();
473 int newSystemUIVisibility = DEFAULT_SYSTEM_UI_VISIBILITY |
474 (visible ? View.SYSTEM_UI_FLAG_VISIBLE :
475 View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN);
476 if (newSystemUIVisibility != currentSystemUIVisibility) {
477 mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility);
478 }
Doris Liu97cb1ea2013-10-11 16:54:51 -0700479
ztenghui17f1e652013-11-06 14:20:31 -0800480 boolean currentActionBarVisibility = mActionBar.isShowing();
481 if (visible != currentActionBarVisibility) {
Doris Liuaa874422013-09-18 19:43:12 -0700482 if (visible) {
483 mActionBar.show();
484 } else {
485 mActionBar.hide();
486 }
487 if (mOnActionBarVisibilityListener != null) {
488 mOnActionBarVisibilityListener.onActionBarVisibilityChanged(visible);
489 }
490 }
491
492 // Now delay hiding the bars
493 if (visible && hideLater) {
494 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS);
Sascha Haeberling5199c202013-09-05 17:10:19 -0700495 }
Sascha Haeberling5199c202013-09-05 17:10:19 -0700496 }
497
Sascha Haeberling37f36112013-08-06 14:31:52 -0700498 private void hidePanoStitchingProgress() {
499 mPanoStitchingPanel.setVisibility(View.GONE);
500 }
501
502 private void showPanoStitchingProgress() {
503 mPanoStitchingPanel.setVisibility(View.VISIBLE);
504 }
505
506 private void updateStitchingProgress(int progress) {
507 mBottomProgress.setProgress(progress);
508 }
Doris Liu6432cd62013-06-13 17:20:31 -0700509
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -0700510 @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
511 private void setupNfcBeamPush() {
512 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this);
513 if (adapter == null) {
514 return;
515 }
516
517 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) {
518 // Disable beaming
519 adapter.setNdefPushMessage(null, CameraActivity.this);
520 return;
521 }
522
523 adapter.setBeamPushUris(null, CameraActivity.this);
524 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() {
525 @Override
526 public Uri[] createBeamUris(NfcEvent event) {
527 return mNfcPushUris;
528 }
529 }, CameraActivity.this);
530 }
531
532 private void setNfcBeamPushUri(Uri uri) {
533 mNfcPushUris[0] = uri;
534 }
535
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700536 private void setStandardShareIntent(Uri contentUri, String mimeType) {
ztenghui05804752013-09-17 11:33:02 -0700537 mStandardShareIntent = getShareIntentFromType(mimeType);
538 if (mStandardShareIntent != null) {
539 mStandardShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
540 mStandardShareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
541 if (mStandardShareActionProvider != null) {
542 mStandardShareActionProvider.setShareIntent(mStandardShareIntent);
543 }
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700544 }
ztenghui05804752013-09-17 11:33:02 -0700545 }
546
547 /**
548 * Get the share intent according to the mimeType
549 *
550 * @param mimeType The mimeType of current data.
551 * @return the video/image's ShareIntent or null if mimeType is invalid.
552 */
553 private Intent getShareIntentFromType(String mimeType) {
554 // Lazily create the intent object.
555 if (mimeType.startsWith("video/")) {
556 if (mVideoShareIntent == null) {
557 mVideoShareIntent = new Intent(Intent.ACTION_SEND);
558 mVideoShareIntent.setType("video/*");
559 }
560 return mVideoShareIntent;
561 } else if (mimeType.startsWith("image/")) {
562 if (mImageShareIntent == null) {
563 mImageShareIntent = new Intent(Intent.ACTION_SEND);
564 mImageShareIntent.setType("image/*");
565 }
566 return mImageShareIntent;
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700567 }
ztenghui05804752013-09-17 11:33:02 -0700568 Log.w(TAG, "unsupported mimeType " + mimeType);
569 return null;
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700570 }
571
572 private void setPanoramaShareIntent(Uri contentUri) {
573 if (mPanoramaShareIntent == null) {
574 mPanoramaShareIntent = new Intent(Intent.ACTION_SEND);
575 }
576 mPanoramaShareIntent.setType("application/vnd.google.panorama360+jpg");
577 mPanoramaShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
578 if (mPanoramaShareActionProvider != null) {
579 mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent);
580 }
581 }
582
Doris Liuaa874422013-09-18 19:43:12 -0700583 @Override
584 public void onMenuVisibilityChanged(boolean isVisible) {
585 // If menu is showing, we need to make sure action bar does not go away.
586 mMainHandler.removeMessages(HIDE_ACTION_BAR);
587 if (!isVisible) {
588 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS);
589 }
590 }
591
ztenghui0353ca22013-08-13 13:53:16 -0700592 /**
593 * According to the data type, make the menu items for supported operations
594 * visible.
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700595 *
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700596 * @param dataID the data ID of the current item.
ztenghui0353ca22013-08-13 13:53:16 -0700597 */
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700598 private void updateActionBarMenu(int dataID) {
599 LocalData currentData = mDataAdapter.getLocalData(dataID);
Erin Dahlgrend4f69ed2013-10-01 13:42:51 -0700600 if (currentData == null) {
601 return;
602 }
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700603 int type = currentData.getLocalDataType();
604
ztenghui0353ca22013-08-13 13:53:16 -0700605 if (mActionBarMenu == null) {
606 return;
607 }
608
609 int supported = 0;
Angus Kong32509872013-10-02 16:53:40 -0700610
ztenghui0353ca22013-08-13 13:53:16 -0700611 switch (type) {
612 case LocalData.LOCAL_IMAGE:
613 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
614 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700615 | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP;
ztenghui0353ca22013-08-13 13:53:16 -0700616 break;
617 case LocalData.LOCAL_VIDEO:
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700618 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM
619 | SUPPORT_SHARE;
ztenghui0353ca22013-08-13 13:53:16 -0700620 break;
621 case LocalData.LOCAL_PHOTO_SPHERE:
622 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
623 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700624 | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP;
625 break;
626 case LocalData.LOCAL_360_PHOTO_SPHERE:
627 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO
628 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT
629 | SUPPORT_SHARE | SUPPORT_SHARE_PANORAMA360
ztenghui0353ca22013-08-13 13:53:16 -0700630 | SUPPORT_SHOW_ON_MAP;
631 break;
632 default:
633 break;
634 }
635
ztenghuie941cbe2013-10-02 17:31:47 -0700636 // In secure camera mode, we only support delete operation.
637 if (isSecureCamera()) {
638 supported &= SUPPORT_DELETE;
639 }
640
ztenghui0353ca22013-08-13 13:53:16 -0700641 setMenuItemVisible(mActionBarMenu, R.id.action_delete,
642 (supported & SUPPORT_DELETE) != 0);
643 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw,
644 (supported & SUPPORT_ROTATE) != 0);
645 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw,
646 (supported & SUPPORT_ROTATE) != 0);
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700647 setMenuItemVisible(mActionBarMenu, R.id.action_details,
648 (supported & SUPPORT_INFO) != 0);
ztenghui0353ca22013-08-13 13:53:16 -0700649 setMenuItemVisible(mActionBarMenu, R.id.action_crop,
650 (supported & SUPPORT_CROP) != 0);
ztenghui0353ca22013-08-13 13:53:16 -0700651 setMenuItemVisible(mActionBarMenu, R.id.action_setas,
652 (supported & SUPPORT_SETAS) != 0);
ztenghui0353ca22013-08-13 13:53:16 -0700653 setMenuItemVisible(mActionBarMenu, R.id.action_edit,
654 (supported & SUPPORT_EDIT) != 0);
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700655 setMenuItemVisible(mActionBarMenu, R.id.action_trim,
656 (supported & SUPPORT_TRIM) != 0);
657
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700658 boolean standardShare = (supported & SUPPORT_SHARE) != 0;
659 boolean panoramaShare = (supported & SUPPORT_SHARE_PANORAMA360) != 0;
660 setMenuItemVisible(mActionBarMenu, R.id.action_share, standardShare);
661 setMenuItemVisible(mActionBarMenu, R.id.action_share_panorama, panoramaShare);
662
663 if (panoramaShare) {
664 // For 360 PhotoSphere, relegate standard share to the overflow menu
665 MenuItem item = mActionBarMenu.findItem(R.id.action_share);
666 if (item != null) {
667 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
668 item.setTitle(getResources().getString(R.string.share_as_photo));
669 }
670 // And, promote "share as panorama" to action bar
671 item = mActionBarMenu.findItem(R.id.action_share_panorama);
672 if (item != null) {
673 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
674 }
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700675 setPanoramaShareIntent(currentData.getContentUri());
676 }
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700677 if (standardShare) {
678 if (!panoramaShare) {
679 MenuItem item = mActionBarMenu.findItem(R.id.action_share);
680 if (item != null) {
681 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
682 item.setTitle(getResources().getString(R.string.share));
683 }
684 }
685 setStandardShareIntent(currentData.getContentUri(), currentData.getMimeType());
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -0700686 setNfcBeamPushUri(currentData.getContentUri());
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700687 }
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700688
689 boolean itemHasLocation = currentData.getLatLong() != null;
690 setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map,
691 itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0);
ztenghui0353ca22013-08-13 13:53:16 -0700692 }
693
694 private void setMenuItemVisible(Menu menu, int itemId, boolean visible) {
695 MenuItem item = menu.findItem(itemId);
696 if (item != null)
697 item.setVisible(visible);
698 }
699
Ruben Brunk7cfcafd2013-10-17 15:41:44 -0700700 private ImageTaskManager.TaskListener mPlaceholderListener =
701 new ImageTaskManager.TaskListener() {
702
703 @Override
704 public void onTaskQueued(String filePath, final Uri imageUri) {
705 mMainHandler.post(new Runnable() {
706 @Override
707 public void run() {
708 notifyNewMedia(imageUri);
709 int dataID = mDataAdapter.findDataByContentUri(imageUri);
710 if (dataID != -1) {
711 LocalData d = mDataAdapter.getLocalData(dataID);
712 InProgressDataWrapper newData = new InProgressDataWrapper(d, true);
713 mDataAdapter.updateData(dataID, newData);
714 }
715 }
716 });
717 }
718
719 @Override
720 public void onTaskDone(String filePath, final Uri imageUri) {
721 mMainHandler.post(new Runnable() {
722 @Override
723 public void run() {
724 mDataAdapter.refresh(getContentResolver(), imageUri);
725 }
726 });
727 }
728
729 @Override
730 public void onTaskProgress(String filePath, Uri imageUri, int progress) {
731 // Do nothing
732 }
733 };
734
Angus Kong6798c342013-07-16 15:14:58 -0700735 private ImageTaskManager.TaskListener mStitchingListener =
736 new ImageTaskManager.TaskListener() {
737 @Override
Sascha Haeberling37f36112013-08-06 14:31:52 -0700738 public void onTaskQueued(String filePath, final Uri imageUri) {
739 mMainHandler.post(new Runnable() {
740 @Override
741 public void run() {
742 notifyNewMedia(imageUri);
Angus Kong32509872013-10-02 16:53:40 -0700743 int dataID = mDataAdapter.findDataByContentUri(imageUri);
744 if (dataID != -1) {
745 // Don't allow special UI actions (swipe to
746 // delete, for example) on in-progress data.
747 LocalData d = mDataAdapter.getLocalData(dataID);
748 InProgressDataWrapper newData = new InProgressDataWrapper(d);
749 mDataAdapter.updateData(dataID, newData);
750 }
Sascha Haeberling37f36112013-08-06 14:31:52 -0700751 }
752 });
Angus Kong6798c342013-07-16 15:14:58 -0700753 }
754
755 @Override
Sascha Haeberling37f36112013-08-06 14:31:52 -0700756 public void onTaskDone(String filePath, final Uri imageUri) {
757 Log.v(TAG, "onTaskDone:" + filePath);
758 mMainHandler.post(new Runnable() {
759 @Override
760 public void run() {
761 int doneID = mDataAdapter.findDataByContentUri(imageUri);
762 int currentDataId = mFilmStripView.getCurrentId();
763
764 if (currentDataId == doneID) {
765 hidePanoStitchingProgress();
766 updateStitchingProgress(0);
767 }
768
769 mDataAdapter.refresh(getContentResolver(), imageUri);
770 }
771 });
Angus Kong6798c342013-07-16 15:14:58 -0700772 }
773
774 @Override
775 public void onTaskProgress(
Sascha Haeberling37f36112013-08-06 14:31:52 -0700776 String filePath, final Uri imageUri, final int progress) {
777 mMainHandler.post(new Runnable() {
778 @Override
779 public void run() {
780 int currentDataId = mFilmStripView.getCurrentId();
781 if (currentDataId == -1) {
782 return;
783 }
784 if (imageUri.equals(
785 mDataAdapter.getLocalData(currentDataId).getContentUri())) {
786 updateStitchingProgress(progress);
787 }
788 }
789 });
Angus Kong6798c342013-07-16 15:14:58 -0700790 }
791 };
792
Angus Kongfd4fc0e2013-11-07 15:38:09 -0800793 public MediaSaver getMediaSaver() {
794 return mMediaSaver;
Michael Kolb8872c232013-01-29 10:33:22 -0800795 }
796
Doris Liu6432cd62013-06-13 17:20:31 -0700797 public void notifyNewMedia(Uri uri) {
798 ContentResolver cr = getContentResolver();
799 String mimeType = cr.getType(uri);
800 if (mimeType.startsWith("video/")) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700801 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri));
Doris Liu6432cd62013-06-13 17:20:31 -0700802 mDataAdapter.addNewVideo(cr, uri);
803 } else if (mimeType.startsWith("image/")) {
Angus Kongb50b5cb2013-08-09 14:55:20 -0700804 CameraUtil.broadcastNewPicture(this, uri);
Doris Liu6432cd62013-06-13 17:20:31 -0700805 mDataAdapter.addNewPhoto(cr, uri);
Sascha Haeberling37f36112013-08-06 14:31:52 -0700806 } else if (mimeType.startsWith("application/stitching-preview")) {
807 mDataAdapter.addNewPhoto(cr, uri);
Ruben Brunk7cfcafd2013-10-17 15:41:44 -0700808 } else if (mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) {
809 mDataAdapter.addNewPhoto(cr, uri);
Doris Liu48239f42013-03-04 22:19:10 -0800810 } else {
Doris Liu6432cd62013-06-13 17:20:31 -0700811 android.util.Log.w(TAG, "Unknown new media with MIME type:"
812 + mimeType + ", uri:" + uri);
Doris Liu48239f42013-03-04 22:19:10 -0800813 }
814 }
815
Doris Liu6432cd62013-06-13 17:20:31 -0700816 private void removeData(int dataID) {
817 mDataAdapter.removeData(CameraActivity.this, dataID);
Doris Liu742cd5b2013-09-12 16:17:43 -0700818 if (mDataAdapter.getTotalNumber() > 1) {
819 showUndoDeletionBar();
820 } else {
821 // If camera preview is the only view left in filmstrip,
822 // no need to show undo bar.
Doris Liuf2c14332013-09-26 11:24:37 -0700823 mPendingDeletion = true;
Doris Liu742cd5b2013-09-12 16:17:43 -0700824 performDeletion();
825 }
Michael Kolb8872c232013-01-29 10:33:22 -0800826 }
827
Angus Kong86d36312013-01-31 18:22:44 -0800828 private void bindMediaSaveService() {
829 Intent intent = new Intent(this, MediaSaveService.class);
Angus Kong86d36312013-01-31 18:22:44 -0800830 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
831 }
832
833 private void unbindMediaSaveService() {
Michael Kolb955e5c52013-03-22 14:11:57 -0700834 if (mConnection != null) {
835 unbindService(mConnection);
836 }
Angus Kong86d36312013-01-31 18:22:44 -0800837 }
838
Michael Kolb8872c232013-01-29 10:33:22 -0800839 @Override
ztenghui0353ca22013-08-13 13:53:16 -0700840 public boolean onCreateOptionsMenu(Menu menu) {
841 // Inflate the menu items for use in the action bar
842 MenuInflater inflater = getMenuInflater();
843 inflater.inflate(R.menu.operations, menu);
844 mActionBarMenu = menu;
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700845
846 // Configure the standard share action provider
847 MenuItem item = menu.findItem(R.id.action_share);
848 mStandardShareActionProvider = (ShareActionProvider) item.getActionProvider();
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700849 mStandardShareActionProvider.setShareHistoryFileName("standard_share_history.xml");
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700850 if (mStandardShareIntent != null) {
851 mStandardShareActionProvider.setShareIntent(mStandardShareIntent);
852 }
853
854 // Configure the panorama share action provider
855 item = menu.findItem(R.id.action_share_panorama);
856 mPanoramaShareActionProvider = (ShareActionProvider) item.getActionProvider();
Mangesh Ghiware33035c12013-08-23 13:28:21 -0700857 mPanoramaShareActionProvider.setShareHistoryFileName("panorama_share_history.xml");
Mangesh Ghiwarecd2eeb02013-08-23 13:28:21 -0700858 if (mPanoramaShareIntent != null) {
859 mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent);
860 }
861
ztenghui0353ca22013-08-13 13:53:16 -0700862 return super.onCreateOptionsMenu(menu);
863 }
864
865 @Override
866 public boolean onOptionsItemSelected(MenuItem item) {
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700867 int currentDataId = mFilmStripView.getCurrentId();
868 if (currentDataId < 0) {
869 return false;
870 }
871 final LocalData localData = mDataAdapter.getLocalData(currentDataId);
872
ztenghui0353ca22013-08-13 13:53:16 -0700873 // Handle presses on the action bar items
874 switch (item.getItemId()) {
Alan Newberger3f969c12013-08-23 10:10:30 -0700875 case android.R.id.home:
876 // ActionBar's Up/Home button was clicked
Mangesh Ghiware30968d02013-10-02 14:38:12 -0700877 try {
Alan Newberger761306f2013-10-30 12:51:21 -0700878 startActivity(IntentHelper.getGalleryIntent(this));
Mangesh Ghiware30968d02013-10-02 14:38:12 -0700879 return true;
880 } catch (ActivityNotFoundException e) {
Alan Newberger761306f2013-10-30 12:51:21 -0700881 Log.w(TAG, "Failed to launch gallery activity, closing");
Mangesh Ghiware30968d02013-10-02 14:38:12 -0700882 finish();
Mangesh Ghiware5df64b02013-09-18 18:25:26 -0700883 }
ztenghui0353ca22013-08-13 13:53:16 -0700884 case R.id.action_delete:
Angus Konge857cc92013-08-19 14:46:20 -0700885 removeData(currentDataId);
ztenghui0353ca22013-08-13 13:53:16 -0700886 return true;
887 case R.id.action_edit:
Sascha Haeberling88ef7662013-08-15 17:19:22 -0700888 launchEditor(localData);
ztenghui0353ca22013-08-13 13:53:16 -0700889 return true;
nicolasroard08193042013-08-20 09:19:23 -0700890 case R.id.action_trim: {
ztenghui6b920322013-08-16 16:21:22 -0700891 // This is going to be handled by the Gallery app.
892 Intent intent = new Intent(ACTION_TRIM_VIDEO);
893 LocalData currentData = mDataAdapter.getLocalData(
894 mFilmStripView.getCurrentId());
895 intent.setData(currentData.getContentUri());
896 // We need the file path to wrap this into a RandomAccessFile.
897 intent.putExtra(MEDIA_ITEM_PATH, currentData.getPath());
Sascha Haeberlingb7639c62013-09-09 14:42:43 -0700898 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
ztenghui0353ca22013-08-13 13:53:16 -0700899 return true;
nicolasroard08193042013-08-20 09:19:23 -0700900 }
ztenghui0353ca22013-08-13 13:53:16 -0700901 case R.id.action_rotate_ccw:
ztenghuia16e7b52013-08-23 11:47:56 -0700902 localData.rotate90Degrees(this, mDataAdapter, currentDataId, false);
ztenghui0353ca22013-08-13 13:53:16 -0700903 return true;
904 case R.id.action_rotate_cw:
ztenghuia16e7b52013-08-23 11:47:56 -0700905 localData.rotate90Degrees(this, mDataAdapter, currentDataId, true);
ztenghui0353ca22013-08-13 13:53:16 -0700906 return true;
nicolasroard19ab7252013-09-18 16:54:05 -0700907 case R.id.action_crop: {
908 Intent intent = new Intent(CropActivity.CROP_ACTION);
909 intent.setClass(this, CropActivity.class);
910 intent.setDataAndType(localData.getContentUri(), localData.getMimeType())
911 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
912 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
ztenghui0353ca22013-08-13 13:53:16 -0700913 return true;
nicolasroard19ab7252013-09-18 16:54:05 -0700914 }
nicolasroard08193042013-08-20 09:19:23 -0700915 case R.id.action_setas: {
916 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA)
917 .setDataAndType(localData.getContentUri(),
918 localData.getMimeType())
919 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
920 intent.putExtra("mimeType", intent.getType());
Sascha Haeberlingb7639c62013-09-09 14:42:43 -0700921 startActivityForResult(Intent.createChooser(
922 intent, getString(R.string.set_as)), REQ_CODE_DONT_SWITCH_TO_PREVIEW);
ztenghui0353ca22013-08-13 13:53:16 -0700923 return true;
nicolasroard08193042013-08-20 09:19:23 -0700924 }
ztenghui0353ca22013-08-13 13:53:16 -0700925 case R.id.action_details:
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700926 (new AsyncTask<Void, Void, MediaDetails>() {
927 @Override
928 protected MediaDetails doInBackground(Void... params) {
929 return localData.getMediaDetails(CameraActivity.this);
930 }
931
932 @Override
933 protected void onPostExecute(MediaDetails mediaDetails) {
Sascha Haeberling64cea6a2013-10-10 20:12:07 -0700934 if (mediaDetails != null) {
935 DetailsDialog.create(CameraActivity.this, mediaDetails).show();
936 }
Sascha Haeberling6f64b502013-08-14 16:23:18 -0700937 }
938 }).execute();
ztenghui0353ca22013-08-13 13:53:16 -0700939 return true;
940 case R.id.action_show_on_map:
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700941 double[] latLong = localData.getLatLong();
942 if (latLong != null) {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -0700943 CameraUtil.showOnMap(this, latLong);
Sascha Haeberlingfae11a12013-08-15 14:29:49 -0700944 }
ztenghui0353ca22013-08-13 13:53:16 -0700945 return true;
946 default:
947 return super.onOptionsItemSelected(item);
948 }
949 }
950
ztenghuifd43e3b2013-09-03 11:30:11 -0700951 private boolean isCaptureIntent() {
952 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())
953 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
954 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
955 return true;
956 } else {
957 return false;
958 }
959 }
960
ztenghui0353ca22013-08-13 13:53:16 -0700961 @Override
Doris Liu6432cd62013-06-13 17:20:31 -0700962 public void onCreate(Bundle state) {
963 super.onCreate(state);
Sascha Haeberling048bf4d2013-10-06 17:49:51 -0700964 GcamHelper.init(getContentResolver());
965
ztenghui50df4702013-08-13 15:53:57 -0700966 getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
Doris Liu6432cd62013-06-13 17:20:31 -0700967 setContentView(R.layout.camera_filmstrip);
ztenghuifa9e2cc2013-08-09 17:37:15 -0700968 mActionBar = getActionBar();
Doris Liuaa874422013-09-18 19:43:12 -0700969 mActionBar.addOnMenuVisibilityListener(this);
ztenghuifa9e2cc2013-08-09 17:37:15 -0700970
Michael Kolb08650182013-02-25 19:43:56 -0800971 if (ApiHelper.HAS_ROTATION_ANIMATION) {
Doris Liu6432cd62013-06-13 17:20:31 -0700972 setRotationAnimation();
Michael Kolb08650182013-02-25 19:43:56 -0800973 }
Doris Liuaa874422013-09-18 19:43:12 -0700974
975 mMainHandler = new MainHandler(getMainLooper());
Doris Liu6432cd62013-06-13 17:20:31 -0700976 // Check if this is in the secure camera mode.
977 Intent intent = getIntent();
978 String action = intent.getAction();
Doris Liub84b9732013-06-18 17:14:26 -0700979 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action)
980 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) {
Doris Liu6432cd62013-06-13 17:20:31 -0700981 mSecureCamera = true;
982 } else {
983 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false);
984 }
Doris Liub84b9732013-06-18 17:14:26 -0700985
986 if (mSecureCamera) {
987 // Change the window flags so that secure camera can show when locked
988 Window win = getWindow();
989 WindowManager.LayoutParams params = win.getAttributes();
990 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
991 win.setAttributes(params);
992
993 // Filter for screen off so that we can finish secure camera activity
994 // when screen is off.
Doris Liu6432cd62013-06-13 17:20:31 -0700995 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
996 registerReceiver(mScreenOffReceiver, filter);
Angus Kong6a8e8a12013-07-19 14:55:07 -0700997 // TODO: This static screen off event receiver is a workaround to the
998 // double onResume() invocation (onResume->onPause->onResume). We should
999 // find a better solution to this.
1000 if (sScreenOffReceiver == null) {
1001 sScreenOffReceiver = new ScreenOffReceiver();
1002 registerReceiver(sScreenOffReceiver, filter);
1003 }
Doris Liub84b9732013-06-18 17:14:26 -07001004 }
Angus Kong653c43b2013-08-21 18:28:43 -07001005 mAboveFilmstripControlLayout =
1006 (FrameLayout) findViewById(R.id.camera_above_filmstrip_layout);
1007 mAboveFilmstripControlLayout.setFitsSystemWindows(true);
Sascha Haeberling5199c202013-09-05 17:10:19 -07001008 // Hide action bar first since we are in full screen mode first, and
1009 // switch the system UI to lights-out mode.
Doris Liuaa874422013-09-18 19:43:12 -07001010 this.setSystemBarsVisibility(false);
Angus Konged15d1a2013-08-19 15:06:12 -07001011 mPanoramaManager = AppManagerFactory.getInstance(this)
1012 .getPanoramaStitchingManager();
Ruben Brunk7cfcafd2013-10-17 15:41:44 -07001013 mPlaceholderManager = AppManagerFactory.getInstance(this)
1014 .getGcamProcessingManager();
Angus Kong6798c342013-07-16 15:14:58 -07001015 mPanoramaManager.addTaskListener(mStitchingListener);
Ruben Brunk7cfcafd2013-10-17 15:41:44 -07001016 mPlaceholderManager.addTaskListener(mPlaceholderListener);
Doris Liu6432cd62013-06-13 17:20:31 -07001017 LayoutInflater inflater = getLayoutInflater();
1018 View rootLayout = inflater.inflate(R.layout.camera, null, false);
Angus Kong653c43b2013-08-21 18:28:43 -07001019 mCameraModuleRootView = rootLayout.findViewById(R.id.camera_app_root);
ztenghuifa9e2cc2013-08-09 17:37:15 -07001020 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel);
Sascha Haeberling37f36112013-08-06 14:31:52 -07001021 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar);
Angus Kong8e5e4ee2013-07-30 11:36:00 -07001022 mCameraPreviewData = new CameraPreviewData(rootLayout,
1023 FilmStripView.ImageData.SIZE_FULL,
1024 FilmStripView.ImageData.SIZE_FULL);
Sascha Haeberling37f36112013-08-06 14:31:52 -07001025 // Put a CameraPreviewData at the first position.
Angus Kong8e5e4ee2013-07-30 11:36:00 -07001026 mWrappedDataAdapter = new FixedFirstDataAdapter(
1027 new CameraDataAdapter(new ColorDrawable(
1028 getResources().getColor(R.color.photo_placeholder))),
1029 mCameraPreviewData);
Doris Liu6432cd62013-06-13 17:20:31 -07001030 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view);
1031 mFilmStripView.setViewGap(
1032 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap));
Sascha Haeberlingf1f51862013-07-31 11:28:21 -07001033 mPanoramaViewHelper = new PanoramaViewHelper(this);
1034 mPanoramaViewHelper.onCreate();
1035 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper);
Doris Liu6432cd62013-06-13 17:20:31 -07001036 // Set up the camera preview first so the preview shows up ASAP.
Doris Liu6432cd62013-06-13 17:20:31 -07001037 mFilmStripView.setListener(mFilmStripListener);
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001038
1039 int moduleIndex = -1;
Doris Liu2a7f44c2013-08-12 15:18:53 -07001040 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction())
1041 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) {
Angus Konged15d1a2013-08-19 15:06:12 -07001042 moduleIndex = ModuleSwitcher.VIDEO_MODULE_INDEX;
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001043 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction())
1044 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent()
1045 .getAction())
1046 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())
1047 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) {
Angus Konged15d1a2013-08-19 15:06:12 -07001048 moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
Doris Liu2a7f44c2013-08-12 15:18:53 -07001049 } else {
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001050 // If the activity has not been started using an explicit intent,
1051 // read the module index from the last time the user changed modes
1052 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Angus Kong0fb819b2013-10-08 13:44:19 -07001053 moduleIndex = prefs.getInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, -1);
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001054 if ((moduleIndex == ModuleSwitcher.GCAM_MODULE_INDEX &&
Sascha Haeberling98f38bb2013-09-24 18:58:34 -07001055 !GcamHelper.hasGcamCapture()) || moduleIndex < 0) {
Angus Konged15d1a2013-08-19 15:06:12 -07001056 moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001057 }
Doris Liu2a7f44c2013-08-12 15:18:53 -07001058 }
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001059
Doris Liu6432cd62013-06-13 17:20:31 -07001060 mOrientationListener = new MyOrientationEventListener(this);
Angus Konged15d1a2013-08-19 15:06:12 -07001061 setModuleFromIndex(moduleIndex);
1062 mCurrentModule.init(this, mCameraModuleRootView);
Sascha Haeberling37f36112013-08-06 14:31:52 -07001063
1064 if (!mSecureCamera) {
1065 mDataAdapter = mWrappedDataAdapter;
Angus Kongdcccc512013-08-08 17:06:03 -07001066 mFilmStripView.setDataAdapter(mDataAdapter);
Angus Konga7194602013-09-06 17:20:15 -07001067 if (!isCaptureIntent()) {
1068 mDataAdapter.requestLoad(getContentResolver());
1069 }
Sascha Haeberling37f36112013-08-06 14:31:52 -07001070 } else {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001071 // Put a lock placeholder as the last image by setting its date to
1072 // 0.
Sascha Haeberling37f36112013-08-06 14:31:52 -07001073 ImageView v = (ImageView) getLayoutInflater().inflate(
1074 R.layout.secure_album_placeholder, null);
Angus Kong690dc472013-09-21 14:48:51 -07001075 v.setOnClickListener(new View.OnClickListener() {
1076 @Override
1077 public void onClick(View view) {
Alan Newberger761306f2013-10-30 12:51:21 -07001078 try {
1079 startActivity(IntentHelper.getGalleryIntent(CameraActivity.this));
1080 } catch (ActivityNotFoundException e) {
1081 Log.w(TAG, "Failed to launch gallery activity, closing");
1082 }
Angus Kong690dc472013-09-21 14:48:51 -07001083 finish();
1084 }
1085 });
Sascha Haeberling37f36112013-08-06 14:31:52 -07001086 mDataAdapter = new FixedLastDataAdapter(
1087 mWrappedDataAdapter,
Angus Kongbd260692013-08-07 14:52:56 -07001088 new SimpleViewData(
Sascha Haeberling37f36112013-08-06 14:31:52 -07001089 v,
1090 v.getDrawable().getIntrinsicWidth(),
1091 v.getDrawable().getIntrinsicHeight(),
1092 0, 0));
1093 // Flush out all the original data.
1094 mDataAdapter.flush();
Angus Kongdcccc512013-08-08 17:06:03 -07001095 mFilmStripView.setDataAdapter(mDataAdapter);
Sascha Haeberling37f36112013-08-06 14:31:52 -07001096 }
Mangesh Ghiwarea5682ab2013-09-19 09:24:23 -07001097
1098 setupNfcBeamPush();
ztenghui064d6002013-09-05 15:47:58 -07001099
Doris Liu2b86d872013-09-26 15:23:41 -07001100 mLocalImagesObserver = new LocalMediaObserver();
1101 mLocalVideosObserver = new LocalMediaObserver();
ztenghui064d6002013-09-05 15:47:58 -07001102
1103 getContentResolver().registerContentObserver(
1104 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true,
1105 mLocalImagesObserver);
1106 getContentResolver().registerContentObserver(
1107 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true,
1108 mLocalVideosObserver);
Michael Kolb8872c232013-01-29 10:33:22 -08001109 }
1110
Doris Liu6432cd62013-06-13 17:20:31 -07001111 private void setRotationAnimation() {
Michael Kolb08650182013-02-25 19:43:56 -08001112 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
Doris Liu6432cd62013-06-13 17:20:31 -07001113 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
Michael Kolb08650182013-02-25 19:43:56 -08001114 Window win = getWindow();
1115 WindowManager.LayoutParams winParams = win.getAttributes();
1116 winParams.rotationAnimation = rotationAnimation;
1117 win.setAttributes(winParams);
1118 }
1119
Michael Kolb8872c232013-01-29 10:33:22 -08001120 @Override
Michael Kolb8872c232013-01-29 10:33:22 -08001121 public void onUserInteraction() {
1122 super.onUserInteraction();
1123 mCurrentModule.onUserInteraction();
1124 }
1125
1126 @Override
Doris Liu742cd5b2013-09-12 16:17:43 -07001127 public boolean dispatchTouchEvent(MotionEvent ev) {
1128 boolean result = super.dispatchTouchEvent(ev);
1129 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
1130 // Real deletion is postponed until the next user interaction after
1131 // the gesture that triggers deletion. Until real deletion is performed,
1132 // users can click the undo button to bring back the image that they
1133 // chose to delete.
1134 if (mPendingDeletion && !mIsUndoingDeletion) {
1135 performDeletion();
1136 }
1137 }
1138 return result;
1139 }
1140
1141 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001142 public void onPause() {
Doris Liuf2c14332013-09-26 11:24:37 -07001143 // Delete photos that are pending deletion
1144 performDeletion();
Doris Liu6432cd62013-06-13 17:20:31 -07001145 mOrientationListener.disable();
1146 mCurrentModule.onPauseBeforeSuper();
1147 super.onPause();
1148 mCurrentModule.onPauseAfterSuper();
Doris Liu2b86d872013-09-26 15:23:41 -07001149
1150 mLocalImagesObserver.setActivityPaused(true);
1151 mLocalVideosObserver.setActivityPaused(true);
Doris Liu6432cd62013-06-13 17:20:31 -07001152 }
1153
1154 @Override
Sascha Haeberlingb7639c62013-09-09 14:42:43 -07001155 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
ztenghui064d6002013-09-05 15:47:58 -07001156 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) {
Sascha Haeberlingb7639c62013-09-09 14:42:43 -07001157 mResetToPreviewOnResume = false;
1158 } else {
1159 super.onActivityResult(requestCode, resultCode, data);
1160 }
1161 }
1162
1163 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001164 public void onResume() {
Angus Kongce2b9492013-09-05 17:49:06 -07001165 // TODO: Handle this in OrientationManager.
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001166 // Auto-rotate off
Doris Liu6432cd62013-06-13 17:20:31 -07001167 if (Settings.System.getInt(getContentResolver(),
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001168 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {
Doris Liu6432cd62013-06-13 17:20:31 -07001169 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
1170 mAutoRotateScreen = false;
1171 } else {
1172 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
1173 mAutoRotateScreen = true;
1174 }
1175 mOrientationListener.enable();
1176 mCurrentModule.onResumeBeforeSuper();
1177 super.onResume();
1178 mCurrentModule.onResumeAfterSuper();
1179
Angus Kong6798c342013-07-16 15:14:58 -07001180 setSwipingEnabled(true);
Sascha Haeberlingb7639c62013-09-09 14:42:43 -07001181
1182 if (mResetToPreviewOnResume) {
1183 // Go to the preview on resume.
1184 mFilmStripView.getController().goToFirstItem();
1185 }
1186 // Default is showing the preview, unless disabled by explicitly
1187 // starting an activity we want to return from to the filmstrip rather
1188 // than the preview.
1189 mResetToPreviewOnResume = true;
ztenghui064d6002013-09-05 15:47:58 -07001190
Doris Liu2b86d872013-09-26 15:23:41 -07001191 if (mLocalVideosObserver.isMediaDataChangedDuringPause()
1192 || mLocalImagesObserver.isMediaDataChangedDuringPause()) {
Angus Kong2d5c7472013-10-12 23:48:46 -07001193 if (!mSecureCamera) {
1194 // If it's secure camera, requestLoad() should not be called
1195 // as it will load all the data.
1196 mDataAdapter.requestLoad(getContentResolver());
1197 }
ztenghui064d6002013-09-05 15:47:58 -07001198 }
Doris Liu2b86d872013-09-26 15:23:41 -07001199 mLocalImagesObserver.setActivityPaused(false);
1200 mLocalVideosObserver.setActivityPaused(false);
Angus Kong6798c342013-07-16 15:14:58 -07001201 }
1202
1203 @Override
1204 public void onStart() {
1205 super.onStart();
Angus Kong7e374eb2013-08-21 11:27:35 -07001206 bindMediaSaveService();
Sascha Haeberlingf1f51862013-07-31 11:28:21 -07001207 mPanoramaViewHelper.onStart();
1208 }
1209
1210 @Override
1211 protected void onStop() {
1212 super.onStop();
1213 mPanoramaViewHelper.onStop();
Angus Kong7e374eb2013-08-21 11:27:35 -07001214 unbindMediaSaveService();
Doris Liu6432cd62013-06-13 17:20:31 -07001215 }
1216
1217 @Override
1218 public void onDestroy() {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001219 if (mSecureCamera) {
1220 unregisterReceiver(mScreenOffReceiver);
1221 }
ztenghui064d6002013-09-05 15:47:58 -07001222 getContentResolver().unregisterContentObserver(mLocalImagesObserver);
1223 getContentResolver().unregisterContentObserver(mLocalVideosObserver);
1224
Doris Liu6432cd62013-06-13 17:20:31 -07001225 super.onDestroy();
1226 }
1227
1228 @Override
1229 public void onConfigurationChanged(Configuration config) {
1230 super.onConfigurationChanged(config);
1231 mCurrentModule.onConfigurationChanged(config);
1232 }
1233
1234 @Override
1235 public boolean onKeyDown(int keyCode, KeyEvent event) {
Doris Liudba16ae2013-10-03 15:31:40 -07001236 if (mFilmStripView.inCameraFullscreen()) {
1237 if (mCurrentModule.onKeyDown(keyCode, event)) {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001238 return true;
1239 }
Doris Liudba16ae2013-10-03 15:31:40 -07001240 // Prevent software keyboard or voice search from showing up.
1241 if (keyCode == KeyEvent.KEYCODE_SEARCH
1242 || keyCode == KeyEvent.KEYCODE_MENU) {
1243 if (event.isLongPress()) {
1244 return true;
1245 }
1246 }
Doris Liu6432cd62013-06-13 17:20:31 -07001247 }
Doris Liu6432cd62013-06-13 17:20:31 -07001248
1249 return super.onKeyDown(keyCode, event);
1250 }
1251
1252 @Override
1253 public boolean onKeyUp(int keyCode, KeyEvent event) {
Doris Liudba16ae2013-10-03 15:31:40 -07001254 if (mFilmStripView.inCameraFullscreen() && mCurrentModule.onKeyUp(keyCode, event)) {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001255 return true;
1256 }
Doris Liu6432cd62013-06-13 17:20:31 -07001257 return super.onKeyUp(keyCode, event);
1258 }
1259
Alan Newberger5f6b50d2013-08-30 15:19:48 -07001260 @Override
1261 public void onBackPressed() {
1262 if (!mFilmStripView.inCameraFullscreen()) {
1263 mFilmStripView.getController().goToFirstItem();
1264 } else if (!mCurrentModule.onBackPressed()) {
1265 super.onBackPressed();
1266 }
1267 }
1268
Doris Liu6432cd62013-06-13 17:20:31 -07001269 public boolean isAutoRotateScreen() {
1270 return mAutoRotateScreen;
1271 }
1272
1273 protected void updateStorageSpace() {
Angus Kong2dcc0a92013-09-25 13:00:08 -07001274 mStorageSpaceBytes = Storage.getAvailableSpace();
Doris Liu6432cd62013-06-13 17:20:31 -07001275 }
1276
Angus Kong2dcc0a92013-09-25 13:00:08 -07001277 protected long getStorageSpaceBytes() {
1278 return mStorageSpaceBytes;
Doris Liu6432cd62013-06-13 17:20:31 -07001279 }
1280
1281 protected void updateStorageSpaceAndHint() {
1282 updateStorageSpace();
Angus Kong2dcc0a92013-09-25 13:00:08 -07001283 updateStorageHint(mStorageSpaceBytes);
Doris Liu6432cd62013-06-13 17:20:31 -07001284 }
1285
1286 protected void updateStorageHint(long storageSpace) {
1287 String message = null;
1288 if (storageSpace == Storage.UNAVAILABLE) {
1289 message = getString(R.string.no_storage);
1290 } else if (storageSpace == Storage.PREPARING) {
1291 message = getString(R.string.preparing_sd);
1292 } else if (storageSpace == Storage.UNKNOWN_SIZE) {
1293 message = getString(R.string.access_sd_fail);
Angus Kong2dcc0a92013-09-25 13:00:08 -07001294 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) {
Doris Liu6432cd62013-06-13 17:20:31 -07001295 message = getString(R.string.spaceIsLow_content);
1296 }
1297
1298 if (message != null) {
1299 if (mStorageHint == null) {
1300 mStorageHint = OnScreenHint.makeText(this, message);
1301 } else {
1302 mStorageHint.setText(message);
1303 }
1304 mStorageHint.show();
1305 } else if (mStorageHint != null) {
1306 mStorageHint.cancel();
1307 mStorageHint = null;
1308 }
1309 }
1310
1311 protected void setResultEx(int resultCode) {
1312 mResultCodeForTesting = resultCode;
1313 setResult(resultCode);
1314 }
1315
1316 protected void setResultEx(int resultCode, Intent data) {
1317 mResultCodeForTesting = resultCode;
1318 mResultDataForTesting = data;
1319 setResult(resultCode, data);
1320 }
1321
1322 public int getResultCode() {
1323 return mResultCodeForTesting;
1324 }
1325
1326 public Intent getResultData() {
1327 return mResultDataForTesting;
1328 }
1329
1330 public boolean isSecureCamera() {
1331 return mSecureCamera;
Michael Kolb8872c232013-01-29 10:33:22 -08001332 }
1333
1334 @Override
Angus Konged15d1a2013-08-19 15:06:12 -07001335 public void onModuleSelected(int moduleIndex) {
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001336 if (mCurrentModuleIndex == moduleIndex) {
1337 return;
1338 }
Doris Liu6432cd62013-06-13 17:20:31 -07001339
1340 CameraHolder.instance().keep();
1341 closeModule(mCurrentModule);
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001342 setModuleFromIndex(moduleIndex);
1343
1344 openModule(mCurrentModule);
1345 mCurrentModule.onOrientationChanged(mLastRawOrientation);
Angus Kongfd4fc0e2013-11-07 15:38:09 -08001346 if (mMediaSaver != null) {
1347 mCurrentModule.onMediaSaverAvailable(mMediaSaver);
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001348 }
1349
1350 // Store the module index so we can use it the next time the Camera
1351 // starts up.
1352 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
Angus Kong0fb819b2013-10-08 13:44:19 -07001353 prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, moduleIndex).apply();
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001354 }
1355
1356 /**
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001357 * Sets the mCurrentModuleIndex, creates a new module instance for the given
1358 * index an sets it as mCurrentModule.
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001359 */
1360 private void setModuleFromIndex(int moduleIndex) {
1361 mCurrentModuleIndex = moduleIndex;
1362 switch (moduleIndex) {
Sascha Haeberling15125662013-09-17 15:44:22 -07001363 case ModuleSwitcher.VIDEO_MODULE_INDEX:
Doris Liu6432cd62013-06-13 17:20:31 -07001364 mCurrentModule = new VideoModule();
1365 break;
Angus Konged15d1a2013-08-19 15:06:12 -07001366
Sascha Haeberling15125662013-09-17 15:44:22 -07001367 case ModuleSwitcher.PHOTO_MODULE_INDEX:
Doris Liu6432cd62013-06-13 17:20:31 -07001368 mCurrentModule = new PhotoModule();
1369 break;
Angus Konged15d1a2013-08-19 15:06:12 -07001370
Sascha Haeberling15125662013-09-17 15:44:22 -07001371 case ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX:
Angus Konged15d1a2013-08-19 15:06:12 -07001372 mCurrentModule = new WideAnglePanoramaModule();
1373 break;
Angus Konged15d1a2013-08-19 15:06:12 -07001374
Sascha Haeberling15125662013-09-17 15:44:22 -07001375 case ModuleSwitcher.LIGHTCYCLE_MODULE_INDEX:
Sascha Haeberling8e963a52013-08-06 11:43:02 -07001376 mCurrentModule = PhotoSphereHelper.createPanoramaModule();
Doris Liu6432cd62013-06-13 17:20:31 -07001377 break;
Ruben Brunka9d66bd2013-09-06 11:56:32 -07001378 case ModuleSwitcher.GCAM_MODULE_INDEX:
1379 // Force immediate release of Camera instance
1380 CameraHolder.instance().strongRelease();
1381 mCurrentModule = GcamHelper.createGcamModule();
1382 break;
Sascha Haeberling03e01452013-10-07 13:21:33 -07001383 case ModuleSwitcher.REFOCUS_MODULE_INDEX:
1384 mCurrentModule = RefocusHelper.createRefocusModule();
1385 break;
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001386 default:
Sascha Haeberling15125662013-09-17 15:44:22 -07001387 // Fall back to photo mode.
1388 mCurrentModule = new PhotoModule();
1389 mCurrentModuleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX;
Sascha Haeberling2654dd92013-08-28 15:28:57 -07001390 break;
Doris Liu6432cd62013-06-13 17:20:31 -07001391 }
Michael Kolb8872c232013-01-29 10:33:22 -08001392 }
1393
Sascha Haeberling88ef7662013-08-15 17:19:22 -07001394 /**
1395 * Launches an ACTION_EDIT intent for the given local data item.
1396 */
1397 public void launchEditor(LocalData data) {
1398 Intent intent = new Intent(Intent.ACTION_EDIT)
1399 .setDataAndType(data.getContentUri(), data.getMimeType())
1400 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Erin Dahlgren05a04922013-11-07 11:35:10 -08001401 try {
1402 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1403 } catch (ActivityNotFoundException e) {
1404 startActivityForResult(Intent.createChooser(intent, null),
1405 REQ_CODE_DONT_SWITCH_TO_PREVIEW);
1406 }
Sascha Haeberling88ef7662013-08-15 17:19:22 -07001407 }
1408
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001409 /**
1410 * Launch the tiny planet editor.
1411 *
1412 * @param data the data must be a 360 degree stereographically mapped
1413 * panoramic image. It will not be modified, instead a new item
1414 * with the result will be added to the filmstrip.
1415 */
1416 public void launchTinyPlanetEditor(LocalData data) {
1417 TinyPlanetFragment fragment = new TinyPlanetFragment();
1418 Bundle bundle = new Bundle();
1419 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString());
1420 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle());
1421 fragment.setArguments(bundle);
1422 fragment.show(getFragmentManager(), "tiny_planet");
1423 }
1424
Doris Liu6432cd62013-06-13 17:20:31 -07001425 private void openModule(CameraModule module) {
Angus Kong653c43b2013-08-21 18:28:43 -07001426 module.init(this, mCameraModuleRootView);
Doris Liu6432cd62013-06-13 17:20:31 -07001427 module.onResumeBeforeSuper();
1428 module.onResumeAfterSuper();
1429 }
1430
1431 private void closeModule(CameraModule module) {
1432 module.onPauseBeforeSuper();
1433 module.onPauseAfterSuper();
Angus Kong653c43b2013-08-21 18:28:43 -07001434 ((ViewGroup) mCameraModuleRootView).removeAllViews();
1435 }
1436
Doris Liu742cd5b2013-09-12 16:17:43 -07001437 private void performDeletion() {
1438 if (!mPendingDeletion) {
1439 return;
1440 }
1441 hideUndoDeletionBar(false);
1442 mDataAdapter.executeDeletion(CameraActivity.this);
Erin Dahlgrenfd7f0a92013-10-21 10:08:54 -07001443
1444 int currentId = mFilmStripView.getCurrentId();
1445 updateActionBarMenu(currentId);
1446 mFilmStripListener.onCurrentDataCentered(currentId);
Doris Liu742cd5b2013-09-12 16:17:43 -07001447 }
1448
1449 public void showUndoDeletionBar() {
1450 if (mPendingDeletion) {
1451 performDeletion();
1452 }
1453 Log.v(TAG, "showing undo bar");
1454 mPendingDeletion = true;
Angus Kong653c43b2013-08-21 18:28:43 -07001455 if (mUndoDeletionBar == null) {
Angus Kong653c43b2013-08-21 18:28:43 -07001456 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(
1457 R.layout.undo_bar, mAboveFilmstripControlLayout, true);
1458 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar);
1459 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button);
1460 button.setOnClickListener(new View.OnClickListener() {
1461 @Override
1462 public void onClick(View view) {
1463 mDataAdapter.undoDataRemoval();
Doris Liu742cd5b2013-09-12 16:17:43 -07001464 hideUndoDeletionBar(true);
1465 }
1466 });
1467 // Setting undo bar clickable to avoid touch events going through
1468 // the bar to the buttons (eg. edit button, etc) underneath the bar.
1469 mUndoDeletionBar.setClickable(true);
1470 // When there is user interaction going on with the undo button, we
1471 // do not want to hide the undo bar.
1472 button.setOnTouchListener(new View.OnTouchListener() {
1473 @Override
1474 public boolean onTouch(View v, MotionEvent event) {
1475 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
1476 mIsUndoingDeletion = true;
1477 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
1478 mIsUndoingDeletion =false;
1479 }
1480 return false;
Angus Kong653c43b2013-08-21 18:28:43 -07001481 }
1482 });
1483 }
1484 mUndoDeletionBar.setAlpha(0f);
1485 mUndoDeletionBar.setVisibility(View.VISIBLE);
Doris Liu742cd5b2013-09-12 16:17:43 -07001486 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start();
Angus Kong653c43b2013-08-21 18:28:43 -07001487 }
1488
Doris Liu742cd5b2013-09-12 16:17:43 -07001489 private void hideUndoDeletionBar(boolean withAnimation) {
Angus Kong653c43b2013-08-21 18:28:43 -07001490 Log.v(TAG, "Hiding undo deletion bar");
Doris Liu742cd5b2013-09-12 16:17:43 -07001491 mPendingDeletion = false;
Angus Kong653c43b2013-08-21 18:28:43 -07001492 if (mUndoDeletionBar != null) {
Doris Liu742cd5b2013-09-12 16:17:43 -07001493 if (withAnimation) {
1494 mUndoDeletionBar.animate()
1495 .setDuration(200)
1496 .alpha(0f)
1497 .setListener(new Animator.AnimatorListener() {
1498 @Override
1499 public void onAnimationStart(Animator animation) {
1500 // Do nothing.
1501 }
1502
1503 @Override
1504 public void onAnimationEnd(Animator animation) {
1505 mUndoDeletionBar.setVisibility(View.GONE);
1506 }
1507
1508 @Override
1509 public void onAnimationCancel(Animator animation) {
1510 // Do nothing.
1511 }
1512
1513 @Override
1514 public void onAnimationRepeat(Animator animation) {
1515 // Do nothing.
1516 }
1517 })
1518 .start();
1519 } else {
1520 mUndoDeletionBar.setVisibility(View.GONE);
1521 }
Angus Kong653c43b2013-08-21 18:28:43 -07001522 }
Michael Kolb8872c232013-01-29 10:33:22 -08001523 }
1524
1525 @Override
Doris Liu6432cd62013-06-13 17:20:31 -07001526 public void onShowSwitcherPopup() {
1527 }
1528
Angus Konga7194602013-09-06 17:20:15 -07001529 /**
Sascha Haeberlinga5a08d72013-09-11 20:30:52 -07001530 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in
1531 * capture intent.
Angus Konga7194602013-09-06 17:20:15 -07001532 *
1533 * @param enable {@code true} to enable swipe.
1534 */
Doris Liu6432cd62013-06-13 17:20:31 -07001535 public void setSwipingEnabled(boolean enable) {
Angus Konga7194602013-09-06 17:20:15 -07001536 if (isCaptureIntent()) {
1537 mCameraPreviewData.lockPreview(true);
1538 } else {
1539 mCameraPreviewData.lockPreview(!enable);
1540 }
Michael Kolb8872c232013-01-29 10:33:22 -08001541 }
1542
Erin Dahlgren3044d8c2013-10-10 18:23:45 -07001543
1544 /**
1545 * Check whether camera controls are visible.
1546 *
1547 * @return whether controls are visible.
1548 */
1549 private boolean arePreviewControlsVisible() {
1550 return mCurrentModule.arePreviewControlsVisible();
1551 }
1552
1553 /**
1554 * Show or hide the {@link CameraControls} using the current module's
1555 * implementation of {@link #onPreviewFocusChanged}.
1556 *
1557 * @param showControls whether to show camera controls.
1558 */
1559 private void setPreviewControlsVisibility(boolean showControls) {
1560 mCurrentModule.onPreviewFocusChanged(showControls);
1561 }
1562
Michael Kolb8872c232013-01-29 10:33:22 -08001563 // Accessor methods for getting latency times used in performance testing
1564 public long getAutoFocusTime() {
1565 return (mCurrentModule instanceof PhotoModule) ?
1566 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
1567 }
1568
1569 public long getShutterLag() {
1570 return (mCurrentModule instanceof PhotoModule) ?
1571 ((PhotoModule) mCurrentModule).mShutterLag : -1;
1572 }
1573
1574 public long getShutterToPictureDisplayedTime() {
1575 return (mCurrentModule instanceof PhotoModule) ?
1576 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
1577 }
1578
1579 public long getPictureDisplayedToJpegCallbackTime() {
1580 return (mCurrentModule instanceof PhotoModule) ?
1581 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
1582 }
1583
1584 public long getJpegCallbackFinishTime() {
1585 return (mCurrentModule instanceof PhotoModule) ?
1586 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
1587 }
1588
1589 public long getCaptureStartTime() {
1590 return (mCurrentModule instanceof PhotoModule) ?
1591 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
1592 }
1593
1594 public boolean isRecording() {
1595 return (mCurrentModule instanceof VideoModule) ?
1596 ((VideoModule) mCurrentModule).isRecording() : false;
1597 }
Angus Kong4f795b82013-09-16 14:25:35 -07001598
1599 public CameraOpenErrorCallback getCameraOpenErrorCallback() {
1600 return mCameraOpenErrorCallback;
1601 }
Ruben Brunkd217ed02013-10-08 23:31:13 -07001602
1603 // For debugging purposes only.
1604 public CameraModule getCurrentModule() {
1605 return mCurrentModule;
1606 }
Michael Kolb8872c232013-01-29 10:33:22 -08001607}