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