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