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