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