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