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