CameraActivity.java revision adde93f26f39cd856fdf749c83d29f9022a310e1
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.app.AlertDialog; 24import android.app.Dialog; 25import android.content.ActivityNotFoundException; 26import android.content.BroadcastReceiver; 27import android.content.ContentResolver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.content.pm.ActivityInfo; 32import android.content.pm.PackageManager; 33import android.content.res.Configuration; 34import android.graphics.Bitmap; 35import android.graphics.BitmapFactory; 36import android.graphics.Matrix; 37import android.graphics.Point; 38import android.graphics.SurfaceTexture; 39import android.graphics.drawable.ColorDrawable; 40import android.graphics.drawable.Drawable; 41import android.net.Uri; 42import android.nfc.NfcAdapter; 43import android.nfc.NfcAdapter.CreateBeamUrisCallback; 44import android.nfc.NfcEvent; 45import android.os.AsyncTask; 46import android.os.Build; 47import android.os.Bundle; 48import android.os.Handler; 49import android.os.HandlerThread; 50import android.os.Looper; 51import android.os.Message; 52import android.provider.MediaStore; 53import android.provider.Settings; 54import android.util.CameraPerformanceTracker; 55import android.view.ContextMenu; 56import android.view.ContextMenu.ContextMenuInfo; 57import android.view.KeyEvent; 58import android.view.Menu; 59import android.view.MenuInflater; 60import android.view.MenuItem; 61import android.view.MotionEvent; 62import android.view.View; 63import android.view.ViewGroup; 64import android.view.Window; 65import android.view.WindowManager; 66import android.widget.FrameLayout; 67import android.widget.ImageView; 68import android.widget.ShareActionProvider; 69 70import com.android.camera.app.AppController; 71import com.android.camera.app.CameraAppUI; 72import com.android.camera.app.CameraController; 73import com.android.camera.app.CameraManager; 74import com.android.camera.app.CameraManagerFactory; 75import com.android.camera.app.CameraProvider; 76import com.android.camera.app.CameraServices; 77import com.android.camera.app.LocationManager; 78import com.android.camera.app.ModuleManagerImpl; 79import com.android.camera.app.OrientationManager; 80import com.android.camera.app.OrientationManagerImpl; 81import com.android.camera.data.CameraDataAdapter; 82import com.android.camera.data.FixedLastDataAdapter; 83import com.android.camera.data.LocalData; 84import com.android.camera.data.LocalDataAdapter; 85import com.android.camera.data.LocalDataUtil; 86import com.android.camera.data.LocalMediaData; 87import com.android.camera.data.LocalMediaObserver; 88import com.android.camera.data.LocalSessionData; 89import com.android.camera.data.MediaDetails; 90import com.android.camera.data.PanoramaMetadataLoader; 91import com.android.camera.data.RgbzMetadataLoader; 92import com.android.camera.data.SimpleViewData; 93import com.android.camera.debug.Log; 94import com.android.camera.filmstrip.FilmstripContentPanel; 95import com.android.camera.filmstrip.FilmstripController; 96import com.android.camera.hardware.HardwareSpec; 97import com.android.camera.hardware.HardwareSpecImpl; 98import com.android.camera.module.ModuleController; 99import com.android.camera.module.ModulesInfo; 100import com.android.camera.session.CaptureSession; 101import com.android.camera.session.CaptureSessionManager; 102import com.android.camera.session.CaptureSessionManager.SessionListener; 103import com.android.camera.settings.CameraSettingsActivity; 104import com.android.camera.settings.SettingsManager; 105import com.android.camera.settings.SettingsManager.SettingsCapabilities; 106import com.android.camera.settings.SettingsUtil; 107import com.android.camera.tinyplanet.TinyPlanetFragment; 108import com.android.camera.ui.AbstractTutorialOverlay; 109import com.android.camera.ui.DetailsDialog; 110import com.android.camera.ui.MainActivityLayout; 111import com.android.camera.ui.ModeListView; 112import com.android.camera.ui.ModeListView.ModeListVisibilityChangedListener; 113import com.android.camera.ui.PreviewStatusListener; 114import com.android.camera.util.ApiHelper; 115import com.android.camera.util.Callback; 116import com.android.camera.util.CameraUtil; 117import com.android.camera.util.FeedbackHelper; 118import com.android.camera.util.GalleryHelper; 119import com.android.camera.util.GcamHelper; 120import com.android.camera.util.IntentHelper; 121import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 122import com.android.camera.util.ReleaseDialogHelper; 123import com.android.camera.util.UsageStatistics; 124import com.android.camera.widget.FilmstripView; 125import com.android.camera.widget.Preloader; 126import com.android.camera2.R; 127import com.google.common.logging.eventprotos; 128import com.google.common.logging.eventprotos.CameraEvent.InteractionCause; 129import com.google.common.logging.eventprotos.NavigationChange; 130 131import java.io.File; 132import java.io.FileInputStream; 133import java.io.FileNotFoundException; 134import java.lang.ref.WeakReference; 135import java.util.ArrayList; 136import java.util.List; 137 138public class CameraActivity extends Activity 139 implements AppController, CameraManager.CameraOpenCallback, 140 ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener, 141 OrientationManager.OnOrientationChangeListener { 142 143 private static final Log.Tag TAG = new Log.Tag("CameraActivity"); 144 145 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 146 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 147 public static final String ACTION_IMAGE_CAPTURE_SECURE = 148 "android.media.action.IMAGE_CAPTURE_SECURE"; 149 150 // The intent extra for camera from secure lock screen. True if the gallery 151 // should only show newly captured pictures. sSecureAlbumId does not 152 // increment. This is used when switching between camera, camcorder, and 153 // panorama. If the extra is not set, it is in the normal camera mode. 154 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 155 156 /** 157 * Request code from an activity we started that indicated that we do not 158 * want to reset the view to the preview in onResume. 159 */ 160 public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142; 161 162 public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999; 163 164 private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2; 165 private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins. 166 private static final int MAX_PEEK_BITMAP_PIXELS = 1600000; // 1.6 * 4 MBs. 167 /** Load metadata for 10 items ahead of our current. */ 168 private static final int FILMSTRIP_PRELOAD_AHEAD_ITEMS = 10; 169 170 /** Should be used wherever a context is needed. */ 171 private Context mAppContext; 172 173 /** 174 * Whether onResume should reset the view to the preview. 175 */ 176 private boolean mResetToPreviewOnResume = true; 177 178 /** 179 * This data adapter is used by FilmStripView. 180 */ 181 private LocalDataAdapter mDataAdapter; 182 183 /** 184 * TODO: This should be moved to the app level. 185 */ 186 private SettingsManager mSettingsManager; 187 188 private ModeListView mModeListView; 189 private boolean mModeListVisible = false; 190 private int mCurrentModeIndex; 191 private CameraModule mCurrentModule; 192 private ModuleManagerImpl mModuleManager; 193 private FrameLayout mAboveFilmstripControlLayout; 194 private FilmstripController mFilmstripController; 195 private boolean mFilmstripVisible; 196 /** Whether the filmstrip fully covers the preview. */ 197 private boolean mFilmstripCoversPreview = false; 198 private int mResultCodeForTesting; 199 private Intent mResultDataForTesting; 200 private OnScreenHint mStorageHint; 201 private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES; 202 private boolean mAutoRotateScreen; 203 private boolean mSecureCamera; 204 private int mLastRawOrientation; 205 private OrientationManagerImpl mOrientationManager; 206 private LocationManager mLocationManager; 207 private ButtonManager mButtonManager; 208 private Handler mMainHandler; 209 private PanoramaViewHelper mPanoramaViewHelper; 210 private ActionBar mActionBar; 211 private ViewGroup mUndoDeletionBar; 212 private boolean mIsUndoingDeletion = false; 213 214 private final Uri[] mNfcPushUris = new Uri[1]; 215 216 private LocalMediaObserver mLocalImagesObserver; 217 private LocalMediaObserver mLocalVideosObserver; 218 219 private boolean mPendingDeletion = false; 220 221 private CameraController mCameraController; 222 private boolean mPaused; 223 private CameraAppUI mCameraAppUI; 224 225 private PeekAnimationHandler mPeekAnimationHandler; 226 private HandlerThread mPeekAnimationThread; 227 228 private FeedbackHelper mFeedbackHelper; 229 230 private Intent mGalleryIntent; 231 private long mOnCreateTime; 232 233 private Menu mActionBarMenu; 234 private Preloader<Integer, AsyncTask> mPreloader; 235 236 @Override 237 public CameraAppUI getCameraAppUI() { 238 return mCameraAppUI; 239 } 240 241 // close activity when screen turns off 242 private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 243 @Override 244 public void onReceive(Context context, Intent intent) { 245 finish(); 246 } 247 }; 248 249 /** 250 * Whether the screen is kept turned on. 251 */ 252 private boolean mKeepScreenOn; 253 private int mLastLayoutOrientation; 254 private final CameraAppUI.BottomPanel.Listener mMyFilmstripBottomControlListener = 255 new CameraAppUI.BottomPanel.Listener() { 256 257 /** 258 * If the current photo is a photo sphere, this will launch the 259 * Photo Sphere panorama viewer. 260 */ 261 @Override 262 public void onExternalViewer() { 263 if (mPanoramaViewHelper == null) { 264 return; 265 } 266 final LocalData data = getCurrentLocalData(); 267 if (data == null) { 268 return; 269 } 270 final Uri contentUri = data.getUri(); 271 if (contentUri == Uri.EMPTY) { 272 return; 273 } 274 275 if (PanoramaMetadataLoader.isPanoramaAndUseViewer(data)) { 276 mPanoramaViewHelper.showPanorama(CameraActivity.this, contentUri); 277 } else if (RgbzMetadataLoader.hasRGBZData(data)) { 278 mPanoramaViewHelper.showRgbz(contentUri); 279 } 280 } 281 282 @Override 283 public void onEdit() { 284 LocalData data = getCurrentLocalData(); 285 if (data == null) { 286 return; 287 } 288 launchEditor(data); 289 } 290 291 @Override 292 public void onTinyPlanet() { 293 LocalData data = getCurrentLocalData(); 294 if (data == null) { 295 return; 296 } 297 launchTinyPlanetEditor(data); 298 } 299 300 @Override 301 public void onDelete() { 302 final int currentDataId = getCurrentDataId(); 303 UsageStatistics.photoInteraction( 304 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)), 305 eventprotos.CameraEvent.InteractionType.DELETE, 306 InteractionCause.BUTTON); 307 removeData(currentDataId); 308 } 309 310 @Override 311 public void onShare() { 312 final LocalData data = getCurrentLocalData(); 313 314 // If applicable, show release information before this item 315 // is shared. 316 if (PanoramaMetadataLoader.isPanorama(data) 317 || RgbzMetadataLoader.hasRGBZData(data)) { 318 ReleaseDialogHelper.showReleaseInfoDialog(CameraActivity.this, 319 new Callback<Void>() { 320 @Override 321 public void onCallback(Void result) { 322 share(data); 323 } 324 }); 325 } else { 326 share(data); 327 } 328 } 329 330 private void share(LocalData data) { 331 Intent shareIntent = getShareIntentByData(data); 332 if (shareIntent != null) { 333 try { 334 launchActivityByIntent(shareIntent); 335 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false); 336 } catch (ActivityNotFoundException ex) { 337 // Nothing. 338 } 339 } 340 } 341 342 private int getCurrentDataId() { 343 return mFilmstripController.getCurrentId(); 344 } 345 346 private LocalData getCurrentLocalData() { 347 return mDataAdapter.getLocalData(getCurrentDataId()); 348 } 349 350 /** 351 * Sets up the share intent and NFC properly according to the 352 * data. 353 * 354 * @param data The data to be shared. 355 */ 356 private Intent getShareIntentByData(final LocalData data) { 357 Intent intent = null; 358 final Uri contentUri = data.getUri(); 359 if (PanoramaMetadataLoader.isPanorama360(data) && 360 data.getUri() != Uri.EMPTY) { 361 intent = new Intent(Intent.ACTION_SEND); 362 intent.setType("application/vnd.google.panorama360+jpg"); 363 intent.putExtra(Intent.EXTRA_STREAM, contentUri); 364 } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) { 365 final String mimeType = data.getMimeType(); 366 intent = getShareIntentFromType(mimeType); 367 if (intent != null) { 368 intent.putExtra(Intent.EXTRA_STREAM, contentUri); 369 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 370 } 371 intent = Intent.createChooser(intent, null); 372 } 373 return intent; 374 } 375 376 /** 377 * Get the share intent according to the mimeType 378 * 379 * @param mimeType The mimeType of current data. 380 * @return the video/image's ShareIntent or null if mimeType is 381 * invalid. 382 */ 383 private Intent getShareIntentFromType(String mimeType) { 384 // Lazily create the intent object. 385 Intent intent = new Intent(Intent.ACTION_SEND); 386 if (mimeType.startsWith("video/")) { 387 intent.setType("video/*"); 388 } else { 389 if (mimeType.startsWith("image/")) { 390 intent.setType("image/*"); 391 } else { 392 Log.w(TAG, "unsupported mimeType " + mimeType); 393 } 394 } 395 return intent; 396 } 397 398 @Override 399 public void onProgressErrorClicked() { 400 LocalData data = getCurrentLocalData(); 401 getServices().getCaptureSessionManager().removeErrorMessage( 402 data.getUri()); 403 updateBottomControlsByData(data); 404 } 405 }; 406 407 private ComboPreferences mPreferences; 408 409 @Override 410 public void onCameraOpened(CameraManager.CameraProxy camera) { 411 /** 412 * The current UI requires that the flash option visibility in front-facing 413 * camera be 414 * * disabled if back facing camera supports flash 415 * * hidden if back facing camera does not support flash 416 * We save whether back facing camera supports flash because we cannot get 417 * this in front facing camera without a camera switch. 418 * 419 * If this preference is cleared, we also need to clear the camera facing 420 * setting so we default to opening the camera in back facing camera, and 421 * can save this flash support value again. 422 */ 423 if (!mSettingsManager.isSet(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA)) { 424 HardwareSpec hardware = new HardwareSpecImpl(camera.getParameters()); 425 mSettingsManager.setBoolean(SettingsManager.SETTING_FLASH_SUPPORTED_BACK_CAMERA, 426 hardware.isFlashSupported()); 427 } 428 429 if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) { 430 // We shouldn't be here. Just close the camera and leave. 431 camera.release(false); 432 throw new IllegalStateException("Camera opened but the module shouldn't be " + 433 "requesting"); 434 } 435 if (mCurrentModule != null) { 436 SettingsCapabilities capabilities = 437 SettingsUtil.getSettingsCapabilities(camera); 438 mSettingsManager.changeCamera(camera.getCameraId(), capabilities); 439 mCurrentModule.onCameraAvailable(camera); 440 } 441 mCameraAppUI.onChangeCamera(); 442 } 443 444 @Override 445 public void onCameraDisabled(int cameraId) { 446 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.SECURITY); 447 448 CameraUtil.showErrorAndFinish(this, R.string.camera_disabled); 449 } 450 451 @Override 452 public void onDeviceOpenFailure(int cameraId) { 453 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.OPEN_FAILURE); 454 455 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 456 } 457 458 @Override 459 public void onDeviceOpenedAlready(int cameraId) { 460 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 461 } 462 463 @Override 464 public void onReconnectionFailure(CameraManager mgr) { 465 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE); 466 467 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 468 } 469 470 private static class MainHandler extends Handler { 471 final WeakReference<CameraActivity> mActivity; 472 473 public MainHandler(CameraActivity activity, Looper looper) { 474 super(looper); 475 mActivity = new WeakReference<CameraActivity>(activity); 476 } 477 478 @Override 479 public void handleMessage(Message msg) { 480 CameraActivity activity = mActivity.get(); 481 if (activity == null) { 482 return; 483 } 484 switch (msg.what) { 485 486 case MSG_CLEAR_SCREEN_ON_FLAG: { 487 if (!activity.mPaused) { 488 activity.getWindow().clearFlags( 489 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 490 } 491 break; 492 } 493 } 494 } 495 } 496 497 private String fileNameFromDataID(int dataID) { 498 final LocalData localData = mDataAdapter.getLocalData(dataID); 499 500 File localFile = new File(localData.getPath()); 501 return localFile.getName(); 502 } 503 504 private final FilmstripContentPanel.Listener mFilmstripListener = 505 new FilmstripContentPanel.Listener() { 506 507 @Override 508 public void onSwipeOut() { 509 UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 510 eventprotos.CameraEvent.InteractionCause.SWIPE_RIGHT); 511 } 512 513 @Override 514 public void onSwipeOutBegin() { 515 mActionBar.hide(); 516 mFilmstripCoversPreview = false; 517 updatePreviewVisibility(); 518 } 519 520 @Override 521 public void onFilmstripHidden() { 522 mFilmstripVisible = false; 523 CameraActivity.this.setFilmstripUiVisibility(false); 524 // When the user hide the filmstrip (either swipe out or 525 // tap on back key) we move to the first item so next time 526 // when the user swipe in the filmstrip, the most recent 527 // one is shown. 528 mFilmstripController.goToFirstItem(); 529 } 530 531 @Override 532 public void onFilmstripShown() { 533 mFilmstripVisible = true; 534 updateUiByData(mFilmstripController.getCurrentId()); 535 } 536 537 @Override 538 public void onFocusedDataLongPressed(int dataId) { 539 // Do nothing. 540 } 541 542 @Override 543 public void onFocusedDataPromoted(int dataID) { 544 UsageStatistics.photoInteraction( 545 UsageStatistics.hashFileName(fileNameFromDataID(dataID)), 546 eventprotos.CameraEvent.InteractionType.DELETE, 547 InteractionCause.SWIPE_UP); 548 549 removeData(dataID); 550 } 551 552 @Override 553 public void onFocusedDataDemoted(int dataID) { 554 UsageStatistics.photoInteraction( 555 UsageStatistics.hashFileName(fileNameFromDataID(dataID)), 556 eventprotos.CameraEvent.InteractionType.DELETE, 557 InteractionCause.SWIPE_DOWN); 558 559 removeData(dataID); 560 } 561 562 @Override 563 public void onEnterFullScreenUiShown(int dataId) { 564 if (mFilmstripVisible) { 565 CameraActivity.this.setFilmstripUiVisibility(true); 566 } 567 } 568 569 @Override 570 public void onLeaveFullScreenUiShown(int dataId) { 571 // Do nothing. 572 } 573 574 @Override 575 public void onEnterFullScreenUiHidden(int dataId) { 576 if (mFilmstripVisible) { 577 CameraActivity.this.setFilmstripUiVisibility(false); 578 } 579 } 580 581 @Override 582 public void onLeaveFullScreenUiHidden(int dataId) { 583 // Do nothing. 584 } 585 586 @Override 587 public void onEnterFilmstrip(int dataId) { 588 if (mFilmstripVisible) { 589 CameraActivity.this.setFilmstripUiVisibility(true); 590 } 591 } 592 593 @Override 594 public void onLeaveFilmstrip(int dataId) { 595 // Do nothing. 596 } 597 598 @Override 599 public void onDataReloaded() { 600 if (!mFilmstripVisible) { 601 return; 602 } 603 updateUiByData(mFilmstripController.getCurrentId()); 604 } 605 606 @Override 607 public void onDataUpdated(int dataId) { 608 if (!mFilmstripVisible) { 609 return; 610 } 611 updateUiByData(mFilmstripController.getCurrentId()); 612 } 613 614 @Override 615 public void onEnterZoomView(int dataID) { 616 if (mFilmstripVisible) { 617 CameraActivity.this.setFilmstripUiVisibility(false); 618 } 619 } 620 621 @Override 622 public void onDataFocusChanged(final int prevDataId, final int newDataId) { 623 if (!mFilmstripVisible) { 624 return; 625 } 626 // TODO: This callback is UI event callback, should always 627 // happen on UI thread. Find the reason for this 628 // runOnUiThread() and fix it. 629 runOnUiThread(new Runnable() { 630 @Override 631 public void run() { 632 updateUiByData(newDataId); 633 } 634 }); 635 } 636 637 @Override 638 public void onScroll(int firstVisiblePosition, int visibleItemCount, int totalItemCount) { 639 mPreloader.onScroll(null /*absListView*/, firstVisiblePosition, visibleItemCount, totalItemCount); 640 } 641 }; 642 643 private final LocalDataAdapter.LocalDataListener mLocalDataListener = 644 new LocalDataAdapter.LocalDataListener() { 645 @Override 646 public void onMetadataUpdated(List<Integer> updatedData) { 647 int currentDataId = mFilmstripController.getCurrentId(); 648 for (Integer dataId : updatedData) { 649 if (dataId == currentDataId) { 650 updateBottomControlsByData(mDataAdapter.getLocalData(dataId)); 651 } 652 } 653 } 654 }; 655 656 public void gotoGallery() { 657 UsageStatistics.changeScreen(NavigationChange.Mode.FILMSTRIP, 658 InteractionCause.BUTTON); 659 660 mFilmstripController.goToNextItem(); 661 } 662 663 /** 664 * If 'visible' is false, this hides the action bar and switches the 665 * filmstrip UI to lights-out mode. 666 * 667 * @param visible is false, this hides the action bar and switches the 668 * filmstrip UI to lights-out mode. 669 */ 670 private void setFilmstripUiVisibility(boolean visible) { 671 int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility(); 672 int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE 673 : View.SYSTEM_UI_FLAG_FULLSCREEN); 674 if (newSystemUIVisibility != currentSystemUIVisibility) { 675 mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility); 676 } 677 678 mCameraAppUI.getFilmstripBottomControls().setVisible(visible); 679 if (visible != mActionBar.isShowing()) { 680 if (visible) { 681 mActionBar.show(); 682 } else { 683 mActionBar.hide(); 684 } 685 } 686 mFilmstripCoversPreview = visible; 687 updatePreviewVisibility(); 688 } 689 690 private void hideSessionProgress() { 691 mCameraAppUI.getFilmstripBottomControls().hideProgress(); 692 } 693 694 private void showSessionProgress(CharSequence message) { 695 CameraAppUI.BottomPanel controls = mCameraAppUI.getFilmstripBottomControls(); 696 controls.setProgressText(message); 697 controls.hideControls(); 698 controls.hideProgressError(); 699 controls.showProgress(); 700 } 701 702 private void showProcessError(CharSequence message) { 703 mCameraAppUI.getFilmstripBottomControls().showProgressError(message); 704 } 705 706 private void updateSessionProgress(int progress) { 707 mCameraAppUI.getFilmstripBottomControls().setProgress(progress); 708 } 709 710 private void updateSessionProgressText(CharSequence message) { 711 mCameraAppUI.getFilmstripBottomControls().setProgressText(message); 712 } 713 714 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 715 private void setupNfcBeamPush() { 716 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mAppContext); 717 if (adapter == null) { 718 return; 719 } 720 721 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) { 722 // Disable beaming 723 adapter.setNdefPushMessage(null, CameraActivity.this); 724 return; 725 } 726 727 adapter.setBeamPushUris(null, CameraActivity.this); 728 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() { 729 @Override 730 public Uri[] createBeamUris(NfcEvent event) { 731 return mNfcPushUris; 732 } 733 }, CameraActivity.this); 734 } 735 736 @Override 737 public void onMenuVisibilityChanged(boolean isVisible) { 738 // TODO: Remove this or bring back the original implementation: cancel 739 // auto-hide actionbar. 740 } 741 742 @Override 743 public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) { 744 int currentDataId = mFilmstripController.getCurrentId(); 745 if (currentDataId < 0) { 746 return false; 747 } 748 UsageStatistics.photoInteraction( 749 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)), 750 eventprotos.CameraEvent.InteractionType.SHARE, 751 InteractionCause.BUTTON); 752 // TODO add intent.getComponent().getPackageName() 753 return true; 754 } 755 756 // Note: All callbacks come back on the main thread. 757 private final SessionListener mSessionListener = 758 new SessionListener() { 759 @Override 760 public void onSessionQueued(final Uri uri) { 761 if (!Storage.isSessionUri(uri)) { 762 return; 763 } 764 LocalSessionData newData = new LocalSessionData(uri); 765 mDataAdapter.addData(newData); 766 } 767 768 @Override 769 public void onSessionDone(final Uri sessionUri) { 770 Log.v(TAG, "onSessionDone:" + sessionUri); 771 Uri contentUri = Storage.getContentUriForSessionUri(sessionUri); 772 if (contentUri == null) { 773 mDataAdapter.refresh(sessionUri); 774 return; 775 } 776 LocalData newData = LocalMediaData.PhotoData.fromContentUri( 777 getContentResolver(), contentUri); 778 779 final int pos = mDataAdapter.findDataByContentUri(sessionUri); 780 if (pos == -1) { 781 // We do not have a placeholder for this image, perhaps due to the 782 // activity crashing or being killed. 783 mDataAdapter.addData(newData); 784 } else { 785 mDataAdapter.updateData(pos, newData); 786 } 787 } 788 789 @Override 790 public void onSessionProgress(final Uri uri, final int progress) { 791 if (progress < 0) { 792 // Do nothing, there is no task for this URI. 793 return; 794 } 795 int currentDataId = mFilmstripController.getCurrentId(); 796 if (currentDataId == -1) { 797 return; 798 } 799 if (uri.equals( 800 mDataAdapter.getLocalData(currentDataId).getUri())) { 801 updateSessionProgress(progress); 802 } 803 } 804 805 @Override 806 public void onSessionProgressText(final Uri uri, final CharSequence message) { 807 int currentDataId = mFilmstripController.getCurrentId(); 808 if (currentDataId == -1) { 809 return; 810 } 811 if (uri.equals( 812 mDataAdapter.getLocalData(currentDataId).getUri())) { 813 updateSessionProgressText(message); 814 } 815 } 816 817 @Override 818 public void onSessionUpdated(Uri uri) { 819 mDataAdapter.refresh(uri); 820 } 821 822 @Override 823 public void onSessionPreviewAvailable(Uri uri) { 824 mDataAdapter.refresh(uri); 825 int dataId = mDataAdapter.findDataByContentUri(uri); 826 if (dataId != -1) { 827 startPeekAnimation(mDataAdapter.getLocalData(dataId)); 828 } 829 } 830 831 @Override 832 public void onSessionFailed(Uri uri, CharSequence reason) { 833 Log.v(TAG, "onSessionFailed:" + uri); 834 835 int failedDataId = mDataAdapter.findDataByContentUri(uri); 836 int currentDataId = mFilmstripController.getCurrentId(); 837 838 if (currentDataId == failedDataId) { 839 updateSessionProgress(0); 840 showProcessError(reason); 841 } 842 // HERE 843 mDataAdapter.refresh(uri); 844 } 845 }; 846 847 @Override 848 public Context getAndroidContext() { 849 return mAppContext; 850 } 851 852 @Override 853 public void launchActivityByIntent(Intent intent) { 854 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW); 855 } 856 857 @Override 858 public int getCurrentModuleIndex() { 859 return mCurrentModeIndex; 860 } 861 862 @Override 863 public ModuleController getCurrentModuleController() { 864 return mCurrentModule; 865 } 866 867 @Override 868 public int getQuickSwitchToModuleId(int currentModuleIndex) { 869 return mModuleManager.getQuickSwitchToModuleId(currentModuleIndex, mSettingsManager, 870 mAppContext); 871 } 872 873 @Override 874 public SurfaceTexture getPreviewBuffer() { 875 // TODO: implement this 876 return null; 877 } 878 879 @Override 880 public void onPreviewReadyToStart() { 881 mCameraAppUI.onPreviewReadyToStart(); 882 } 883 884 @Override 885 public void onPreviewStarted() { 886 mCameraAppUI.onPreviewStarted(); 887 } 888 889 @Override 890 public void addPreviewAreaSizeChangedListener( 891 PreviewStatusListener.PreviewAreaChangedListener listener) { 892 mCameraAppUI.addPreviewAreaChangedListener(listener); 893 } 894 895 @Override 896 public void removePreviewAreaSizeChangedListener( 897 PreviewStatusListener.PreviewAreaChangedListener listener) { 898 mCameraAppUI.removePreviewAreaChangedListener(listener); 899 } 900 901 @Override 902 public void setupOneShotPreviewListener() { 903 mCameraController.setOneShotPreviewCallback(mMainHandler, 904 new CameraManager.CameraPreviewDataCallback() { 905 @Override 906 public void onPreviewFrame(byte[] data, CameraManager.CameraProxy camera) { 907 mCurrentModule.onPreviewInitialDataReceived(); 908 mCameraAppUI.onNewPreviewFrame(); 909 } 910 } 911 ); 912 } 913 914 @Override 915 public void updatePreviewAspectRatio(float aspectRatio) { 916 mCameraAppUI.updatePreviewAspectRatio(aspectRatio); 917 } 918 919 @Override 920 public void updatePreviewTransform(Matrix matrix) { 921 mCameraAppUI.updatePreviewTransform(matrix); 922 } 923 924 @Override 925 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) { 926 mCameraAppUI.setPreviewStatusListener(previewStatusListener); 927 } 928 929 @Override 930 public FrameLayout getModuleLayoutRoot() { 931 return mCameraAppUI.getModuleRootView(); 932 } 933 934 @Override 935 public void setShutterEventsListener(ShutterEventsListener listener) { 936 // TODO: implement this 937 } 938 939 @Override 940 public void setShutterEnabled(boolean enabled) { 941 // TODO: implement this 942 } 943 944 @Override 945 public boolean isShutterEnabled() { 946 // TODO: implement this 947 return false; 948 } 949 950 @Override 951 public void startPreCaptureAnimation() { 952 mCameraAppUI.startPreCaptureAnimation(); 953 } 954 955 @Override 956 public void cancelPreCaptureAnimation() { 957 // TODO: implement this 958 } 959 960 @Override 961 public void startPostCaptureAnimation() { 962 // TODO: implement this 963 } 964 965 @Override 966 public void startPostCaptureAnimation(Bitmap thumbnail) { 967 // TODO: implement this 968 } 969 970 @Override 971 public void cancelPostCaptureAnimation() { 972 // TODO: implement this 973 } 974 975 @Override 976 public OrientationManager getOrientationManager() { 977 return mOrientationManager; 978 } 979 980 @Override 981 public LocationManager getLocationManager() { 982 return mLocationManager; 983 } 984 985 @Override 986 public void lockOrientation() { 987 if (mOrientationManager != null) { 988 mOrientationManager.lockOrientation(); 989 } 990 } 991 992 @Override 993 public void unlockOrientation() { 994 if (mOrientationManager != null) { 995 mOrientationManager.unlockOrientation(); 996 } 997 } 998 999 /** 1000 * Starts the filmstrip peek animation if the filmstrip is not visible. 1001 * Only {@link LocalData#LOCAL_IMAGE}, {@link 1002 * LocalData#LOCAL_IN_PROGRESS_DATA} and {@link 1003 * LocalData#LOCAL_VIDEO} are supported. 1004 * 1005 * @param data The data to peek. 1006 */ 1007 private void startPeekAnimation(final LocalData data) { 1008 if (mFilmstripVisible || mPeekAnimationHandler == null) { 1009 return; 1010 } 1011 1012 int dataType = data.getLocalDataType(); 1013 if (dataType != LocalData.LOCAL_IMAGE && dataType != LocalData.LOCAL_IN_PROGRESS_DATA && 1014 dataType != LocalData.LOCAL_VIDEO) { 1015 return; 1016 } 1017 1018 mPeekAnimationHandler.startDecodingJob(data, new Callback<Bitmap>() { 1019 @Override 1020 public void onCallback(Bitmap result) { 1021 mCameraAppUI.startPeekAnimation(result, true); 1022 } 1023 }); 1024 } 1025 1026 @Override 1027 public void notifyNewMedia(Uri uri) { 1028 ContentResolver cr = getContentResolver(); 1029 String mimeType = cr.getType(uri); 1030 if (LocalDataUtil.isMimeTypeVideo(mimeType)) { 1031 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 1032 LocalData newData = LocalMediaData.VideoData.fromContentUri(getContentResolver(), uri); 1033 if (newData == null) { 1034 Log.e(TAG, "Can't find video data in content resolver:" + uri); 1035 return; 1036 } 1037 if (mDataAdapter.addData(newData)) { 1038 startPeekAnimation(newData); 1039 } 1040 } else if (LocalDataUtil.isMimeTypeImage(mimeType)) { 1041 CameraUtil.broadcastNewPicture(mAppContext, uri); 1042 LocalData newData = LocalMediaData.PhotoData.fromContentUri(getContentResolver(), uri); 1043 if (newData == null) { 1044 Log.e(TAG, "Can't find photo data in content resolver:" + uri); 1045 return; 1046 } 1047 if (mDataAdapter.addData(newData)) { 1048 startPeekAnimation(newData); 1049 } 1050 } else { 1051 Log.w(TAG, "Unknown new media with MIME type:" + mimeType + ", uri:" + uri); 1052 } 1053 } 1054 1055 @Override 1056 public void enableKeepScreenOn(boolean enabled) { 1057 if (mPaused) { 1058 return; 1059 } 1060 1061 mKeepScreenOn = enabled; 1062 if (mKeepScreenOn) { 1063 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1064 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1065 } else { 1066 keepScreenOnForAWhile(); 1067 } 1068 } 1069 1070 @Override 1071 public CameraProvider getCameraProvider() { 1072 return mCameraController; 1073 } 1074 1075 private void removeData(int dataID) { 1076 mDataAdapter.removeData(dataID); 1077 if (mDataAdapter.getTotalNumber() > 1) { 1078 showUndoDeletionBar(); 1079 } else { 1080 // If camera preview is the only view left in filmstrip, 1081 // no need to show undo bar. 1082 mPendingDeletion = true; 1083 performDeletion(); 1084 if (mFilmstripVisible) { 1085 mCameraAppUI.getFilmstripContentPanel().animateHide(); 1086 } 1087 } 1088 } 1089 1090 @Override 1091 public boolean onOptionsItemSelected(MenuItem item) { 1092 // Handle presses on the action bar items 1093 switch (item.getItemId()) { 1094 case android.R.id.home: 1095 if (mFilmstripVisible && startGallery()) { 1096 return true; 1097 } 1098 onBackPressed(); 1099 return true; 1100 case R.id.action_details: 1101 showDetailsDialog(mFilmstripController.getCurrentId()); 1102 return true; 1103 default: 1104 return super.onOptionsItemSelected(item); 1105 } 1106 } 1107 1108 private boolean isCaptureIntent() { 1109 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction()) 1110 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 1111 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 1112 return true; 1113 } else { 1114 return false; 1115 } 1116 } 1117 1118 private final SettingsManager.StrictUpgradeCallback mStrictUpgradeCallback 1119 = new SettingsManager.StrictUpgradeCallback() { 1120 @Override 1121 public void upgrade(SettingsManager settingsManager, int version) { 1122 // Show the location dialog on upgrade if 1123 // (a) the user has never set this option (status quo). 1124 // (b) the user opt'ed out previously. 1125 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION)) { 1126 // Location is set in the source file defined for this setting. 1127 // Remove the setting if the value is false to launch the dialog. 1128 if (!settingsManager.getBoolean(SettingsManager.SETTING_RECORD_LOCATION)) { 1129 settingsManager.remove(SettingsManager.SETTING_RECORD_LOCATION); 1130 } 1131 } else { 1132 // Location is not set, check to see if we're upgrading from 1133 // a different source file. 1134 if (settingsManager.isSet(SettingsManager.SETTING_RECORD_LOCATION, 1135 SettingsManager.SOURCE_GLOBAL)) { 1136 boolean location = settingsManager.getBoolean( 1137 SettingsManager.SETTING_RECORD_LOCATION, 1138 SettingsManager.SOURCE_GLOBAL); 1139 if (location) { 1140 // Set the old setting only if the value is true, to prevent 1141 // launching the dialog. 1142 settingsManager.setBoolean( 1143 SettingsManager.SETTING_RECORD_LOCATION, location); 1144 } 1145 } 1146 } 1147 1148 settingsManager.remove(SettingsManager.SETTING_STARTUP_MODULE_INDEX); 1149 } 1150 }; 1151 1152 private final CameraManager.CameraExceptionCallback mCameraDefaultExceptionCallback 1153 = new CameraManager.CameraExceptionCallback() { 1154 @Override 1155 public void onCameraException(RuntimeException e) { 1156 Log.d(TAG, "Camera Exception", e); 1157 CameraUtil.showErrorAndFinish(CameraActivity.this, 1158 R.string.cannot_connect_camera); 1159 } 1160 }; 1161 1162 @Override 1163 public void onCreate(Bundle state) { 1164 super.onCreate(state); 1165 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START); 1166 mOnCreateTime = System.currentTimeMillis(); 1167 mAppContext = getApplicationContext(); 1168 GcamHelper.init(getContentResolver()); 1169 1170 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 1171 setContentView(R.layout.activity_main); 1172 mActionBar = getActionBar(); 1173 mActionBar.addOnMenuVisibilityListener(this); 1174 mMainHandler = new MainHandler(this, getMainLooper()); 1175 mCameraController = 1176 new CameraController(mAppContext, this, mMainHandler, 1177 CameraManagerFactory.getAndroidCameraManager()); 1178 mCameraController.setCameraDefaultExceptionCallback(mCameraDefaultExceptionCallback, 1179 mMainHandler); 1180 1181 mPreferences = new ComboPreferences(mAppContext); 1182 1183 mSettingsManager = new SettingsManager(mAppContext, this, 1184 mCameraController.getNumberOfCameras(), mStrictUpgradeCallback); 1185 1186 // Remove this after we get rid of ComboPreferences. 1187 int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID)); 1188 mPreferences.setLocalId(mAppContext, cameraId); 1189 CameraSettings.upgradeGlobalPreferences(mPreferences, 1190 mCameraController.getNumberOfCameras()); 1191 // TODO: Try to move all the resources allocation to happen as soon as 1192 // possible so we can call module.init() at the earliest time. 1193 mModuleManager = new ModuleManagerImpl(); 1194 ModulesInfo.setupModules(mAppContext, mModuleManager); 1195 1196 mModeListView = (ModeListView) findViewById(R.id.mode_list_layout); 1197 mModeListView.init(mModuleManager.getSupportedModeIndexList()); 1198 if (ApiHelper.HAS_ROTATION_ANIMATION) { 1199 setRotationAnimation(); 1200 } 1201 mModeListView.setVisibilityChangedListener(new ModeListVisibilityChangedListener() { 1202 @Override 1203 public void onVisibilityChanged(boolean visible) { 1204 mModeListVisible = visible; 1205 updatePreviewVisibility(); 1206 } 1207 }); 1208 1209 // Check if this is in the secure camera mode. 1210 Intent intent = getIntent(); 1211 String action = intent.getAction(); 1212 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 1213 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 1214 mSecureCamera = true; 1215 } else { 1216 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 1217 } 1218 1219 if (mSecureCamera) { 1220 // Foreground event caused by lock screen startup. 1221 // It is necessary to log this in onCreate, to avoid the 1222 // onResume->onPause->onResume sequence. 1223 UsageStatistics.foregrounded( 1224 eventprotos.ForegroundEvent.ForegroundSource.LOCK_SCREEN); 1225 1226 // Change the window flags so that secure camera can show when 1227 // locked 1228 Window win = getWindow(); 1229 WindowManager.LayoutParams params = win.getAttributes(); 1230 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 1231 win.setAttributes(params); 1232 1233 // Filter for screen off so that we can finish secure camera 1234 // activity 1235 // when screen is off. 1236 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 1237 registerReceiver(mScreenOffReceiver, filter); 1238 } 1239 mCameraAppUI = new CameraAppUI(this, 1240 (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent()); 1241 1242 mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener); 1243 1244 mAboveFilmstripControlLayout = 1245 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout); 1246 1247 // Add the session listener so we can track the session progress 1248 // updates. 1249 getServices().getCaptureSessionManager().addSessionListener(mSessionListener); 1250 mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController(); 1251 mFilmstripController.setImageGap( 1252 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 1253 mPanoramaViewHelper = new PanoramaViewHelper(this); 1254 mPanoramaViewHelper.onCreate(); 1255 // Set up the camera preview first so the preview shows up ASAP. 1256 mDataAdapter = new CameraDataAdapter(mAppContext, 1257 new ColorDrawable(getResources().getColor(R.color.photo_placeholder))); 1258 mDataAdapter.setLocalDataListener(mLocalDataListener); 1259 1260 mPreloader = new Preloader<Integer, AsyncTask>(FILMSTRIP_PRELOAD_AHEAD_ITEMS, mDataAdapter, 1261 mDataAdapter); 1262 1263 mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener); 1264 1265 mLocationManager = new LocationManager(mAppContext); 1266 1267 mOrientationManager = new OrientationManagerImpl(this); 1268 mOrientationManager.addOnOrientationChangeListener(mMainHandler, this); 1269 1270 setModuleFromModeIndex(getModeIndex()); 1271 mCameraAppUI.prepareModuleUI(); 1272 mCurrentModule.init(this, isSecureCamera(), isCaptureIntent()); 1273 1274 if (!mSecureCamera) { 1275 mFilmstripController.setDataAdapter(mDataAdapter); 1276 if (!isCaptureIntent()) { 1277 mDataAdapter.requestLoad(new Callback<Void>() { 1278 @Override 1279 public void onCallback(Void result) { 1280 fillTemporarySessions(); 1281 } 1282 }); 1283 } 1284 } else { 1285 // Put a lock placeholder as the last image by setting its date to 1286 // 0. 1287 ImageView v = (ImageView) getLayoutInflater().inflate( 1288 R.layout.secure_album_placeholder, null); 1289 v.setOnClickListener(new View.OnClickListener() { 1290 @Override 1291 public void onClick(View view) { 1292 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, 1293 InteractionCause.BUTTON); 1294 startGallery(); 1295 finish(); 1296 } 1297 }); 1298 v.setContentDescription(getString(R.string.accessibility_unlock_to_camera)); 1299 mDataAdapter = new FixedLastDataAdapter( 1300 mAppContext, 1301 mDataAdapter, 1302 new SimpleViewData( 1303 v, 1304 v.getDrawable().getIntrinsicWidth(), 1305 v.getDrawable().getIntrinsicHeight(), 1306 0, 0)); 1307 // Flush out all the original data. 1308 mDataAdapter.flush(); 1309 mFilmstripController.setDataAdapter(mDataAdapter); 1310 } 1311 1312 setupNfcBeamPush(); 1313 1314 mLocalImagesObserver = new LocalMediaObserver(); 1315 mLocalVideosObserver = new LocalMediaObserver(); 1316 1317 getContentResolver().registerContentObserver( 1318 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, 1319 mLocalImagesObserver); 1320 getContentResolver().registerContentObserver( 1321 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, 1322 mLocalVideosObserver); 1323 if (FeedbackHelper.feedbackAvailable()) { 1324 mFeedbackHelper = new FeedbackHelper(mAppContext); 1325 } 1326 } 1327 1328 /** 1329 * Get the current mode index from the Intent or from persistent 1330 * settings. 1331 */ 1332 public int getModeIndex() { 1333 int modeIndex = -1; 1334 int photoIndex = getResources().getInteger(R.integer.camera_mode_photo); 1335 int videoIndex = getResources().getInteger(R.integer.camera_mode_video); 1336 int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1337 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 1338 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 1339 modeIndex = videoIndex; 1340 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction()) 1341 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction())) { 1342 // TODO: synchronize mode options with photo module without losing 1343 // HDR+ preferences. 1344 modeIndex = photoIndex; 1345 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent() 1346 .getAction()) 1347 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 1348 modeIndex = mSettingsManager.getInt( 1349 SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX); 1350 } else { 1351 // If the activity has not been started using an explicit intent, 1352 // read the module index from the last time the user changed modes 1353 modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX); 1354 if ((modeIndex == gcamIndex && 1355 !GcamHelper.hasGcamCapture()) || modeIndex < 0) { 1356 modeIndex = photoIndex; 1357 } 1358 } 1359 return modeIndex; 1360 } 1361 1362 /** 1363 * Call this whenever the mode drawer or filmstrip change the visibility 1364 * state. 1365 */ 1366 private void updatePreviewVisibility() { 1367 if (mCurrentModule == null) { 1368 return; 1369 } 1370 1371 int visibility = getPreviewVisibility(); 1372 updatePreviewRendering(visibility); 1373 updateCaptureControls(visibility); 1374 mCurrentModule.onPreviewVisibilityChanged(visibility); 1375 } 1376 1377 private void updateCaptureControls(int visibility) { 1378 if (visibility == ModuleController.VISIBILITY_HIDDEN) { 1379 mCameraAppUI.setIndicatorBottomBarWrapperVisible(false); 1380 } else { 1381 mCameraAppUI.setIndicatorBottomBarWrapperVisible(true); 1382 } 1383 } 1384 1385 private void updatePreviewRendering(int visibility) { 1386 if (visibility == ModuleController.VISIBILITY_HIDDEN) { 1387 mCameraAppUI.pausePreviewRendering(); 1388 } else { 1389 mCameraAppUI.resumePreviewRendering(); 1390 } 1391 } 1392 1393 private int getPreviewVisibility() { 1394 if (mFilmstripCoversPreview) { 1395 return ModuleController.VISIBILITY_HIDDEN; 1396 } else if (mModeListVisible){ 1397 return ModuleController.VISIBILITY_COVERED; 1398 } else { 1399 return ModuleController.VISIBILITY_VISIBLE; 1400 } 1401 } 1402 1403 private void setRotationAnimation() { 1404 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 1405 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 1406 Window win = getWindow(); 1407 WindowManager.LayoutParams winParams = win.getAttributes(); 1408 winParams.rotationAnimation = rotationAnimation; 1409 win.setAttributes(winParams); 1410 } 1411 1412 @Override 1413 public void onUserInteraction() { 1414 super.onUserInteraction(); 1415 if (!isFinishing()) { 1416 keepScreenOnForAWhile(); 1417 } 1418 } 1419 1420 @Override 1421 public boolean dispatchTouchEvent(MotionEvent ev) { 1422 boolean result = super.dispatchTouchEvent(ev); 1423 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 1424 // Real deletion is postponed until the next user interaction after 1425 // the gesture that triggers deletion. Until real deletion is 1426 // performed, users can click the undo button to bring back the 1427 // image that they chose to delete. 1428 if (mPendingDeletion && !mIsUndoingDeletion) { 1429 performDeletion(); 1430 } 1431 } 1432 return result; 1433 } 1434 1435 @Override 1436 public void onPause() { 1437 /* 1438 * Save the last module index after all secure camera and icon launches, 1439 * not just on mode switches. 1440 * 1441 * Right now we exclude capture intents from this logic, because we also 1442 * ignore the cross-Activity recovery logic in onStart for capture intents. 1443 */ 1444 if (!isCaptureIntent()) { 1445 mSettingsManager.setInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX, 1446 mCurrentModeIndex); 1447 } 1448 1449 mPaused = true; 1450 mPeekAnimationHandler = null; 1451 mPeekAnimationThread.quitSafely(); 1452 mPeekAnimationThread = null; 1453 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE); 1454 1455 // Delete photos that are pending deletion 1456 performDeletion(); 1457 mCurrentModule.pause(); 1458 mOrientationManager.pause(); 1459 // Close the camera and wait for the operation done. 1460 mCameraController.closeCamera(); 1461 mPanoramaViewHelper.onPause(); 1462 1463 mLocalImagesObserver.setForegroundChangeListener(null); 1464 mLocalImagesObserver.setActivityPaused(true); 1465 mLocalVideosObserver.setActivityPaused(true); 1466 mPreloader.cancelAllLoads(); 1467 resetScreenOn(); 1468 super.onPause(); 1469 } 1470 1471 @Override 1472 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1473 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) { 1474 mResetToPreviewOnResume = false; 1475 } else { 1476 super.onActivityResult(requestCode, resultCode, data); 1477 } 1478 } 1479 1480 @Override 1481 public void onResume() { 1482 mPaused = false; 1483 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME); 1484 1485 mLastLayoutOrientation = getResources().getConfiguration().orientation; 1486 1487 // TODO: Handle this in OrientationManager. 1488 // Auto-rotate off 1489 if (Settings.System.getInt(getContentResolver(), 1490 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) { 1491 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 1492 mAutoRotateScreen = false; 1493 } else { 1494 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 1495 mAutoRotateScreen = true; 1496 } 1497 1498 if (isCaptureIntent()) { 1499 // Foreground event caused by photo or video capure intent. 1500 UsageStatistics.foregrounded( 1501 eventprotos.ForegroundEvent.ForegroundSource.INTENT_PICKER); 1502 } else if (!mSecureCamera) { 1503 // Foreground event that is not caused by an intent. 1504 UsageStatistics.foregrounded( 1505 eventprotos.ForegroundEvent.ForegroundSource.ICON_LAUNCHER); 1506 } 1507 1508 Drawable galleryLogo; 1509 if (mSecureCamera) { 1510 mGalleryIntent = null; 1511 galleryLogo = null; 1512 } else { 1513 mGalleryIntent = IntentHelper.getDefaultGalleryIntent(mAppContext); 1514 galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent); 1515 } 1516 if (galleryLogo == null) { 1517 try { 1518 galleryLogo = getPackageManager().getActivityLogo(getComponentName()); 1519 } catch (PackageManager.NameNotFoundException e) { 1520 Log.e(TAG, "Can't get the activity logo"); 1521 } 1522 } 1523 if (mGalleryIntent != null) { 1524 mActionBar.setDisplayUseLogoEnabled(true); 1525 } 1526 mActionBar.setLogo(galleryLogo); 1527 mOrientationManager.resume(); 1528 super.onResume(); 1529 mPeekAnimationThread = new HandlerThread("Peek animation"); 1530 mPeekAnimationThread.start(); 1531 mPeekAnimationHandler = new PeekAnimationHandler(mPeekAnimationThread.getLooper()); 1532 1533 mCurrentModule.hardResetSettings(mSettingsManager); 1534 mCurrentModule.resume(); 1535 setSwipingEnabled(true); 1536 1537 if (!mResetToPreviewOnResume) { 1538 LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId()); 1539 if (data != null) { 1540 mDataAdapter.refresh(data.getUri()); 1541 } 1542 } 1543 // The share button might be disabled to avoid double tapping. 1544 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true); 1545 // Default is showing the preview, unless disabled by explicitly 1546 // starting an activity we want to return from to the filmstrip rather 1547 // than the preview. 1548 mResetToPreviewOnResume = true; 1549 1550 if (mLocalVideosObserver.isMediaDataChangedDuringPause() 1551 || mLocalImagesObserver.isMediaDataChangedDuringPause()) { 1552 if (!mSecureCamera) { 1553 // If it's secure camera, requestLoad() should not be called 1554 // as it will load all the data. 1555 if (!mFilmstripVisible) { 1556 mDataAdapter.requestLoad(new Callback<Void>() { 1557 @Override 1558 public void onCallback(Void result) { 1559 fillTemporarySessions(); 1560 } 1561 }); 1562 } else { 1563 mDataAdapter.requestLoadNewPhotos(); 1564 } 1565 } 1566 } 1567 mLocalImagesObserver.setActivityPaused(false); 1568 mLocalVideosObserver.setActivityPaused(false); 1569 if (!mSecureCamera) { 1570 mLocalImagesObserver.setForegroundChangeListener( 1571 new LocalMediaObserver.ChangeListener() { 1572 @Override 1573 public void onChange() { 1574 mDataAdapter.requestLoadNewPhotos(); 1575 } 1576 }); 1577 } 1578 1579 keepScreenOnForAWhile(); 1580 1581 mPanoramaViewHelper.onResume(); 1582 ReleaseDialogHelper.showReleaseInfoDialogOnStart(this, mSettingsManager); 1583 syncLocationManagerSetting(); 1584 1585 final int previewVisibility = getPreviewVisibility(); 1586 updatePreviewRendering(previewVisibility); 1587 } 1588 1589 private void fillTemporarySessions() { 1590 if (mSecureCamera) { 1591 return; 1592 } 1593 // There might be sessions still in flight (processed by our service). 1594 // Make sure they're added to the filmstrip. 1595 getServices().getCaptureSessionManager().fillTemporarySession(mSessionListener); 1596 } 1597 1598 @Override 1599 public void onStart() { 1600 super.onStart(); 1601 mPanoramaViewHelper.onStart(); 1602 1603 /* 1604 * If we're starting after launching a different Activity (lockscreen), 1605 * we need to use the last mode used in the other Activity, and 1606 * not the old one from this Activity. 1607 * 1608 * This needs to happen before CameraAppUI.resume() in order to set the 1609 * mode cover icon to the actual last mode used. 1610 * 1611 * Right now we exclude capture intents from this logic. 1612 */ 1613 int modeIndex = getModeIndex(); 1614 if (!isCaptureIntent() && mCurrentModeIndex != modeIndex) { 1615 onModeSelected(modeIndex); 1616 } 1617 1618 if (mResetToPreviewOnResume) { 1619 mCameraAppUI.resume(); 1620 mResetToPreviewOnResume = false; 1621 } 1622 } 1623 1624 @Override 1625 protected void onStop() { 1626 mPanoramaViewHelper.onStop(); 1627 if (mFeedbackHelper != null) { 1628 mFeedbackHelper.stopFeedback(); 1629 } 1630 1631 mLocationManager.disconnect(); 1632 super.onStop(); 1633 } 1634 1635 @Override 1636 public void onDestroy() { 1637 if (mSecureCamera) { 1638 unregisterReceiver(mScreenOffReceiver); 1639 } 1640 mActionBar.removeOnMenuVisibilityListener(this); 1641 mSettingsManager.removeAllListeners(); 1642 mCameraController.removeCallbackReceiver(); 1643 getContentResolver().unregisterContentObserver(mLocalImagesObserver); 1644 getContentResolver().unregisterContentObserver(mLocalVideosObserver); 1645 getServices().getCaptureSessionManager().removeSessionListener(mSessionListener); 1646 mCameraAppUI.onDestroy(); 1647 mModeListView.setVisibilityChangedListener(null); 1648 mCameraController = null; 1649 mSettingsManager = null; 1650 mCameraAppUI = null; 1651 mOrientationManager = null; 1652 mButtonManager = null; 1653 CameraManagerFactory.recycle(); 1654 super.onDestroy(); 1655 } 1656 1657 @Override 1658 public void onConfigurationChanged(Configuration config) { 1659 super.onConfigurationChanged(config); 1660 Log.v(TAG, "onConfigurationChanged"); 1661 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) { 1662 return; 1663 } 1664 1665 if (mLastLayoutOrientation != config.orientation) { 1666 mLastLayoutOrientation = config.orientation; 1667 mCurrentModule.onLayoutOrientationChanged( 1668 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE); 1669 } 1670 } 1671 1672 @Override 1673 public boolean onKeyDown(int keyCode, KeyEvent event) { 1674 if (!mFilmstripVisible) { 1675 if (mCurrentModule.onKeyDown(keyCode, event)) { 1676 return true; 1677 } 1678 // Prevent software keyboard or voice search from showing up. 1679 if (keyCode == KeyEvent.KEYCODE_SEARCH 1680 || keyCode == KeyEvent.KEYCODE_MENU) { 1681 if (event.isLongPress()) { 1682 return true; 1683 } 1684 } 1685 } 1686 1687 return super.onKeyDown(keyCode, event); 1688 } 1689 1690 @Override 1691 public boolean onKeyUp(int keyCode, KeyEvent event) { 1692 if (!mFilmstripVisible) { 1693 // If a module is in the middle of capture, it should 1694 // consume the key event. 1695 if (mCurrentModule.onKeyUp(keyCode, event)) { 1696 return true; 1697 } else if (keyCode == KeyEvent.KEYCODE_MENU 1698 || keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { 1699 // Let the mode list view consume the event. 1700 mModeListView.onMenuPressed(); 1701 return true; 1702 } else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 1703 mCameraAppUI.showFilmstrip(); 1704 return true; 1705 } 1706 } else { 1707 if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) { 1708 mFilmstripController.goToNextItem(); 1709 return true; 1710 } else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) { 1711 boolean wentToPrevious = mFilmstripController.goToPreviousItem(); 1712 if (!wentToPrevious) { 1713 // at beginning of filmstrip, hide and go back to preview 1714 mCameraAppUI.hideFilmstrip(); 1715 } 1716 return true; 1717 } 1718 } 1719 return super.onKeyUp(keyCode, event); 1720 } 1721 1722 @Override 1723 public void onBackPressed() { 1724 if (!mCameraAppUI.onBackPressed()) { 1725 if (!mCurrentModule.onBackPressed()) { 1726 super.onBackPressed(); 1727 } 1728 } 1729 } 1730 1731 @Override 1732 public boolean isAutoRotateScreen() { 1733 // TODO: Move to OrientationManager. 1734 return mAutoRotateScreen; 1735 } 1736 1737 @Override 1738 public boolean onCreateOptionsMenu(Menu menu) { 1739 MenuInflater inflater = getMenuInflater(); 1740 inflater.inflate(R.menu.filmstrip_menu, menu); 1741 mActionBarMenu = menu; 1742 return super.onCreateOptionsMenu(menu); 1743 } 1744 1745 protected void updateStorageSpace() { 1746 mStorageSpaceBytes = Storage.getAvailableSpace(); 1747 } 1748 1749 protected long getStorageSpaceBytes() { 1750 return mStorageSpaceBytes; 1751 } 1752 1753 protected void updateStorageSpaceAndHint() { 1754 updateStorageSpace(); 1755 updateStorageHint(mStorageSpaceBytes); 1756 } 1757 1758 protected void updateStorageHint(long storageSpace) { 1759 String message = null; 1760 if (storageSpace == Storage.UNAVAILABLE) { 1761 message = getString(R.string.no_storage); 1762 } else if (storageSpace == Storage.PREPARING) { 1763 message = getString(R.string.preparing_sd); 1764 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 1765 message = getString(R.string.access_sd_fail); 1766 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1767 message = getString(R.string.spaceIsLow_content); 1768 } 1769 1770 if (message != null) { 1771 if (mStorageHint == null) { 1772 mStorageHint = OnScreenHint.makeText(mAppContext, message); 1773 } else { 1774 mStorageHint.setText(message); 1775 } 1776 mStorageHint.show(); 1777 } else if (mStorageHint != null) { 1778 mStorageHint.cancel(); 1779 mStorageHint = null; 1780 } 1781 } 1782 1783 protected void setResultEx(int resultCode) { 1784 mResultCodeForTesting = resultCode; 1785 setResult(resultCode); 1786 } 1787 1788 protected void setResultEx(int resultCode, Intent data) { 1789 mResultCodeForTesting = resultCode; 1790 mResultDataForTesting = data; 1791 setResult(resultCode, data); 1792 } 1793 1794 public int getResultCode() { 1795 return mResultCodeForTesting; 1796 } 1797 1798 public Intent getResultData() { 1799 return mResultDataForTesting; 1800 } 1801 1802 public boolean isSecureCamera() { 1803 return mSecureCamera; 1804 } 1805 1806 @Override 1807 public boolean isPaused() { 1808 return mPaused; 1809 } 1810 1811 @Override 1812 public int getPreferredChildModeIndex(int modeIndex) { 1813 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) { 1814 boolean hdrPlusOn = mSettingsManager.isHdrPlusOn(); 1815 if (hdrPlusOn && GcamHelper.hasGcamCapture()) { 1816 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1817 } 1818 } 1819 return modeIndex; 1820 } 1821 1822 @Override 1823 public void onModeSelected(int modeIndex) { 1824 if (mCurrentModeIndex == modeIndex) { 1825 return; 1826 } 1827 1828 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START); 1829 // Record last used camera mode for quick switching 1830 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo) 1831 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) { 1832 mSettingsManager.setInt(SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX, 1833 modeIndex); 1834 } 1835 1836 closeModule(mCurrentModule); 1837 int oldModuleIndex = mCurrentModeIndex; 1838 1839 // Select the correct module index from the mode switcher index. 1840 modeIndex = getPreferredChildModeIndex(modeIndex); 1841 setModuleFromModeIndex(modeIndex); 1842 1843 mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex); 1844 mCameraAppUI.addShutterListener(mCurrentModule); 1845 openModule(mCurrentModule); 1846 mCurrentModule.onOrientationChanged(mLastRawOrientation); 1847 // Store the module index so we can use it the next time the Camera 1848 // starts up. 1849 mSettingsManager.setInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX, modeIndex); 1850 } 1851 1852 /** 1853 * Shows the settings dialog. 1854 */ 1855 @Override 1856 public void onSettingsSelected() { 1857 Intent intent = new Intent(this, CameraSettingsActivity.class); 1858 startActivity(intent); 1859 } 1860 1861 /** 1862 * Sets the mCurrentModuleIndex, creates a new module instance for the given 1863 * index an sets it as mCurrentModule. 1864 */ 1865 private void setModuleFromModeIndex(int modeIndex) { 1866 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex); 1867 if (agent == null) { 1868 return; 1869 } 1870 if (!agent.requestAppForCamera()) { 1871 mCameraController.closeCamera(); 1872 } 1873 mCurrentModeIndex = agent.getModuleId(); 1874 mCurrentModule = (CameraModule) agent.createModule(this); 1875 } 1876 1877 @Override 1878 public SettingsManager getSettingsManager() { 1879 return mSettingsManager; 1880 } 1881 1882 @Override 1883 public CameraServices getServices() { 1884 return (CameraServices) getApplication(); 1885 } 1886 1887 public List<String> getSupportedModeNames() { 1888 List<Integer> indices = mModuleManager.getSupportedModeIndexList(); 1889 List<String> supported = new ArrayList<String>(); 1890 1891 for (Integer modeIndex : indices) { 1892 String name = CameraUtil.getCameraModeText(modeIndex, mAppContext); 1893 if (name != null && !name.equals("")) { 1894 supported.add(name); 1895 } 1896 } 1897 return supported; 1898 } 1899 1900 @Override 1901 public ButtonManager getButtonManager() { 1902 if (mButtonManager == null) { 1903 mButtonManager = new ButtonManager(this); 1904 } 1905 return mButtonManager; 1906 } 1907 1908 /** 1909 * Creates an AlertDialog appropriate for choosing whether to enable 1910 * location on the first run of the app. 1911 */ 1912 public AlertDialog getFirstTimeLocationAlert() { 1913 AlertDialog.Builder builder = new AlertDialog.Builder(this); 1914 builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() { 1915 @Override 1916 public void onCallback(Boolean locationOn) { 1917 mSettingsManager.setLocation(locationOn, mLocationManager); 1918 } 1919 }); 1920 if (builder != null) { 1921 return builder.create(); 1922 } else { 1923 return null; 1924 } 1925 } 1926 1927 /** 1928 * Launches an ACTION_EDIT intent for the given local data item. If 1929 * 'withTinyPlanet' is set, this will show a disambig dialog first to let 1930 * the user start either the tiny planet editor or another photo edior. 1931 * 1932 * @param data The data item to edit. 1933 */ 1934 public void launchEditor(LocalData data) { 1935 Intent intent = new Intent(Intent.ACTION_EDIT) 1936 .setDataAndType(data.getUri(), data.getMimeType()) 1937 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1938 try { 1939 launchActivityByIntent(intent); 1940 } catch (ActivityNotFoundException e) { 1941 launchActivityByIntent(Intent.createChooser(intent, null)); 1942 } 1943 } 1944 1945 @Override 1946 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 1947 super.onCreateContextMenu(menu, v, menuInfo); 1948 1949 MenuInflater inflater = getMenuInflater(); 1950 inflater.inflate(R.menu.filmstrip_context_menu, menu); 1951 } 1952 1953 @Override 1954 public boolean onContextItemSelected(MenuItem item) { 1955 switch (item.getItemId()) { 1956 case R.id.tiny_planet_editor: 1957 mMyFilmstripBottomControlListener.onTinyPlanet(); 1958 return true; 1959 case R.id.photo_editor: 1960 mMyFilmstripBottomControlListener.onEdit(); 1961 return true; 1962 } 1963 return false; 1964 } 1965 1966 /** 1967 * Launch the tiny planet editor. 1968 * 1969 * @param data The data must be a 360 degree stereographically mapped 1970 * panoramic image. It will not be modified, instead a new item 1971 * with the result will be added to the filmstrip. 1972 */ 1973 public void launchTinyPlanetEditor(LocalData data) { 1974 TinyPlanetFragment fragment = new TinyPlanetFragment(); 1975 Bundle bundle = new Bundle(); 1976 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getUri().toString()); 1977 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle()); 1978 fragment.setArguments(bundle); 1979 fragment.show(getFragmentManager(), "tiny_planet"); 1980 } 1981 1982 private void openModule(CameraModule module) { 1983 module.init(this, isSecureCamera(), isCaptureIntent()); 1984 module.hardResetSettings(mSettingsManager); 1985 module.resume(); 1986 updatePreviewVisibility(); 1987 } 1988 1989 private void closeModule(CameraModule module) { 1990 module.pause(); 1991 mCameraAppUI.clearModuleUI(); 1992 } 1993 1994 private void performDeletion() { 1995 if (!mPendingDeletion) { 1996 return; 1997 } 1998 hideUndoDeletionBar(false); 1999 mDataAdapter.executeDeletion(); 2000 } 2001 2002 public void showUndoDeletionBar() { 2003 if (mPendingDeletion) { 2004 performDeletion(); 2005 } 2006 Log.v(TAG, "showing undo bar"); 2007 mPendingDeletion = true; 2008 if (mUndoDeletionBar == null) { 2009 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar, 2010 mAboveFilmstripControlLayout, true); 2011 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 2012 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 2013 button.setOnClickListener(new View.OnClickListener() { 2014 @Override 2015 public void onClick(View view) { 2016 mDataAdapter.undoDataRemoval(); 2017 hideUndoDeletionBar(true); 2018 } 2019 }); 2020 // Setting undo bar clickable to avoid touch events going through 2021 // the bar to the buttons (eg. edit button, etc) underneath the bar. 2022 mUndoDeletionBar.setClickable(true); 2023 // When there is user interaction going on with the undo button, we 2024 // do not want to hide the undo bar. 2025 button.setOnTouchListener(new View.OnTouchListener() { 2026 @Override 2027 public boolean onTouch(View v, MotionEvent event) { 2028 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 2029 mIsUndoingDeletion = true; 2030 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { 2031 mIsUndoingDeletion = false; 2032 } 2033 return false; 2034 } 2035 }); 2036 } 2037 mUndoDeletionBar.setAlpha(0f); 2038 mUndoDeletionBar.setVisibility(View.VISIBLE); 2039 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start(); 2040 } 2041 2042 private void hideUndoDeletionBar(boolean withAnimation) { 2043 Log.v(TAG, "Hiding undo deletion bar"); 2044 mPendingDeletion = false; 2045 if (mUndoDeletionBar != null) { 2046 if (withAnimation) { 2047 mUndoDeletionBar.animate().setDuration(200).alpha(0f) 2048 .setListener(new Animator.AnimatorListener() { 2049 @Override 2050 public void onAnimationStart(Animator animation) { 2051 // Do nothing. 2052 } 2053 2054 @Override 2055 public void onAnimationEnd(Animator animation) { 2056 mUndoDeletionBar.setVisibility(View.GONE); 2057 } 2058 2059 @Override 2060 public void onAnimationCancel(Animator animation) { 2061 // Do nothing. 2062 } 2063 2064 @Override 2065 public void onAnimationRepeat(Animator animation) { 2066 // Do nothing. 2067 } 2068 }).start(); 2069 } else { 2070 mUndoDeletionBar.setVisibility(View.GONE); 2071 } 2072 } 2073 } 2074 2075 @Override 2076 public void onOrientationChanged(int orientation) { 2077 // We keep the last known orientation. So if the user first orient 2078 // the camera then point the camera to floor or sky, we still have 2079 // the correct orientation. 2080 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) { 2081 return; 2082 } 2083 mLastRawOrientation = orientation; 2084 if (mCurrentModule != null) { 2085 mCurrentModule.onOrientationChanged(orientation); 2086 } 2087 } 2088 2089 /** 2090 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in 2091 * capture intent. 2092 * 2093 * @param enable {@code true} to enable swipe. 2094 */ 2095 public void setSwipingEnabled(boolean enable) { 2096 // TODO: Bring back the functionality. 2097 if (isCaptureIntent()) { 2098 // lockPreview(true); 2099 } else { 2100 // lockPreview(!enable); 2101 } 2102 } 2103 2104 // Accessor methods for getting latency times used in performance testing 2105 public long getFirstPreviewTime() { 2106 if (mCurrentModule instanceof PhotoModule) { 2107 long coverHiddenTime = getCameraAppUI().getCoverHiddenTime(); 2108 if (coverHiddenTime != -1) { 2109 return coverHiddenTime - mOnCreateTime; 2110 } 2111 } 2112 return -1; 2113 } 2114 2115 public long getAutoFocusTime() { 2116 return (mCurrentModule instanceof PhotoModule) ? 2117 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 2118 } 2119 2120 public long getShutterLag() { 2121 return (mCurrentModule instanceof PhotoModule) ? 2122 ((PhotoModule) mCurrentModule).mShutterLag : -1; 2123 } 2124 2125 public long getShutterToPictureDisplayedTime() { 2126 return (mCurrentModule instanceof PhotoModule) ? 2127 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 2128 } 2129 2130 public long getPictureDisplayedToJpegCallbackTime() { 2131 return (mCurrentModule instanceof PhotoModule) ? 2132 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 2133 } 2134 2135 public long getJpegCallbackFinishTime() { 2136 return (mCurrentModule instanceof PhotoModule) ? 2137 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 2138 } 2139 2140 public long getCaptureStartTime() { 2141 return (mCurrentModule instanceof PhotoModule) ? 2142 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 2143 } 2144 2145 public boolean isRecording() { 2146 return (mCurrentModule instanceof VideoModule) ? 2147 ((VideoModule) mCurrentModule).isRecording() : false; 2148 } 2149 2150 public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() { 2151 return mCameraController; 2152 } 2153 2154 // For debugging purposes only. 2155 public CameraModule getCurrentModule() { 2156 return mCurrentModule; 2157 } 2158 2159 @Override 2160 public void showTutorial(AbstractTutorialOverlay tutorial) { 2161 mCameraAppUI.showTutorial(tutorial, getLayoutInflater()); 2162 } 2163 2164 /** 2165 * Reads the current location recording settings and passes it on to the 2166 * location manager. 2167 */ 2168 public void syncLocationManagerSetting() { 2169 mSettingsManager.syncLocationManager(mLocationManager); 2170 } 2171 2172 private void keepScreenOnForAWhile() { 2173 if (mKeepScreenOn) { 2174 return; 2175 } 2176 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 2177 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2178 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS); 2179 } 2180 2181 private void resetScreenOn() { 2182 mKeepScreenOn = false; 2183 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 2184 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 2185 } 2186 2187 /** 2188 * @return {@code true} if the Gallery is launched successfully. 2189 */ 2190 private boolean startGallery() { 2191 if (mGalleryIntent == null) { 2192 return false; 2193 } 2194 try { 2195 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, InteractionCause.BUTTON); 2196 Intent startGalleryIntent = new Intent(mGalleryIntent); 2197 int currentDataId = mFilmstripController.getCurrentId(); 2198 LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId); 2199 if (currentLocalData != null) { 2200 GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getUri()); 2201 } 2202 launchActivityByIntent(startGalleryIntent); 2203 } catch (ActivityNotFoundException e) { 2204 Log.w(TAG, "Failed to launch gallery activity, closing"); 2205 } 2206 return false; 2207 } 2208 2209 private void setNfcBeamPushUriFromData(LocalData data) { 2210 final Uri uri = data.getUri(); 2211 if (uri != Uri.EMPTY) { 2212 mNfcPushUris[0] = uri; 2213 } else { 2214 mNfcPushUris[0] = null; 2215 } 2216 } 2217 2218 /** 2219 * Updates the visibility of the filmstrip bottom controls and action bar. 2220 */ 2221 private void updateUiByData(final int dataId) { 2222 final LocalData currentData = mDataAdapter.getLocalData(dataId); 2223 if (currentData == null) { 2224 Log.w(TAG, "Current data ID not found."); 2225 hideSessionProgress(); 2226 return; 2227 } 2228 updateActionBarMenu(currentData); 2229 2230 /* Bottom controls. */ 2231 updateBottomControlsByData(currentData); 2232 2233 if (isSecureCamera()) { 2234 // We cannot show buttons in secure camera since go to other 2235 // activities might create a security hole. 2236 mCameraAppUI.getFilmstripBottomControls().hideControls(); 2237 return; 2238 } 2239 2240 2241 setNfcBeamPushUriFromData(currentData); 2242 2243 if (!mDataAdapter.isMetadataUpdated(dataId)) { 2244 mDataAdapter.updateMetadata(dataId); 2245 } 2246 } 2247 2248 /** 2249 * Updates the bottom controls based on the data. 2250 */ 2251 private void updateBottomControlsByData(final LocalData currentData) { 2252 2253 final CameraAppUI.BottomPanel filmstripBottomPanel = 2254 mCameraAppUI.getFilmstripBottomControls(); 2255 filmstripBottomPanel.showControls(); 2256 filmstripBottomPanel.setEditButtonVisibility( 2257 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT)); 2258 filmstripBottomPanel.setShareButtonVisibility( 2259 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE)); 2260 filmstripBottomPanel.setDeleteButtonVisibility( 2261 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE)); 2262 2263 /* Progress bar */ 2264 2265 Uri contentUri = currentData.getUri(); 2266 CaptureSessionManager sessionManager = getServices() 2267 .getCaptureSessionManager(); 2268 2269 if (sessionManager.hasErrorMessage(contentUri)) { 2270 showProcessError(sessionManager.getErrorMesage(contentUri)); 2271 } else { 2272 filmstripBottomPanel.hideProgressError(); 2273 CaptureSession session = sessionManager.getSession(contentUri); 2274 2275 if (session != null) { 2276 int sessionProgress = session.getProgress(); 2277 2278 if (sessionProgress < 0) { 2279 hideSessionProgress(); 2280 } else { 2281 CharSequence progressMessage = session.getProgressMessage(); 2282 showSessionProgress(progressMessage); 2283 updateSessionProgress(sessionProgress); 2284 } 2285 } else { 2286 hideSessionProgress(); 2287 } 2288 } 2289 2290 /* View button */ 2291 2292 // We need to add this to a separate DB. 2293 final int viewButtonVisibility; 2294 if (PanoramaMetadataLoader.isPanoramaAndUseViewer(currentData)) { 2295 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_PHOTO_SPHERE; 2296 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) { 2297 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_REFOCUS; 2298 } else { 2299 viewButtonVisibility = CameraAppUI.BottomPanel.VIEWER_NONE; 2300 } 2301 2302 filmstripBottomPanel.setTinyPlanetEnabled( 2303 PanoramaMetadataLoader.isPanorama360(currentData)); 2304 filmstripBottomPanel.setViewerButtonVisibility(viewButtonVisibility); 2305 } 2306 2307 private class PeekAnimationHandler extends Handler { 2308 private class DataAndCallback { 2309 LocalData mData; 2310 com.android.camera.util.Callback<Bitmap> mCallback; 2311 2312 public DataAndCallback(LocalData data, com.android.camera.util.Callback<Bitmap> 2313 callback) { 2314 mData = data; 2315 mCallback = callback; 2316 } 2317 } 2318 2319 public PeekAnimationHandler(Looper looper) { 2320 super(looper); 2321 } 2322 2323 /** 2324 * Starts the animation decoding job and posts a {@code Runnable} back 2325 * when when the decoding is done. 2326 * 2327 * @param data The data item to decode the thumbnail for. 2328 * @param callback {@link com.android.camera.util.Callback} after the 2329 * decoding is done. 2330 */ 2331 public void startDecodingJob(final LocalData data, 2332 final com.android.camera.util.Callback<Bitmap> callback) { 2333 PeekAnimationHandler.this.obtainMessage(0 /** dummy integer **/, 2334 new DataAndCallback(data, callback)).sendToTarget(); 2335 } 2336 2337 @Override 2338 public void handleMessage(Message msg) { 2339 final LocalData data = ((DataAndCallback) msg.obj).mData; 2340 final com.android.camera.util.Callback<Bitmap> callback = 2341 ((DataAndCallback) msg.obj).mCallback; 2342 if (data == null || callback == null) { 2343 return; 2344 } 2345 2346 final Bitmap bitmap; 2347 switch (data.getLocalDataType()) { 2348 case LocalData.LOCAL_IN_PROGRESS_DATA: 2349 byte[] jpegData = Storage.getJpegForSession(data.getUri()); 2350 if (jpegData != null) { 2351 bitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); 2352 } else { 2353 bitmap = null; 2354 } 2355 break; 2356 2357 case LocalData.LOCAL_IMAGE: 2358 FileInputStream stream; 2359 try { 2360 stream = new FileInputStream(data.getPath()); 2361 } catch (FileNotFoundException e) { 2362 Log.e(TAG, "File not found:" + data.getPath()); 2363 return; 2364 } 2365 Point dim = CameraUtil.resizeToFill(data.getWidth(), data.getHeight(), 2366 data.getRotation(), mAboveFilmstripControlLayout.getWidth(), 2367 mAboveFilmstripControlLayout.getMeasuredHeight()); 2368 if (data.getRotation() % 180 != 0) { 2369 int dummy = dim.x; 2370 dim.x = dim.y; 2371 dim.y = dummy; 2372 } 2373 bitmap = LocalDataUtil 2374 .loadImageThumbnailFromStream(stream, data.getWidth(), data.getHeight(), 2375 (int) (dim.x * 0.7f), (int) (dim.y * 0.7), 2376 data.getRotation(), MAX_PEEK_BITMAP_PIXELS); 2377 break; 2378 2379 case LocalData.LOCAL_VIDEO: 2380 bitmap = LocalDataUtil.loadVideoThumbnail(data.getPath()); 2381 break; 2382 2383 default: 2384 bitmap = null; 2385 break; 2386 } 2387 2388 if (bitmap == null) { 2389 return; 2390 } 2391 2392 mMainHandler.post(new Runnable() { 2393 @Override 2394 public void run() { 2395 callback.onCallback(bitmap); 2396 } 2397 }); 2398 } 2399 } 2400 2401 private void showDetailsDialog(int dataId) { 2402 final LocalData data = mDataAdapter.getLocalData(dataId); 2403 if (data == null) { 2404 return; 2405 } 2406 MediaDetails details = data.getMediaDetails(getAndroidContext()); 2407 if (details == null) { 2408 return; 2409 } 2410 Dialog detailDialog = DetailsDialog.create(CameraActivity.this, details); 2411 detailDialog.show(); 2412 2413 UsageStatistics.photoInteraction( 2414 UsageStatistics.hashFileName(fileNameFromDataID(dataId)), 2415 eventprotos.CameraEvent.InteractionType.DETAILS, 2416 InteractionCause.BUTTON); 2417 } 2418 2419 /** 2420 * Show or hide action bar items depending on current data type. 2421 */ 2422 private void updateActionBarMenu(LocalData data) { 2423 if (mActionBarMenu == null) { 2424 return; 2425 } 2426 2427 MenuItem detailsMenuItem = mActionBarMenu.findItem(R.id.action_details); 2428 if (detailsMenuItem == null) { 2429 return; 2430 } 2431 2432 int type = data.getLocalDataType(); 2433 boolean showDetails = (type == LocalData.LOCAL_IMAGE) || (type == LocalData.LOCAL_VIDEO); 2434 detailsMenuItem.setVisible(showDetails); 2435 } 2436} 2437