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