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