CameraActivity.java revision 690dc4722d068a1838b16dc0eabe9c7d518d524c
1/* 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 19import android.animation.Animator; 20import android.annotation.TargetApi; 21import android.app.ActionBar; 22import android.app.Activity; 23import android.content.BroadcastReceiver; 24import android.content.ComponentName; 25import android.content.ContentResolver; 26import android.content.Context; 27import android.content.Intent; 28import android.content.IntentFilter; 29import android.content.ServiceConnection; 30import android.content.SharedPreferences; 31import android.content.pm.ActivityInfo; 32import android.content.res.Configuration; 33import android.graphics.drawable.ColorDrawable; 34import android.net.Uri; 35import android.nfc.NfcAdapter; 36import android.nfc.NfcAdapter.CreateBeamUrisCallback; 37import android.nfc.NfcEvent; 38import android.os.AsyncTask; 39import android.os.Build; 40import android.os.Bundle; 41import android.os.Handler; 42import android.os.IBinder; 43import android.os.Looper; 44import android.os.Message; 45import android.preference.PreferenceManager; 46import android.provider.MediaStore; 47import android.provider.Settings; 48import android.util.Log; 49import android.view.KeyEvent; 50import android.view.LayoutInflater; 51import android.view.Menu; 52import android.view.MenuInflater; 53import android.view.MenuItem; 54import android.view.MotionEvent; 55import android.view.OrientationEventListener; 56import android.view.View; 57import android.view.ViewGroup; 58import android.view.Window; 59import android.view.WindowManager; 60import android.widget.FrameLayout; 61import android.widget.ImageView; 62import android.widget.ProgressBar; 63import android.widget.ShareActionProvider; 64 65import com.android.camera.app.AppManagerFactory; 66import com.android.camera.app.PanoramaStitchingManager; 67import com.android.camera.crop.CropActivity; 68import com.android.camera.data.CameraDataAdapter; 69import com.android.camera.data.CameraPreviewData; 70import com.android.camera.data.FixedFirstDataAdapter; 71import com.android.camera.data.FixedLastDataAdapter; 72import com.android.camera.data.LocalData; 73import com.android.camera.data.LocalDataAdapter; 74import com.android.camera.data.LocalMediaObserver; 75import com.android.camera.data.MediaDetails; 76import com.android.camera.data.SimpleViewData; 77import com.android.camera.tinyplanet.TinyPlanetFragment; 78import com.android.camera.ui.ModuleSwitcher; 79import com.android.camera.ui.DetailsDialog; 80import com.android.camera.ui.FilmStripView; 81import com.android.camera.util.ApiHelper; 82import com.android.camera.util.CameraUtil; 83import com.android.camera.util.GcamHelper; 84import com.android.camera.util.PhotoSphereHelper; 85import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 86import com.android.camera2.R; 87 88import static com.android.camera.CameraManager.CameraOpenErrorCallback; 89 90public class CameraActivity extends Activity 91 implements ModuleSwitcher.ModuleSwitchListener, 92 ActionBar.OnMenuVisibilityListener { 93 94 private static final String TAG = "CAM_Activity"; 95 96 /** 97 * The visibility flags to use to switch the system in either lights-out 98 * mode (pre-K) or hideybar mode (K and up). 99 */ 100 private static final int IMMERSIVE_FLAGS = getImmersiveFlags(); 101 102 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 103 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 104 public static final String ACTION_IMAGE_CAPTURE_SECURE = 105 "android.media.action.IMAGE_CAPTURE_SECURE"; 106 public static final String ACTION_TRIM_VIDEO = 107 "com.android.camera.action.TRIM"; 108 public static final String MEDIA_ITEM_PATH = "media-item-path"; 109 110 private static final String PREF_STARTUP_MODULE_INDEX = "camera.startup_module"; 111 112 // The intent extra for camera from secure lock screen. True if the gallery 113 // should only show newly captured pictures. sSecureAlbumId does not 114 // increment. This is used when switching between camera, camcorder, and 115 // panorama. If the extra is not set, it is in the normal camera mode. 116 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 117 118 /** 119 * Request code from an activity we started that indicated that we do not 120 * want to reset the view to the preview in onResume. 121 */ 122 public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142; 123 124 private static final int HIDE_ACTION_BAR = 1; 125 private static final long SHOW_ACTION_BAR_TIMEOUT_MS = 3000; 126 127 /** Whether onResume should reset the view to the preview. */ 128 private boolean mResetToPreviewOnResume = true; 129 130 // Supported operations at FilmStripView. Different data has different 131 // set of supported operations. 132 private static final int SUPPORT_DELETE = 1 << 0; 133 private static final int SUPPORT_ROTATE = 1 << 1; 134 private static final int SUPPORT_INFO = 1 << 2; 135 private static final int SUPPORT_CROP = 1 << 3; 136 private static final int SUPPORT_SETAS = 1 << 4; 137 private static final int SUPPORT_EDIT = 1 << 5; 138 private static final int SUPPORT_TRIM = 1 << 6; 139 private static final int SUPPORT_SHARE = 1 << 7; 140 private static final int SUPPORT_SHARE_PANORAMA360 = 1 << 8; 141 private static final int SUPPORT_SHOW_ON_MAP = 1 << 9; 142 private static final int SUPPORT_ALL = 0xffffffff; 143 144 /** This data adapter is used by FilmStripView. */ 145 private LocalDataAdapter mDataAdapter; 146 /** This data adapter represents the real local camera data. */ 147 private LocalDataAdapter mWrappedDataAdapter; 148 149 private PanoramaStitchingManager mPanoramaManager; 150 private int mCurrentModuleIndex; 151 private CameraModule mCurrentModule; 152 private FrameLayout mAboveFilmstripControlLayout; 153 private View mCameraModuleRootView; 154 private FilmStripView mFilmStripView; 155 private ProgressBar mBottomProgress; 156 private View mPanoStitchingPanel; 157 private int mResultCodeForTesting; 158 private Intent mResultDataForTesting; 159 private OnScreenHint mStorageHint; 160 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 161 private boolean mAutoRotateScreen; 162 private boolean mSecureCamera; 163 // This is a hack to speed up the start of SecureCamera. 164 private static boolean sFirstStartAfterScreenOn = true; 165 private int mLastRawOrientation; 166 private MyOrientationEventListener mOrientationListener; 167 private Handler mMainHandler; 168 private PanoramaViewHelper mPanoramaViewHelper; 169 private CameraPreviewData mCameraPreviewData; 170 private ActionBar mActionBar; 171 private OnActionBarVisibilityListener mOnActionBarVisibilityListener = null; 172 private Menu mActionBarMenu; 173 private ViewGroup mUndoDeletionBar; 174 private boolean mIsUndoingDeletion = false; 175 176 private Uri[] mNfcPushUris = new Uri[1]; 177 178 private ShareActionProvider mStandardShareActionProvider; 179 private Intent mStandardShareIntent; 180 private ShareActionProvider mPanoramaShareActionProvider; 181 private Intent mPanoramaShareIntent; 182 private LocalMediaObserver mLocalImagesObserver; 183 private LocalMediaObserver mLocalVideosObserver; 184 private boolean mActivityPaused; 185 private boolean mMediaDataChangedDuringPause; 186 187 private final int DEFAULT_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 188 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION; 189 private boolean mPendingDeletion = false; 190 191 private Intent mVideoShareIntent; 192 private Intent mImageShareIntent; 193 194 private class MyOrientationEventListener 195 extends OrientationEventListener { 196 public MyOrientationEventListener(Context context) { 197 super(context); 198 } 199 200 @Override 201 public void onOrientationChanged(int orientation) { 202 // We keep the last known orientation. So if the user first orient 203 // the camera then point the camera to floor or sky, we still have 204 // the correct orientation. 205 if (orientation == ORIENTATION_UNKNOWN) { 206 return; 207 } 208 mLastRawOrientation = orientation; 209 mCurrentModule.onOrientationChanged(orientation); 210 } 211 } 212 213 private MediaSaveService mMediaSaveService; 214 private ServiceConnection mConnection = new ServiceConnection() { 215 @Override 216 public void onServiceConnected(ComponentName className, IBinder b) { 217 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 218 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 219 } 220 221 @Override 222 public void onServiceDisconnected(ComponentName className) { 223 if (mMediaSaveService != null) { 224 mMediaSaveService.setListener(null); 225 mMediaSaveService = null; 226 } 227 } 228 }; 229 230 private CameraOpenErrorCallback mCameraOpenErrorCallback = 231 new CameraOpenErrorCallback() { 232 @Override 233 public void onCameraDisabled(int cameraId) { 234 CameraUtil.showErrorAndFinish(CameraActivity.this, 235 R.string.camera_disabled); 236 } 237 238 @Override 239 public void onDeviceOpenFailure(int cameraId) { 240 CameraUtil.showErrorAndFinish(CameraActivity.this, 241 R.string.cannot_connect_camera); 242 } 243 244 @Override 245 public void onReconnectionFailure(CameraManager mgr) { 246 CameraUtil.showErrorAndFinish(CameraActivity.this, 247 R.string.cannot_connect_camera); 248 } 249 }; 250 251 // close activity when screen turns off 252 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 253 @Override 254 public void onReceive(Context context, Intent intent) { 255 finish(); 256 } 257 }; 258 259 private static BroadcastReceiver sScreenOffReceiver; 260 261 private static class ScreenOffReceiver extends BroadcastReceiver { 262 @Override 263 public void onReceive(Context context, Intent intent) { 264 sFirstStartAfterScreenOn = true; 265 } 266 } 267 268 private class MainHandler extends Handler { 269 public MainHandler(Looper looper) { 270 super(looper); 271 } 272 273 @Override 274 public void handleMessage(Message msg) { 275 if (msg.what == HIDE_ACTION_BAR) { 276 removeMessages(HIDE_ACTION_BAR); 277 CameraActivity.this.setSystemBarsVisibility(false); 278 } 279 } 280 } 281 282 public interface OnActionBarVisibilityListener { 283 public void onActionBarVisibilityChanged(boolean isVisible); 284 } 285 286 public void setOnActionBarVisibilityListener(OnActionBarVisibilityListener listener) { 287 mOnActionBarVisibilityListener = listener; 288 } 289 290 private static int getImmersiveFlags() { 291 if (ApiHelper.HAS_HIDEYBARS) { 292 return View.SYSTEM_UI_FLAG_IMMERSIVE 293 | View.SYSTEM_UI_FLAG_TRANSPARENT_STATUS 294 | View.SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION 295 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 296 | View.SYSTEM_UI_FLAG_FULLSCREEN; 297 } else { 298 // Pre-KitKat we use lights-out mode. 299 return View.SYSTEM_UI_FLAG_LOW_PROFILE; 300 } 301 } 302 303 public static boolean isFirstStartAfterScreenOn() { 304 return sFirstStartAfterScreenOn; 305 } 306 307 public static void resetFirstStartAfterScreenOn() { 308 sFirstStartAfterScreenOn = false; 309 } 310 311 private FilmStripView.Listener mFilmStripListener = 312 new FilmStripView.Listener() { 313 @Override 314 public void onDataPromoted(int dataID) { 315 removeData(dataID); 316 } 317 318 @Override 319 public void onDataDemoted(int dataID) { 320 removeData(dataID); 321 } 322 323 @Override 324 public void onDataFullScreenChange(int dataID, boolean full) { 325 boolean isCameraID = isCameraPreview(dataID); 326 if (!isCameraID) { 327 if (!full) { 328 // Always show action bar in filmstrip mode 329 CameraActivity.this.setSystemBarsVisibility(true, false); 330 } else if (mActionBar.isShowing()) { 331 // Hide action bar after time out in full screen mode 332 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, 333 SHOW_ACTION_BAR_TIMEOUT_MS); 334 } 335 } 336 } 337 338 /** 339 * Check if the local data corresponding to dataID is the camera 340 * preview. 341 * 342 * @param dataID the ID of the local data 343 * @return true if the local data is not null and it is the 344 * camera preview. 345 */ 346 private boolean isCameraPreview(int dataID) { 347 LocalData localData = mDataAdapter.getLocalData(dataID); 348 if (localData == null) { 349 Log.w(TAG, "Current data ID not found."); 350 return false; 351 } 352 return localData.getLocalDataType() == LocalData.LOCAL_CAMERA_PREVIEW; 353 } 354 355 @Override 356 public void onCurrentDataChanged(final int dataID, final boolean current) { 357 // Delay hiding action bar if there is any user interaction 358 if (mMainHandler.hasMessages(HIDE_ACTION_BAR)) { 359 mMainHandler.removeMessages(HIDE_ACTION_BAR); 360 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, 361 SHOW_ACTION_BAR_TIMEOUT_MS); 362 } 363 runOnUiThread(new Runnable() { 364 @Override 365 public void run() { 366 LocalData currentData = mDataAdapter.getLocalData(dataID); 367 if (currentData == null) { 368 Log.w(TAG, "Current data ID not found."); 369 hidePanoStitchingProgress(); 370 return; 371 } 372 boolean isCameraID = currentData.getLocalDataType() == 373 LocalData.LOCAL_CAMERA_PREVIEW; 374 if (!current) { 375 if (isCameraID) { 376 mCurrentModule.onPreviewFocusChanged(false); 377 CameraActivity.this.setSystemBarsVisibility(true); 378 } 379 hidePanoStitchingProgress(); 380 } else { 381 if (isCameraID) { 382 mCurrentModule.onPreviewFocusChanged(true); 383 // Don't show the action bar in Camera 384 // preview. 385 CameraActivity.this.setSystemBarsVisibility(false); 386 if (mPendingDeletion) { 387 performDeletion(); 388 } 389 } else { 390 updateActionBarMenu(dataID); 391 } 392 393 Uri contentUri = currentData.getContentUri(); 394 if (contentUri == null) { 395 hidePanoStitchingProgress(); 396 return; 397 } 398 int panoStitchingProgress = mPanoramaManager.getTaskProgress( 399 contentUri); 400 if (panoStitchingProgress < 0) { 401 hidePanoStitchingProgress(); 402 return; 403 } 404 showPanoStitchingProgress(); 405 updateStitchingProgress(panoStitchingProgress); 406 } 407 } 408 }); 409 } 410 411 @Override 412 public void onToggleSystemDecorsVisibility(int dataID) { 413 // If action bar is showing, hide it immediately, otherwise 414 // show action bar and hide it later 415 if (mActionBar.isShowing()) { 416 CameraActivity.this.setSystemBarsVisibility(false); 417 } else { 418 // Don't show the action bar if that is the camera preview. 419 boolean isCameraID = isCameraPreview(dataID); 420 if (!isCameraID) { 421 CameraActivity.this.setSystemBarsVisibility(true, true); 422 } 423 } 424 } 425 426 @Override 427 public void setSystemDecorsVisibility(boolean visible) { 428 CameraActivity.this.setSystemBarsVisibility(visible); 429 } 430 }; 431 432 public void gotoGallery() { 433 mFilmStripView.getController().goToNextItem(); 434 } 435 436 /** 437 * If {@param visible} is false, this hides the action bar and switches the system UI 438 * to lights-out mode. 439 */ 440 441 private void setSystemBarsVisibility(boolean visible) { 442 setSystemBarsVisibility(visible, false); 443 } 444 445 /** 446 * If {@param visible} is false, this hides the action bar and switches the 447 * system UI to lights-out mode. If {@param hideLater} is true, a delayed message 448 * will be sent after a timeout to hide the action bar. 449 */ 450 private void setSystemBarsVisibility(boolean visible, boolean hideLater) { 451 mMainHandler.removeMessages(HIDE_ACTION_BAR); 452 boolean currentlyVisible = mActionBar.isShowing(); 453 454 if (visible != currentlyVisible) { 455 int visibility = DEFAULT_SYSTEM_UI_VISIBILITY | (visible ? View.SYSTEM_UI_FLAG_VISIBLE 456 : IMMERSIVE_FLAGS); 457 mAboveFilmstripControlLayout.setSystemUiVisibility(visibility); 458 if (visible) { 459 mActionBar.show(); 460 } else { 461 mActionBar.hide(); 462 } 463 if (mOnActionBarVisibilityListener != null) { 464 mOnActionBarVisibilityListener.onActionBarVisibilityChanged(visible); 465 } 466 } 467 468 // Now delay hiding the bars 469 if (visible && hideLater) { 470 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS); 471 } 472 } 473 474 private void hidePanoStitchingProgress() { 475 mPanoStitchingPanel.setVisibility(View.GONE); 476 } 477 478 private void showPanoStitchingProgress() { 479 mPanoStitchingPanel.setVisibility(View.VISIBLE); 480 } 481 482 private void updateStitchingProgress(int progress) { 483 mBottomProgress.setProgress(progress); 484 } 485 486 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 487 private void setupNfcBeamPush() { 488 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(CameraActivity.this); 489 if (adapter == null) { 490 return; 491 } 492 493 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) { 494 // Disable beaming 495 adapter.setNdefPushMessage(null, CameraActivity.this); 496 return; 497 } 498 499 adapter.setBeamPushUris(null, CameraActivity.this); 500 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() { 501 @Override 502 public Uri[] createBeamUris(NfcEvent event) { 503 return mNfcPushUris; 504 } 505 }, CameraActivity.this); 506 } 507 508 private void setNfcBeamPushUri(Uri uri) { 509 mNfcPushUris[0] = uri; 510 } 511 512 private void setStandardShareIntent(Uri contentUri, String mimeType) { 513 mStandardShareIntent = getShareIntentFromType(mimeType); 514 if (mStandardShareIntent != null) { 515 mStandardShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); 516 mStandardShareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 517 if (mStandardShareActionProvider != null) { 518 mStandardShareActionProvider.setShareIntent(mStandardShareIntent); 519 } 520 } 521 } 522 523 /** 524 * Get the share intent according to the mimeType 525 * 526 * @param mimeType The mimeType of current data. 527 * @return the video/image's ShareIntent or null if mimeType is invalid. 528 */ 529 private Intent getShareIntentFromType(String mimeType) { 530 // Lazily create the intent object. 531 if (mimeType.startsWith("video/")) { 532 if (mVideoShareIntent == null) { 533 mVideoShareIntent = new Intent(Intent.ACTION_SEND); 534 mVideoShareIntent.setType("video/*"); 535 } 536 return mVideoShareIntent; 537 } else if (mimeType.startsWith("image/")) { 538 if (mImageShareIntent == null) { 539 mImageShareIntent = new Intent(Intent.ACTION_SEND); 540 mImageShareIntent.setType("image/*"); 541 } 542 return mImageShareIntent; 543 } 544 Log.w(TAG, "unsupported mimeType " + mimeType); 545 return null; 546 } 547 548 private void setPanoramaShareIntent(Uri contentUri) { 549 if (mPanoramaShareIntent == null) { 550 mPanoramaShareIntent = new Intent(Intent.ACTION_SEND); 551 } 552 mPanoramaShareIntent.setType("application/vnd.google.panorama360+jpg"); 553 mPanoramaShareIntent.putExtra(Intent.EXTRA_STREAM, contentUri); 554 if (mPanoramaShareActionProvider != null) { 555 mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent); 556 } 557 } 558 559 @Override 560 public void onMenuVisibilityChanged(boolean isVisible) { 561 // If menu is showing, we need to make sure action bar does not go away. 562 mMainHandler.removeMessages(HIDE_ACTION_BAR); 563 if (!isVisible) { 564 mMainHandler.sendEmptyMessageDelayed(HIDE_ACTION_BAR, SHOW_ACTION_BAR_TIMEOUT_MS); 565 } 566 } 567 568 /** 569 * According to the data type, make the menu items for supported operations 570 * visible. 571 * 572 * @param dataID the data ID of the current item. 573 */ 574 private void updateActionBarMenu(int dataID) { 575 LocalData currentData = mDataAdapter.getLocalData(dataID); 576 int type = currentData.getLocalDataType(); 577 578 if (mActionBarMenu == null) { 579 return; 580 } 581 582 int supported = 0; 583 switch (type) { 584 case LocalData.LOCAL_IMAGE: 585 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 586 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 587 | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP; 588 break; 589 case LocalData.LOCAL_VIDEO: 590 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM 591 | SUPPORT_SHARE; 592 break; 593 case LocalData.LOCAL_PHOTO_SPHERE: 594 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 595 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 596 | SUPPORT_SHARE | SUPPORT_SHOW_ON_MAP; 597 break; 598 case LocalData.LOCAL_360_PHOTO_SPHERE: 599 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 600 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 601 | SUPPORT_SHARE | SUPPORT_SHARE_PANORAMA360 602 | SUPPORT_SHOW_ON_MAP; 603 break; 604 default: 605 break; 606 } 607 608 setMenuItemVisible(mActionBarMenu, R.id.action_delete, 609 (supported & SUPPORT_DELETE) != 0); 610 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw, 611 (supported & SUPPORT_ROTATE) != 0); 612 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw, 613 (supported & SUPPORT_ROTATE) != 0); 614 setMenuItemVisible(mActionBarMenu, R.id.action_details, 615 (supported & SUPPORT_INFO) != 0); 616 setMenuItemVisible(mActionBarMenu, R.id.action_crop, 617 (supported & SUPPORT_CROP) != 0); 618 setMenuItemVisible(mActionBarMenu, R.id.action_setas, 619 (supported & SUPPORT_SETAS) != 0); 620 setMenuItemVisible(mActionBarMenu, R.id.action_edit, 621 (supported & SUPPORT_EDIT) != 0); 622 setMenuItemVisible(mActionBarMenu, R.id.action_trim, 623 (supported & SUPPORT_TRIM) != 0); 624 625 boolean standardShare = (supported & SUPPORT_SHARE) != 0; 626 boolean panoramaShare = (supported & SUPPORT_SHARE_PANORAMA360) != 0; 627 setMenuItemVisible(mActionBarMenu, R.id.action_share, standardShare); 628 setMenuItemVisible(mActionBarMenu, R.id.action_share_panorama, panoramaShare); 629 630 if (panoramaShare) { 631 // For 360 PhotoSphere, relegate standard share to the overflow menu 632 MenuItem item = mActionBarMenu.findItem(R.id.action_share); 633 if (item != null) { 634 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 635 item.setTitle(getResources().getString(R.string.share_as_photo)); 636 } 637 // And, promote "share as panorama" to action bar 638 item = mActionBarMenu.findItem(R.id.action_share_panorama); 639 if (item != null) { 640 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 641 } 642 setPanoramaShareIntent(currentData.getContentUri()); 643 } 644 if (standardShare) { 645 if (!panoramaShare) { 646 MenuItem item = mActionBarMenu.findItem(R.id.action_share); 647 if (item != null) { 648 item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 649 item.setTitle(getResources().getString(R.string.share)); 650 } 651 } 652 setStandardShareIntent(currentData.getContentUri(), currentData.getMimeType()); 653 setNfcBeamPushUri(currentData.getContentUri()); 654 } 655 656 boolean itemHasLocation = currentData.getLatLong() != null; 657 setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map, 658 itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0); 659 } 660 661 private void setMenuItemVisible(Menu menu, int itemId, boolean visible) { 662 MenuItem item = menu.findItem(itemId); 663 if (item != null) 664 item.setVisible(visible); 665 } 666 667 private ImageTaskManager.TaskListener mStitchingListener = 668 new ImageTaskManager.TaskListener() { 669 @Override 670 public void onTaskQueued(String filePath, final Uri imageUri) { 671 mMainHandler.post(new Runnable() { 672 @Override 673 public void run() { 674 notifyNewMedia(imageUri); 675 } 676 }); 677 } 678 679 @Override 680 public void onTaskDone(String filePath, final Uri imageUri) { 681 Log.v(TAG, "onTaskDone:" + filePath); 682 mMainHandler.post(new Runnable() { 683 @Override 684 public void run() { 685 int doneID = mDataAdapter.findDataByContentUri(imageUri); 686 int currentDataId = mFilmStripView.getCurrentId(); 687 688 if (currentDataId == doneID) { 689 hidePanoStitchingProgress(); 690 updateStitchingProgress(0); 691 } 692 693 mDataAdapter.refresh(getContentResolver(), imageUri); 694 } 695 }); 696 } 697 698 @Override 699 public void onTaskProgress( 700 String filePath, final Uri imageUri, final int progress) { 701 mMainHandler.post(new Runnable() { 702 @Override 703 public void run() { 704 int currentDataId = mFilmStripView.getCurrentId(); 705 if (currentDataId == -1) { 706 return; 707 } 708 if (imageUri.equals( 709 mDataAdapter.getLocalData(currentDataId).getContentUri())) { 710 updateStitchingProgress(progress); 711 } 712 } 713 }); 714 } 715 }; 716 717 public MediaSaveService getMediaSaveService() { 718 return mMediaSaveService; 719 } 720 721 public void notifyNewMedia(Uri uri) { 722 ContentResolver cr = getContentResolver(); 723 String mimeType = cr.getType(uri); 724 if (mimeType.startsWith("video/")) { 725 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 726 mDataAdapter.addNewVideo(cr, uri); 727 } else if (mimeType.startsWith("image/")) { 728 CameraUtil.broadcastNewPicture(this, uri); 729 mDataAdapter.addNewPhoto(cr, uri); 730 } else if (mimeType.startsWith("application/stitching-preview")) { 731 mDataAdapter.addNewPhoto(cr, uri); 732 } else { 733 android.util.Log.w(TAG, "Unknown new media with MIME type:" 734 + mimeType + ", uri:" + uri); 735 } 736 } 737 738 private void removeData(int dataID) { 739 mDataAdapter.removeData(CameraActivity.this, dataID); 740 if (mDataAdapter.getTotalNumber() > 1) { 741 showUndoDeletionBar(); 742 } else { 743 // If camera preview is the only view left in filmstrip, 744 // no need to show undo bar. 745 performDeletion(); 746 } 747 } 748 749 private void bindMediaSaveService() { 750 Intent intent = new Intent(this, MediaSaveService.class); 751 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 752 } 753 754 private void unbindMediaSaveService() { 755 if (mConnection != null) { 756 unbindService(mConnection); 757 } 758 } 759 760 @Override 761 public boolean onCreateOptionsMenu(Menu menu) { 762 // Inflate the menu items for use in the action bar 763 MenuInflater inflater = getMenuInflater(); 764 inflater.inflate(R.menu.operations, menu); 765 mActionBarMenu = menu; 766 767 // Configure the standard share action provider 768 MenuItem item = menu.findItem(R.id.action_share); 769 mStandardShareActionProvider = (ShareActionProvider) item.getActionProvider(); 770 mStandardShareActionProvider.setShareHistoryFileName("standard_share_history.xml"); 771 if (mStandardShareIntent != null) { 772 mStandardShareActionProvider.setShareIntent(mStandardShareIntent); 773 } 774 775 // Configure the panorama share action provider 776 item = menu.findItem(R.id.action_share_panorama); 777 mPanoramaShareActionProvider = (ShareActionProvider) item.getActionProvider(); 778 mPanoramaShareActionProvider.setShareHistoryFileName("panorama_share_history.xml"); 779 if (mPanoramaShareIntent != null) { 780 mPanoramaShareActionProvider.setShareIntent(mPanoramaShareIntent); 781 } 782 783 return super.onCreateOptionsMenu(menu); 784 } 785 786 @Override 787 public boolean onOptionsItemSelected(MenuItem item) { 788 int currentDataId = mFilmStripView.getCurrentId(); 789 if (currentDataId < 0) { 790 return false; 791 } 792 final LocalData localData = mDataAdapter.getLocalData(currentDataId); 793 794 // Handle presses on the action bar items 795 switch (item.getItemId()) { 796 case android.R.id.home: 797 // ActionBar's Up/Home button was clicked 798 if (!CameraUtil.launchGallery(CameraActivity.this)) { 799 mFilmStripView.getController().goToFirstItem(); 800 } 801 return true; 802 case R.id.action_delete: 803 removeData(currentDataId); 804 return true; 805 case R.id.action_edit: 806 launchEditor(localData); 807 return true; 808 case R.id.action_trim: { 809 // This is going to be handled by the Gallery app. 810 Intent intent = new Intent(ACTION_TRIM_VIDEO); 811 LocalData currentData = mDataAdapter.getLocalData( 812 mFilmStripView.getCurrentId()); 813 intent.setData(currentData.getContentUri()); 814 // We need the file path to wrap this into a RandomAccessFile. 815 intent.putExtra(MEDIA_ITEM_PATH, currentData.getPath()); 816 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW); 817 return true; 818 } 819 case R.id.action_rotate_ccw: 820 localData.rotate90Degrees(this, mDataAdapter, currentDataId, false); 821 return true; 822 case R.id.action_rotate_cw: 823 localData.rotate90Degrees(this, mDataAdapter, currentDataId, true); 824 return true; 825 case R.id.action_crop: { 826 Intent intent = new Intent(CropActivity.CROP_ACTION); 827 intent.setClass(this, CropActivity.class); 828 intent.setDataAndType(localData.getContentUri(), localData.getMimeType()) 829 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 830 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW); 831 return true; 832 } 833 case R.id.action_setas: { 834 Intent intent = new Intent(Intent.ACTION_ATTACH_DATA) 835 .setDataAndType(localData.getContentUri(), 836 localData.getMimeType()) 837 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 838 intent.putExtra("mimeType", intent.getType()); 839 startActivityForResult(Intent.createChooser( 840 intent, getString(R.string.set_as)), REQ_CODE_DONT_SWITCH_TO_PREVIEW); 841 return true; 842 } 843 case R.id.action_details: 844 (new AsyncTask<Void, Void, MediaDetails>() { 845 @Override 846 protected MediaDetails doInBackground(Void... params) { 847 return localData.getMediaDetails(CameraActivity.this); 848 } 849 850 @Override 851 protected void onPostExecute(MediaDetails mediaDetails) { 852 DetailsDialog.create(CameraActivity.this, mediaDetails).show(); 853 } 854 }).execute(); 855 return true; 856 case R.id.action_show_on_map: 857 double[] latLong = localData.getLatLong(); 858 if (latLong != null) { 859 CameraUtil.showOnMap(this, latLong); 860 } 861 return true; 862 default: 863 return super.onOptionsItemSelected(item); 864 } 865 } 866 867 private boolean isCaptureIntent() { 868 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction()) 869 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 870 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 871 return true; 872 } else { 873 return false; 874 } 875 } 876 877 @Override 878 public void onCreate(Bundle state) { 879 super.onCreate(state); 880 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 881 setContentView(R.layout.camera_filmstrip); 882 mActionBar = getActionBar(); 883 mActionBar.addOnMenuVisibilityListener(this); 884 885 if (ApiHelper.HAS_ROTATION_ANIMATION) { 886 setRotationAnimation(); 887 } 888 889 mMainHandler = new MainHandler(getMainLooper()); 890 // Check if this is in the secure camera mode. 891 Intent intent = getIntent(); 892 String action = intent.getAction(); 893 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 894 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 895 mSecureCamera = true; 896 } else { 897 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 898 } 899 900 if (mSecureCamera) { 901 // Change the window flags so that secure camera can show when locked 902 Window win = getWindow(); 903 WindowManager.LayoutParams params = win.getAttributes(); 904 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 905 win.setAttributes(params); 906 907 // Filter for screen off so that we can finish secure camera activity 908 // when screen is off. 909 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 910 registerReceiver(mScreenOffReceiver, filter); 911 // TODO: This static screen off event receiver is a workaround to the 912 // double onResume() invocation (onResume->onPause->onResume). We should 913 // find a better solution to this. 914 if (sScreenOffReceiver == null) { 915 sScreenOffReceiver = new ScreenOffReceiver(); 916 registerReceiver(sScreenOffReceiver, filter); 917 } 918 } 919 mAboveFilmstripControlLayout = 920 (FrameLayout) findViewById(R.id.camera_above_filmstrip_layout); 921 mAboveFilmstripControlLayout.setFitsSystemWindows(true); 922 // Hide action bar first since we are in full screen mode first, and 923 // switch the system UI to lights-out mode. 924 this.setSystemBarsVisibility(false); 925 mPanoramaManager = AppManagerFactory.getInstance(this) 926 .getPanoramaStitchingManager(); 927 mPanoramaManager.addTaskListener(mStitchingListener); 928 LayoutInflater inflater = getLayoutInflater(); 929 View rootLayout = inflater.inflate(R.layout.camera, null, false); 930 mCameraModuleRootView = rootLayout.findViewById(R.id.camera_app_root); 931 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel); 932 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar); 933 mCameraPreviewData = new CameraPreviewData(rootLayout, 934 FilmStripView.ImageData.SIZE_FULL, 935 FilmStripView.ImageData.SIZE_FULL); 936 // Put a CameraPreviewData at the first position. 937 mWrappedDataAdapter = new FixedFirstDataAdapter( 938 new CameraDataAdapter(new ColorDrawable( 939 getResources().getColor(R.color.photo_placeholder))), 940 mCameraPreviewData); 941 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); 942 mFilmStripView.setViewGap( 943 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 944 mPanoramaViewHelper = new PanoramaViewHelper(this); 945 mPanoramaViewHelper.onCreate(); 946 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper); 947 // Set up the camera preview first so the preview shows up ASAP. 948 mFilmStripView.setListener(mFilmStripListener); 949 950 int moduleIndex = -1; 951 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 952 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 953 moduleIndex = ModuleSwitcher.VIDEO_MODULE_INDEX; 954 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction()) 955 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent() 956 .getAction()) 957 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 958 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 959 moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX; 960 } else { 961 // If the activity has not been started using an explicit intent, 962 // read the module index from the last time the user changed modes 963 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 964 moduleIndex = prefs.getInt(PREF_STARTUP_MODULE_INDEX, -1); 965 if ((moduleIndex == ModuleSwitcher.GCAM_MODULE_INDEX && 966 !GcamHelper.hasGcamCapture(this)) || moduleIndex < 0) { 967 moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX; 968 } 969 } 970 971 mOrientationListener = new MyOrientationEventListener(this); 972 setModuleFromIndex(moduleIndex); 973 mCurrentModule.init(this, mCameraModuleRootView); 974 975 if (!mSecureCamera) { 976 mDataAdapter = mWrappedDataAdapter; 977 mFilmStripView.setDataAdapter(mDataAdapter); 978 if (!isCaptureIntent()) { 979 mDataAdapter.requestLoad(getContentResolver()); 980 } 981 } else { 982 // Put a lock placeholder as the last image by setting its date to 983 // 0. 984 ImageView v = (ImageView) getLayoutInflater().inflate( 985 R.layout.secure_album_placeholder, null); 986 v.setOnClickListener(new View.OnClickListener() { 987 @Override 988 public void onClick(View view) { 989 CameraUtil.launchGallery(CameraActivity.this); 990 finish(); 991 } 992 }); 993 mDataAdapter = new FixedLastDataAdapter( 994 mWrappedDataAdapter, 995 new SimpleViewData( 996 v, 997 v.getDrawable().getIntrinsicWidth(), 998 v.getDrawable().getIntrinsicHeight(), 999 0, 0)); 1000 // Flush out all the original data. 1001 mDataAdapter.flush(); 1002 mFilmStripView.setDataAdapter(mDataAdapter); 1003 } 1004 1005 setupNfcBeamPush(); 1006 1007 mLocalImagesObserver = new LocalMediaObserver(mMainHandler, this); 1008 mLocalVideosObserver = new LocalMediaObserver(mMainHandler, this); 1009 1010 getContentResolver().registerContentObserver( 1011 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, 1012 mLocalImagesObserver); 1013 getContentResolver().registerContentObserver( 1014 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, 1015 mLocalVideosObserver); 1016 } 1017 1018 private void setRotationAnimation() { 1019 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 1020 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 1021 Window win = getWindow(); 1022 WindowManager.LayoutParams winParams = win.getAttributes(); 1023 winParams.rotationAnimation = rotationAnimation; 1024 win.setAttributes(winParams); 1025 } 1026 1027 @Override 1028 public void onUserInteraction() { 1029 super.onUserInteraction(); 1030 mCurrentModule.onUserInteraction(); 1031 } 1032 1033 @Override 1034 public boolean dispatchTouchEvent(MotionEvent ev) { 1035 boolean result = super.dispatchTouchEvent(ev); 1036 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 1037 // Real deletion is postponed until the next user interaction after 1038 // the gesture that triggers deletion. Until real deletion is performed, 1039 // users can click the undo button to bring back the image that they 1040 // chose to delete. 1041 if (mPendingDeletion && !mIsUndoingDeletion) { 1042 performDeletion(); 1043 } 1044 } 1045 return result; 1046 } 1047 1048 @Override 1049 public void onPause() { 1050 mOrientationListener.disable(); 1051 mCurrentModule.onPauseBeforeSuper(); 1052 super.onPause(); 1053 mCurrentModule.onPauseAfterSuper(); 1054 mActivityPaused = true; 1055 } 1056 1057 @Override 1058 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1059 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) { 1060 mResetToPreviewOnResume = false; 1061 } else { 1062 super.onActivityResult(requestCode, resultCode, data); 1063 } 1064 } 1065 1066 @Override 1067 public void onResume() { 1068 // TODO: Handle this in OrientationManager. 1069 // Auto-rotate off 1070 if (Settings.System.getInt(getContentResolver(), 1071 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) { 1072 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 1073 mAutoRotateScreen = false; 1074 } else { 1075 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 1076 mAutoRotateScreen = true; 1077 } 1078 mOrientationListener.enable(); 1079 mCurrentModule.onResumeBeforeSuper(); 1080 super.onResume(); 1081 mCurrentModule.onResumeAfterSuper(); 1082 1083 setSwipingEnabled(true); 1084 1085 if (mResetToPreviewOnResume) { 1086 // Go to the preview on resume. 1087 mFilmStripView.getController().goToFirstItem(); 1088 } 1089 // Default is showing the preview, unless disabled by explicitly 1090 // starting an activity we want to return from to the filmstrip rather 1091 // than the preview. 1092 mResetToPreviewOnResume = true; 1093 1094 mActivityPaused = false; 1095 if (mMediaDataChangedDuringPause) { 1096 mDataAdapter.requestLoad(getContentResolver()); 1097 mMediaDataChangedDuringPause = false; 1098 } 1099 } 1100 1101 @Override 1102 public void onStart() { 1103 super.onStart(); 1104 bindMediaSaveService(); 1105 mPanoramaViewHelper.onStart(); 1106 } 1107 1108 @Override 1109 protected void onStop() { 1110 super.onStop(); 1111 mPanoramaViewHelper.onStop(); 1112 unbindMediaSaveService(); 1113 } 1114 1115 @Override 1116 public void onDestroy() { 1117 if (mSecureCamera) { 1118 unregisterReceiver(mScreenOffReceiver); 1119 } 1120 getContentResolver().unregisterContentObserver(mLocalImagesObserver); 1121 getContentResolver().unregisterContentObserver(mLocalVideosObserver); 1122 1123 super.onDestroy(); 1124 } 1125 1126 @Override 1127 public void onConfigurationChanged(Configuration config) { 1128 super.onConfigurationChanged(config); 1129 mCurrentModule.onConfigurationChanged(config); 1130 } 1131 1132 @Override 1133 public boolean onKeyDown(int keyCode, KeyEvent event) { 1134 if (mCurrentModule.onKeyDown(keyCode, event)) { 1135 return true; 1136 } 1137 // Prevent software keyboard or voice search from showing up. 1138 if (keyCode == KeyEvent.KEYCODE_SEARCH 1139 || keyCode == KeyEvent.KEYCODE_MENU) { 1140 if (event.isLongPress()) { 1141 return true; 1142 } 1143 } 1144 1145 return super.onKeyDown(keyCode, event); 1146 } 1147 1148 @Override 1149 public boolean onKeyUp(int keyCode, KeyEvent event) { 1150 if (mCurrentModule.onKeyUp(keyCode, event)) { 1151 return true; 1152 } 1153 return super.onKeyUp(keyCode, event); 1154 } 1155 1156 @Override 1157 public void onBackPressed() { 1158 if (!mFilmStripView.inCameraFullscreen()) { 1159 mFilmStripView.getController().goToFirstItem(); 1160 } else if (!mCurrentModule.onBackPressed()) { 1161 super.onBackPressed(); 1162 } 1163 } 1164 1165 public boolean isAutoRotateScreen() { 1166 return mAutoRotateScreen; 1167 } 1168 1169 protected void updateStorageSpace() { 1170 mStorageSpace = Storage.getAvailableSpace(); 1171 } 1172 1173 protected long getStorageSpace() { 1174 return mStorageSpace; 1175 } 1176 1177 protected void updateStorageSpaceAndHint() { 1178 updateStorageSpace(); 1179 updateStorageHint(mStorageSpace); 1180 } 1181 1182 protected void updateStorageHint() { 1183 updateStorageHint(mStorageSpace); 1184 } 1185 1186 protected boolean updateStorageHintOnResume() { 1187 return true; 1188 } 1189 1190 protected void updateStorageHint(long storageSpace) { 1191 String message = null; 1192 if (storageSpace == Storage.UNAVAILABLE) { 1193 message = getString(R.string.no_storage); 1194 } else if (storageSpace == Storage.PREPARING) { 1195 message = getString(R.string.preparing_sd); 1196 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 1197 message = getString(R.string.access_sd_fail); 1198 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 1199 message = getString(R.string.spaceIsLow_content); 1200 } 1201 1202 if (message != null) { 1203 if (mStorageHint == null) { 1204 mStorageHint = OnScreenHint.makeText(this, message); 1205 } else { 1206 mStorageHint.setText(message); 1207 } 1208 mStorageHint.show(); 1209 } else if (mStorageHint != null) { 1210 mStorageHint.cancel(); 1211 mStorageHint = null; 1212 } 1213 } 1214 1215 protected void setResultEx(int resultCode) { 1216 mResultCodeForTesting = resultCode; 1217 setResult(resultCode); 1218 } 1219 1220 protected void setResultEx(int resultCode, Intent data) { 1221 mResultCodeForTesting = resultCode; 1222 mResultDataForTesting = data; 1223 setResult(resultCode, data); 1224 } 1225 1226 public int getResultCode() { 1227 return mResultCodeForTesting; 1228 } 1229 1230 public Intent getResultData() { 1231 return mResultDataForTesting; 1232 } 1233 1234 public boolean isSecureCamera() { 1235 return mSecureCamera; 1236 } 1237 1238 @Override 1239 public void onModuleSelected(int moduleIndex) { 1240 if (mCurrentModuleIndex == moduleIndex) { 1241 return; 1242 } 1243 1244 CameraHolder.instance().keep(); 1245 closeModule(mCurrentModule); 1246 setModuleFromIndex(moduleIndex); 1247 1248 openModule(mCurrentModule); 1249 mCurrentModule.onOrientationChanged(mLastRawOrientation); 1250 if (mMediaSaveService != null) { 1251 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 1252 } 1253 1254 // Store the module index so we can use it the next time the Camera 1255 // starts up. 1256 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); 1257 prefs.edit().putInt(PREF_STARTUP_MODULE_INDEX, moduleIndex).apply(); 1258 } 1259 1260 /** 1261 * Sets the mCurrentModuleIndex, creates a new module instance for the given 1262 * index an sets it as mCurrentModule. 1263 */ 1264 private void setModuleFromIndex(int moduleIndex) { 1265 mCurrentModuleIndex = moduleIndex; 1266 switch (moduleIndex) { 1267 case ModuleSwitcher.VIDEO_MODULE_INDEX: 1268 mCurrentModule = new VideoModule(); 1269 break; 1270 1271 case ModuleSwitcher.PHOTO_MODULE_INDEX: 1272 mCurrentModule = new PhotoModule(); 1273 break; 1274 1275 case ModuleSwitcher.WIDE_ANGLE_PANO_MODULE_INDEX: 1276 mCurrentModule = new WideAnglePanoramaModule(); 1277 break; 1278 1279 case ModuleSwitcher.LIGHTCYCLE_MODULE_INDEX: 1280 mCurrentModule = PhotoSphereHelper.createPanoramaModule(); 1281 break; 1282 case ModuleSwitcher.GCAM_MODULE_INDEX: 1283 // Force immediate release of Camera instance 1284 CameraHolder.instance().strongRelease(); 1285 mCurrentModule = GcamHelper.createGcamModule(); 1286 break; 1287 default: 1288 // Fall back to photo mode. 1289 mCurrentModule = new PhotoModule(); 1290 mCurrentModuleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX; 1291 break; 1292 } 1293 } 1294 1295 /** 1296 * Launches an ACTION_EDIT intent for the given local data item. 1297 */ 1298 public void launchEditor(LocalData data) { 1299 Intent intent = new Intent(Intent.ACTION_EDIT) 1300 .setDataAndType(data.getContentUri(), data.getMimeType()) 1301 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1302 startActivityForResult(Intent.createChooser(intent, null), 1303 REQ_CODE_DONT_SWITCH_TO_PREVIEW); 1304 } 1305 1306 /** 1307 * Launch the tiny planet editor. 1308 * 1309 * @param data the data must be a 360 degree stereographically mapped 1310 * panoramic image. It will not be modified, instead a new item 1311 * with the result will be added to the filmstrip. 1312 */ 1313 public void launchTinyPlanetEditor(LocalData data) { 1314 TinyPlanetFragment fragment = new TinyPlanetFragment(); 1315 Bundle bundle = new Bundle(); 1316 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString()); 1317 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle()); 1318 fragment.setArguments(bundle); 1319 fragment.show(getFragmentManager(), "tiny_planet"); 1320 } 1321 1322 private void openModule(CameraModule module) { 1323 module.init(this, mCameraModuleRootView); 1324 module.onResumeBeforeSuper(); 1325 module.onResumeAfterSuper(); 1326 } 1327 1328 private void closeModule(CameraModule module) { 1329 module.onPauseBeforeSuper(); 1330 module.onPauseAfterSuper(); 1331 ((ViewGroup) mCameraModuleRootView).removeAllViews(); 1332 } 1333 1334 private void performDeletion() { 1335 if (!mPendingDeletion) { 1336 return; 1337 } 1338 hideUndoDeletionBar(false); 1339 mDataAdapter.executeDeletion(CameraActivity.this); 1340 } 1341 1342 public void showUndoDeletionBar() { 1343 if (mPendingDeletion) { 1344 performDeletion(); 1345 } 1346 Log.v(TAG, "showing undo bar"); 1347 mPendingDeletion = true; 1348 if (mUndoDeletionBar == null) { 1349 ViewGroup v = (ViewGroup) getLayoutInflater().inflate( 1350 R.layout.undo_bar, mAboveFilmstripControlLayout, true); 1351 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 1352 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 1353 button.setOnClickListener(new View.OnClickListener() { 1354 @Override 1355 public void onClick(View view) { 1356 mDataAdapter.undoDataRemoval(); 1357 hideUndoDeletionBar(true); 1358 } 1359 }); 1360 // Setting undo bar clickable to avoid touch events going through 1361 // the bar to the buttons (eg. edit button, etc) underneath the bar. 1362 mUndoDeletionBar.setClickable(true); 1363 // When there is user interaction going on with the undo button, we 1364 // do not want to hide the undo bar. 1365 button.setOnTouchListener(new View.OnTouchListener() { 1366 @Override 1367 public boolean onTouch(View v, MotionEvent event) { 1368 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1369 mIsUndoingDeletion = true; 1370 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { 1371 mIsUndoingDeletion =false; 1372 } 1373 return false; 1374 } 1375 }); 1376 } 1377 mUndoDeletionBar.setAlpha(0f); 1378 mUndoDeletionBar.setVisibility(View.VISIBLE); 1379 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start(); 1380 } 1381 1382 private void hideUndoDeletionBar(boolean withAnimation) { 1383 Log.v(TAG, "Hiding undo deletion bar"); 1384 mPendingDeletion = false; 1385 if (mUndoDeletionBar != null) { 1386 if (withAnimation) { 1387 mUndoDeletionBar.animate() 1388 .setDuration(200) 1389 .alpha(0f) 1390 .setListener(new Animator.AnimatorListener() { 1391 @Override 1392 public void onAnimationStart(Animator animation) { 1393 // Do nothing. 1394 } 1395 1396 @Override 1397 public void onAnimationEnd(Animator animation) { 1398 mUndoDeletionBar.setVisibility(View.GONE); 1399 } 1400 1401 @Override 1402 public void onAnimationCancel(Animator animation) { 1403 // Do nothing. 1404 } 1405 1406 @Override 1407 public void onAnimationRepeat(Animator animation) { 1408 // Do nothing. 1409 } 1410 }) 1411 .start(); 1412 } else { 1413 mUndoDeletionBar.setVisibility(View.GONE); 1414 } 1415 } 1416 } 1417 1418 @Override 1419 public void onShowSwitcherPopup() { 1420 } 1421 1422 /** 1423 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in 1424 * capture intent. 1425 * 1426 * @param enable {@code true} to enable swipe. 1427 */ 1428 public void setSwipingEnabled(boolean enable) { 1429 if (isCaptureIntent()) { 1430 mCameraPreviewData.lockPreview(true); 1431 } else { 1432 mCameraPreviewData.lockPreview(!enable); 1433 } 1434 } 1435 1436 // Accessor methods for getting latency times used in performance testing 1437 public long getAutoFocusTime() { 1438 return (mCurrentModule instanceof PhotoModule) ? 1439 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 1440 } 1441 1442 public long getShutterLag() { 1443 return (mCurrentModule instanceof PhotoModule) ? 1444 ((PhotoModule) mCurrentModule).mShutterLag : -1; 1445 } 1446 1447 public long getShutterToPictureDisplayedTime() { 1448 return (mCurrentModule instanceof PhotoModule) ? 1449 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 1450 } 1451 1452 public long getPictureDisplayedToJpegCallbackTime() { 1453 return (mCurrentModule instanceof PhotoModule) ? 1454 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 1455 } 1456 1457 public long getJpegCallbackFinishTime() { 1458 return (mCurrentModule instanceof PhotoModule) ? 1459 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 1460 } 1461 1462 public long getCaptureStartTime() { 1463 return (mCurrentModule instanceof PhotoModule) ? 1464 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 1465 } 1466 1467 public boolean isRecording() { 1468 return (mCurrentModule instanceof VideoModule) ? 1469 ((VideoModule) mCurrentModule).isRecording() : false; 1470 } 1471 1472 public CameraOpenErrorCallback getCameraOpenErrorCallback() { 1473 return mCameraOpenErrorCallback; 1474 } 1475 1476 /** 1477 * When the activity is paused and MediaObserver get onChange() call, then 1478 * we would like to set a dirty bit to reload the data at onResume(). 1479 */ 1480 public void setDirtyWhenPaused() { 1481 if (mActivityPaused && !mMediaDataChangedDuringPause) { 1482 mMediaDataChangedDuringPause = true; 1483 } 1484 } 1485} 1486