CameraActivity.java revision c893312b7639d1e9c9c73e29bbb4470e8b8147e3
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.content.ActivityNotFoundException; 25import android.content.BroadcastReceiver; 26import android.content.ContentResolver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.SharedPreferences; 31import android.content.pm.ActivityInfo; 32import android.content.pm.PackageManager; 33import android.content.res.Configuration; 34import android.graphics.Bitmap; 35import android.graphics.Matrix; 36import android.graphics.SurfaceTexture; 37import android.graphics.drawable.ColorDrawable; 38import android.graphics.drawable.Drawable; 39import android.net.Uri; 40import android.nfc.NfcAdapter; 41import android.nfc.NfcAdapter.CreateBeamUrisCallback; 42import android.nfc.NfcEvent; 43import android.os.Build; 44import android.os.Bundle; 45import android.os.Handler; 46import android.os.Looper; 47import android.os.Message; 48import android.preference.PreferenceManager; 49import android.provider.MediaStore; 50import android.provider.Settings; 51import android.util.CameraPerformanceTracker; 52import android.util.Log; 53import android.view.ContextMenu; 54import android.view.ContextMenu.ContextMenuInfo; 55import android.view.KeyEvent; 56import android.view.MenuInflater; 57import android.view.MenuItem; 58import android.view.MotionEvent; 59import android.view.View; 60import android.view.ViewGroup; 61import android.view.Window; 62import android.view.WindowManager; 63import android.widget.FrameLayout; 64import android.widget.ImageView; 65import android.widget.ProgressBar; 66import android.widget.ShareActionProvider; 67import android.widget.TextView; 68 69import com.android.camera.app.AppController; 70import com.android.camera.app.CameraAppUI; 71import com.android.camera.app.CameraController; 72import com.android.camera.app.CameraManager; 73import com.android.camera.app.CameraManagerFactory; 74import com.android.camera.app.CameraProvider; 75import com.android.camera.app.CameraServices; 76import com.android.camera.app.LocationManager; 77import com.android.camera.app.ModuleManagerImpl; 78import com.android.camera.app.OrientationManager; 79import com.android.camera.app.OrientationManagerImpl; 80import com.android.camera.data.CameraDataAdapter; 81import com.android.camera.data.FixedLastDataAdapter; 82import com.android.camera.data.InProgressDataWrapper; 83import com.android.camera.data.LocalData; 84import com.android.camera.data.LocalDataAdapter; 85import com.android.camera.data.LocalMediaObserver; 86import com.android.camera.data.PanoramaMetadataLoader; 87import com.android.camera.data.RgbzMetadataLoader; 88import com.android.camera.data.SimpleViewData; 89import com.android.camera.filmstrip.FilmstripContentPanel; 90import com.android.camera.filmstrip.FilmstripController; 91import com.android.camera.module.ModuleController; 92import com.android.camera.module.ModulesInfo; 93import com.android.camera.session.CaptureSessionManager; 94import com.android.camera.session.CaptureSessionManager.SessionListener; 95import com.android.camera.session.PlaceholderManager; 96import com.android.camera.settings.CameraSettingsActivity; 97import com.android.camera.settings.SettingsManager; 98import com.android.camera.settings.SettingsManager.SettingsCapabilities; 99import com.android.camera.settings.SettingsUtil; 100import com.android.camera.tinyplanet.TinyPlanetFragment; 101import com.android.camera.ui.MainActivityLayout; 102import com.android.camera.ui.ModeListView; 103import com.android.camera.ui.PreviewStatusListener; 104import com.android.camera.util.ApiHelper; 105import com.android.camera.util.Callback; 106import com.android.camera.util.CameraUtil; 107import com.android.camera.util.FeedbackHelper; 108import com.android.camera.util.GalleryHelper; 109import com.android.camera.util.GcamHelper; 110import com.android.camera.util.IntentHelper; 111import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 112import com.android.camera.util.UsageStatistics; 113import com.android.camera.widget.FilmstripView; 114import com.android.camera2.R; 115import com.google.common.logging.eventprotos; 116import com.google.common.logging.eventprotos.CameraEvent.InteractionCause; 117import com.google.common.logging.eventprotos.NavigationChange; 118 119import java.io.File; 120import java.lang.ref.WeakReference; 121import java.util.ArrayList; 122import java.util.List; 123 124public class CameraActivity extends Activity 125 implements AppController, CameraManager.CameraOpenCallback, 126 ActionBar.OnMenuVisibilityListener, ShareActionProvider.OnShareTargetSelectedListener, 127 OrientationManager.OnOrientationChangeListener { 128 129 private static final String TAG = "CameraActivity"; 130 131 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 132 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 133 public static final String ACTION_IMAGE_CAPTURE_SECURE = 134 "android.media.action.IMAGE_CAPTURE_SECURE"; 135 136 // The intent extra for camera from secure lock screen. True if the gallery 137 // should only show newly captured pictures. sSecureAlbumId does not 138 // increment. This is used when switching between camera, camcorder, and 139 // panorama. If the extra is not set, it is in the normal camera mode. 140 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 141 142 /** 143 * Request code from an activity we started that indicated that we do not 144 * want to reset the view to the preview in onResume. 145 */ 146 public static final int REQ_CODE_DONT_SWITCH_TO_PREVIEW = 142; 147 148 public static final int REQ_CODE_GCAM_DEBUG_POSTCAPTURE = 999; 149 150 private static final int MSG_HIDE_ACTION_BAR = 1; 151 private static final int MSG_CLEAR_SCREEN_ON_FLAG = 2; 152 private static final long SCREEN_DELAY_MS = 2 * 60 * 1000; // 2 mins. 153 154 /** Should be used wherever a context is needed. */ 155 private Context mAppContext; 156 157 /** 158 * Whether onResume should reset the view to the preview. 159 */ 160 private boolean mResetToPreviewOnResume = true; 161 162 /** 163 * This data adapter is used by FilmStripView. 164 */ 165 private LocalDataAdapter mDataAdapter; 166 167 /** 168 * TODO: This should be moved to the app level. 169 */ 170 private SettingsManager mSettingsManager; 171 172 private ModeListView mModeListView; 173 private int mCurrentModeIndex; 174 private CameraModule mCurrentModule; 175 private ModuleManagerImpl mModuleManager; 176 private FrameLayout mAboveFilmstripControlLayout; 177 private FilmstripController mFilmstripController; 178 private boolean mFilmstripVisible; 179 private TextView mBottomProgressText; 180 private ProgressBar mBottomProgressBar; 181 private View mSessionProgressPanel; 182 private int mResultCodeForTesting; 183 private Intent mResultDataForTesting; 184 private OnScreenHint mStorageHint; 185 private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES; 186 private boolean mAutoRotateScreen; 187 private boolean mSecureCamera; 188 private int mLastRawOrientation; 189 private OrientationManagerImpl mOrientationManager; 190 private LocationManager mLocationManager; 191 private ButtonManager mButtonManager; 192 private Handler mMainHandler; 193 private PanoramaViewHelper mPanoramaViewHelper; 194 private ActionBar mActionBar; 195 private ViewGroup mUndoDeletionBar; 196 private boolean mIsUndoingDeletion = false; 197 198 private final Uri[] mNfcPushUris = new Uri[1]; 199 200 private LocalMediaObserver mLocalImagesObserver; 201 private LocalMediaObserver mLocalVideosObserver; 202 203 private boolean mPendingDeletion = false; 204 205 private CameraController mCameraController; 206 private boolean mPaused; 207 private boolean mUpAsGallery; 208 private CameraAppUI mCameraAppUI; 209 210 private FeedbackHelper mFeedbackHelper; 211 212 private Intent mGalleryIntent; 213 private long mOnCreateTime; 214 215 @Override 216 public CameraAppUI getCameraAppUI() { 217 return mCameraAppUI; 218 } 219 220 // close activity when screen turns off 221 private final BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 222 @Override 223 public void onReceive(Context context, Intent intent) { 224 finish(); 225 } 226 }; 227 228 /** 229 * Whether the screen is kept turned on. 230 */ 231 private boolean mKeepScreenOn; 232 private int mLastLayoutOrientation; 233 private final CameraAppUI.BottomControls.Listener mMyFilmstripBottomControlListener = 234 new CameraAppUI.BottomControls.Listener() { 235 236 /** 237 * If the current photo is a photo sphere, this will launch the 238 * Photo Sphere panorama viewer. 239 */ 240 @Override 241 public void onExternalViewer() { 242 if (mPanoramaViewHelper == null) { 243 return; 244 } 245 final LocalData data = getCurrentLocalData(); 246 if (data == null) { 247 return; 248 } 249 final Uri contentUri = data.getContentUri(); 250 if (contentUri == Uri.EMPTY) { 251 return; 252 } 253 254 if (PanoramaMetadataLoader.isPanorama(data)) { 255 mPanoramaViewHelper.showPanorama(contentUri); 256 } else if (RgbzMetadataLoader.hasRGBZData(data)) { 257 mPanoramaViewHelper.showRgbz(contentUri); 258 } 259 } 260 261 @Override 262 public void onEdit() { 263 LocalData data = getCurrentLocalData(); 264 if (data == null) { 265 return; 266 } 267 launchEditor(data); 268 } 269 270 @Override 271 public void onTinyPlanet() { 272 LocalData data = getCurrentLocalData(); 273 if (data == null) { 274 return; 275 } 276 launchTinyPlanetEditor(data); 277 } 278 279 @Override 280 public void onDelete() { 281 final int currentDataId = getCurrentDataId(); 282 UsageStatistics.photoInteraction( 283 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)), 284 eventprotos.CameraEvent.InteractionType.DELETE, 285 InteractionCause.BUTTON); 286 removeData(currentDataId); 287 } 288 289 @Override 290 public void onShare() { 291 final LocalData data = getCurrentLocalData(); 292 Intent shareIntent = getShareIntentByData(data); 293 if (shareIntent != null) { 294 try { 295 launchActivityByIntent(shareIntent); 296 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(false); 297 } catch (ActivityNotFoundException ex) { 298 // Nothing. 299 } 300 } 301 } 302 303 private int getCurrentDataId() { 304 return mFilmstripController.getCurrentId(); 305 } 306 307 private LocalData getCurrentLocalData() { 308 return mDataAdapter.getLocalData(getCurrentDataId()); 309 } 310 311 /** 312 * Sets up the share intent and NFC properly according to the 313 * data. 314 * 315 * @param data The data to be shared. 316 */ 317 private Intent getShareIntentByData(final LocalData data) { 318 Intent intent = null; 319 final Uri contentUri = data.getContentUri(); 320 if (PanoramaMetadataLoader.isPanorama360(data) && 321 data.getContentUri() != Uri.EMPTY) { 322 intent = new Intent(Intent.ACTION_SEND); 323 intent.setType("application/vnd.google.panorama360+jpg"); 324 intent.putExtra(Intent.EXTRA_STREAM, contentUri); 325 } else if (data.isDataActionSupported(LocalData.DATA_ACTION_SHARE)) { 326 final String mimeType = data.getMimeType(); 327 intent = getShareIntentFromType(mimeType); 328 if (intent != null) { 329 intent.putExtra(Intent.EXTRA_STREAM, contentUri); 330 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 331 } 332 } 333 return intent; 334 } 335 336 /** 337 * Get the share intent according to the mimeType 338 * 339 * @param mimeType The mimeType of current data. 340 * @return the video/image's ShareIntent or null if mimeType is 341 * invalid. 342 */ 343 private Intent getShareIntentFromType(String mimeType) { 344 // Lazily create the intent object. 345 Intent intent = new Intent(Intent.ACTION_SEND); 346 if (mimeType.startsWith("video/")) { 347 intent.setType("video/*"); 348 } else { 349 if (mimeType.startsWith("image/")) { 350 intent.setType("image/*"); 351 } else { 352 Log.w(TAG, "unsupported mimeType " + mimeType); 353 } 354 } 355 return intent; 356 } 357 }; 358 359 private ComboPreferences mPreferences; 360 private ContentResolver mContentResolver; 361 362 @Override 363 public void onCameraOpened(CameraManager.CameraProxy camera) { 364 if (!mModuleManager.getModuleAgent(mCurrentModeIndex).requestAppForCamera()) { 365 // We shouldn't be here. Just close the camera and leave. 366 camera.release(false); 367 throw new IllegalStateException("Camera opened but the module shouldn't be " + 368 "requesting"); 369 } 370 if (mCurrentModule != null) { 371 SettingsCapabilities capabilities = 372 SettingsUtil.getSettingsCapabilities(camera); 373 mSettingsManager.changeCamera(camera.getCameraId(), capabilities); 374 mCurrentModule.onCameraAvailable(camera); 375 } 376 mCameraAppUI.onChangeCamera(); 377 } 378 379 @Override 380 public void onCameraDisabled(int cameraId) { 381 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.SECURITY); 382 383 CameraUtil.showErrorAndFinish(this, R.string.camera_disabled); 384 } 385 386 @Override 387 public void onDeviceOpenFailure(int cameraId) { 388 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.OPEN_FAILURE); 389 390 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 391 } 392 393 @Override 394 public void onReconnectionFailure(CameraManager mgr) { 395 UsageStatistics.cameraFailure(eventprotos.CameraFailure.FailureReason.RECONNECT_FAILURE); 396 397 CameraUtil.showErrorAndFinish(this, R.string.cannot_connect_camera); 398 } 399 400 private static class MainHandler extends Handler { 401 final WeakReference<CameraActivity> mActivity; 402 403 public MainHandler(CameraActivity activity, Looper looper) { 404 super(looper); 405 mActivity = new WeakReference<CameraActivity>(activity); 406 } 407 408 @Override 409 public void handleMessage(Message msg) { 410 CameraActivity activity = mActivity.get(); 411 if (activity == null) { 412 return; 413 } 414 switch (msg.what) { 415 case MSG_HIDE_ACTION_BAR: { 416 removeMessages(MSG_HIDE_ACTION_BAR); 417 activity.setFilmstripUiVisibility(false); 418 break; 419 } 420 421 case MSG_CLEAR_SCREEN_ON_FLAG: { 422 if (!activity.mPaused) { 423 activity.getWindow().clearFlags( 424 WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 425 } 426 break; 427 } 428 } 429 } 430 } 431 432 private String fileNameFromDataID(int dataID) { 433 final LocalData localData = mDataAdapter.getLocalData(dataID); 434 435 File localFile = new File(localData.getPath()); 436 return localFile.getName(); 437 } 438 439 private final FilmstripContentPanel.Listener mFilmstripListener = 440 new FilmstripContentPanel.Listener() { 441 442 @Override 443 public void onSwipeOut() { 444 UsageStatistics.changeScreen(eventprotos.NavigationChange.Mode.PHOTO_CAPTURE, 445 eventprotos.CameraEvent.InteractionCause.SWIPE_RIGHT); 446 } 447 448 @Override 449 public void onFilmstripHidden() { 450 mFilmstripVisible = false; 451 CameraActivity.this.setFilmstripUiVisibility(false); 452 // When the user hide the filmstrip (either swipe out or 453 // tap on back key) we move to the first item so next time 454 // when the user swipe in the filmstrip, the most recent 455 // one is shown. 456 mFilmstripController.goToFirstItem(); 457 if (mCurrentModule != null) { 458 mCurrentModule.onPreviewVisibilityChanged(true); 459 } 460 } 461 462 @Override 463 public void onFilmstripShown() { 464 mFilmstripVisible = true; 465 updateUiByData(mFilmstripController.getCurrentId()); 466 if (mCurrentModule != null) { 467 mCurrentModule.onPreviewVisibilityChanged(false); 468 } 469 } 470 471 @Override 472 public void onDataPromoted(int dataID) { 473 UsageStatistics.photoInteraction( 474 UsageStatistics.hashFileName(fileNameFromDataID(dataID)), 475 eventprotos.CameraEvent.InteractionType.DELETE, 476 InteractionCause.SWIPE_UP); 477 478 removeData(dataID); 479 } 480 481 @Override 482 public void onDataDemoted(int dataID) { 483 UsageStatistics.photoInteraction( 484 UsageStatistics.hashFileName(fileNameFromDataID(dataID)), 485 eventprotos.CameraEvent.InteractionType.DELETE, 486 InteractionCause.SWIPE_DOWN); 487 488 removeData(dataID); 489 } 490 491 @Override 492 public void onEnterFullScreenUiShown(int dataId) { 493 if (mFilmstripVisible) { 494 CameraActivity.this.setFilmstripUiVisibility(true); 495 } 496 } 497 498 @Override 499 public void onLeaveFullScreenUiShown(int dataId) { 500 // Do nothing. 501 } 502 503 @Override 504 public void onEnterFullScreenUiHidden(int dataId) { 505 if (mFilmstripVisible) { 506 CameraActivity.this.setFilmstripUiVisibility(false); 507 } 508 } 509 510 @Override 511 public void onLeaveFullScreenUiHidden(int dataId) { 512 // Do nothing. 513 } 514 515 @Override 516 public void onEnterFilmstrip(int dataId) { 517 if (mGalleryIntent != null) { 518 mActionBar.setDisplayUseLogoEnabled(true); 519 mUpAsGallery = true; 520 } 521 if (mFilmstripVisible) { 522 CameraActivity.this.setFilmstripUiVisibility(true); 523 } 524 } 525 526 @Override 527 public void onLeaveFilmstrip(int dataId) { 528 if (mGalleryIntent != null) { 529 mActionBar.setDisplayUseLogoEnabled(false); 530 mUpAsGallery = false; 531 } 532 } 533 534 @Override 535 public void onDataReloaded() { 536 if (!mFilmstripVisible) { 537 return; 538 } 539 updateUiByData(mFilmstripController.getCurrentId()); 540 } 541 542 @Override 543 public void onDataUpdated(int dataId) { 544 if (!mFilmstripVisible) { 545 return; 546 } 547 updateUiByData(mFilmstripController.getCurrentId()); 548 } 549 550 @Override 551 public void onEnterZoomView(int dataID) { 552 if (mFilmstripVisible) { 553 CameraActivity.this.setFilmstripUiVisibility(false); 554 } 555 } 556 557 @Override 558 public void onDataFocusChanged(final int prevDataId, final int newDataId) { 559 if (!mFilmstripVisible) { 560 return; 561 } 562 // TODO: This callback is UI event callback, should always 563 // happen on UI thread. Find the reason for this 564 // runOnUiThread() and fix it. 565 runOnUiThread(new Runnable() { 566 @Override 567 public void run() { 568 updateUiByData(newDataId); 569 } 570 }); 571 } 572 }; 573 574 private final LocalDataAdapter.LocalDataListener mLocalDataListener = 575 new LocalDataAdapter.LocalDataListener() { 576 @Override 577 public void onMetadataUpdated(List<Integer> updatedData) { 578 int currentDataId = mFilmstripController.getCurrentId(); 579 for (Integer dataId : updatedData) { 580 if (dataId == currentDataId) { 581 updateBottomControlsByData(mDataAdapter.getLocalData(dataId)); 582 } 583 } 584 } 585 }; 586 587 public void gotoGallery() { 588 UsageStatistics.changeScreen(NavigationChange.Mode.FILMSTRIP, 589 InteractionCause.BUTTON); 590 591 mFilmstripController.goToNextItem(); 592 } 593 594 /** 595 * If 'visible' is false, this hides the action bar and switches the 596 * filmstrip UI to lights-out mode. 597 * 598 * @param visible is false, this hides the action bar and switches the 599 * filmstrip UI to lights-out mode. 600 */ 601 // TODO: This should not be called outside of the activity. 602 public void setFilmstripUiVisibility(boolean visible) { 603 mMainHandler.removeMessages(MSG_HIDE_ACTION_BAR); 604 605 int currentSystemUIVisibility = mAboveFilmstripControlLayout.getSystemUiVisibility(); 606 int newSystemUIVisibility = (visible ? View.SYSTEM_UI_FLAG_VISIBLE 607 : View.SYSTEM_UI_FLAG_FULLSCREEN); 608 if (newSystemUIVisibility != currentSystemUIVisibility) { 609 mAboveFilmstripControlLayout.setSystemUiVisibility(newSystemUIVisibility); 610 } 611 612 boolean currentActionBarVisibility = mActionBar.isShowing(); 613 mCameraAppUI.getFilmstripBottomControls().setVisible(visible); 614 if (visible != currentActionBarVisibility) { 615 if (visible) { 616 mActionBar.show(); 617 } else { 618 mActionBar.hide(); 619 } 620 } 621 } 622 623 private void hideSessionProgress() { 624 mSessionProgressPanel.setVisibility(View.GONE); 625 } 626 627 private void showSessionProgress(CharSequence message) { 628 mBottomProgressText.setText(message); 629 mSessionProgressPanel.setVisibility(View.VISIBLE); 630 } 631 632 private void updateSessionProgress(int progress) { 633 mBottomProgressBar.setProgress(progress); 634 } 635 636 @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 637 private void setupNfcBeamPush() { 638 NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mAppContext); 639 if (adapter == null) { 640 return; 641 } 642 643 if (!ApiHelper.HAS_SET_BEAM_PUSH_URIS) { 644 // Disable beaming 645 adapter.setNdefPushMessage(null, CameraActivity.this); 646 return; 647 } 648 649 adapter.setBeamPushUris(null, CameraActivity.this); 650 adapter.setBeamPushUrisCallback(new CreateBeamUrisCallback() { 651 @Override 652 public Uri[] createBeamUris(NfcEvent event) { 653 return mNfcPushUris; 654 } 655 }, CameraActivity.this); 656 } 657 658 @Override 659 public void onMenuVisibilityChanged(boolean isVisible) { 660 // TODO: Remove this or bring back the original implementation: cancel 661 // auto-hide actionbar. 662 } 663 664 @Override 665 public boolean onShareTargetSelected(ShareActionProvider shareActionProvider, Intent intent) { 666 int currentDataId = mFilmstripController.getCurrentId(); 667 if (currentDataId < 0) { 668 return false; 669 } 670 UsageStatistics.photoInteraction( 671 UsageStatistics.hashFileName(fileNameFromDataID(currentDataId)), 672 eventprotos.CameraEvent.InteractionType.SHARE, 673 InteractionCause.BUTTON); 674 // TODO add intent.getComponent().getPackageName() 675 return true; 676 } 677 678 // Note: All callbacks come back on the main thread. 679 private final SessionListener mSessionListener = 680 new SessionListener() { 681 @Override 682 public void onSessionQueued(final Uri uri) { 683 notifyNewMedia(uri); 684 int dataID = mDataAdapter.findDataByContentUri(uri); 685 if (dataID != -1) { 686 // Don't allow special UI actions (swipe to 687 // delete, for example) on in-progress data. 688 LocalData d = mDataAdapter.getLocalData(dataID); 689 InProgressDataWrapper newData = new InProgressDataWrapper(d); 690 mDataAdapter.updateData(dataID, newData); 691 } 692 } 693 694 @Override 695 public void onSessionDone(final Uri uri) { 696 Log.v(TAG, "onSessionDone:" + uri); 697 int doneID = mDataAdapter.findDataByContentUri(uri); 698 int currentDataId = mFilmstripController.getCurrentId(); 699 700 if (currentDataId == doneID) { 701 hideSessionProgress(); 702 updateSessionProgress(0); 703 } 704 mDataAdapter.refresh(uri, /* isInProgress */false); 705 } 706 707 @Override 708 public void onSessionProgress(final Uri uri, final int progress) { 709 if (progress < 0) { 710 // Do nothing, there is no task for this URI. 711 return; 712 } 713 int currentDataId = mFilmstripController.getCurrentId(); 714 if (currentDataId == -1) { 715 return; 716 } 717 if (uri.equals( 718 mDataAdapter.getLocalData(currentDataId).getContentUri())) { 719 updateSessionProgress(progress); 720 } 721 } 722 723 @Override 724 public void onSessionUpdated(Uri uri) { 725 mDataAdapter.refresh(uri, /* isInProgress */true); 726 } 727 }; 728 729 @Override 730 public Context getAndroidContext() { 731 return mAppContext; 732 } 733 734 @Override 735 public void launchActivityByIntent(Intent intent) { 736 startActivityForResult(intent, REQ_CODE_DONT_SWITCH_TO_PREVIEW); 737 } 738 739 @Override 740 public int getCurrentModuleIndex() { 741 return mCurrentModeIndex; 742 } 743 744 @Override 745 public ModuleController getCurrentModuleController() { 746 return mCurrentModule; 747 } 748 749 @Override 750 public int getQuickSwitchToModuleId(int currentModuleIndex) { 751 return mModuleManager.getQuickSwitchToModuleId(currentModuleIndex, mSettingsManager, 752 mAppContext); 753 } 754 755 @Override 756 public SurfaceTexture getPreviewBuffer() { 757 // TODO: implement this 758 return null; 759 } 760 761 @Override 762 public void onPreviewReadyToStart() { 763 mCameraAppUI.onPreviewReadyToStart(); 764 } 765 766 @Override 767 public void onPreviewStarted() { 768 mCameraAppUI.onPreviewStarted(); 769 } 770 771 @Override 772 public void addPreviewAreaSizeChangedListener( 773 PreviewStatusListener.PreviewAreaSizeChangedListener listener) { 774 mCameraAppUI.addPreviewAreaSizeChangedListener(listener); 775 } 776 777 @Override 778 public void removePreviewAreaSizeChangedListener( 779 PreviewStatusListener.PreviewAreaSizeChangedListener listener) { 780 mCameraAppUI.removePreviewAreaSizeChangedListener(listener); 781 } 782 783 @Override 784 public void setupOneShotPreviewListener() { 785 mCameraController.setOneShotPreviewCallback(mMainHandler, 786 new CameraManager.CameraPreviewDataCallback() { 787 @Override 788 public void onPreviewFrame(byte[] data, CameraManager.CameraProxy camera) { 789 mCurrentModule.onPreviewInitialDataReceived(); 790 mCameraAppUI.onNewPreviewFrame(); 791 } 792 }); 793 } 794 795 @Override 796 public void updatePreviewAspectRatio(float aspectRatio) { 797 mCameraAppUI.updatePreviewAspectRatio(aspectRatio); 798 } 799 800 @Override 801 public boolean shouldShowShimmy() { 802 int remainingTimes = mSettingsManager.getInt( 803 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX); 804 return remainingTimes > 0; 805 } 806 807 @Override 808 public void decrementShimmyPlayTimes() { 809 int remainingTimes = mSettingsManager.getInt( 810 SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX) - 1; 811 if (remainingTimes >= 0) { 812 mSettingsManager.setInt(SettingsManager.SETTING_SHIMMY_REMAINING_PLAY_TIMES_INDEX, 813 remainingTimes); 814 } 815 } 816 817 @Override 818 public void updatePreviewTransform(Matrix matrix) { 819 mCameraAppUI.updatePreviewTransform(matrix); 820 } 821 822 @Override 823 public void setPreviewStatusListener(PreviewStatusListener previewStatusListener) { 824 mCameraAppUI.setPreviewStatusListener(previewStatusListener); 825 } 826 827 @Override 828 public FrameLayout getModuleLayoutRoot() { 829 return mCameraAppUI.getModuleRootView(); 830 } 831 832 @Override 833 public void setShutterEventsListener(ShutterEventsListener listener) { 834 // TODO: implement this 835 } 836 837 @Override 838 public void setShutterEnabled(boolean enabled) { 839 // TODO: implement this 840 } 841 842 @Override 843 public boolean isShutterEnabled() { 844 // TODO: implement this 845 return false; 846 } 847 848 @Override 849 public void startPreCaptureAnimation() { 850 mCameraAppUI.startPreCaptureAnimation(); 851 } 852 853 @Override 854 public void cancelPreCaptureAnimation() { 855 // TODO: implement this 856 } 857 858 @Override 859 public void startPostCaptureAnimation() { 860 // TODO: implement this 861 } 862 863 @Override 864 public void startPostCaptureAnimation(Bitmap thumbnail) { 865 // TODO: implement this 866 } 867 868 @Override 869 public void cancelPostCaptureAnimation() { 870 // TODO: implement this 871 } 872 873 @Override 874 public OrientationManager getOrientationManager() { 875 return mOrientationManager; 876 } 877 878 @Override 879 public LocationManager getLocationManager() { 880 return mLocationManager; 881 } 882 883 @Override 884 public void lockOrientation() { 885 mOrientationManager.lockOrientation(); 886 } 887 888 @Override 889 public void unlockOrientation() { 890 mOrientationManager.unlockOrientation(); 891 } 892 893 @Override 894 public void notifyNewMedia(Uri uri) { 895 ContentResolver cr = getContentResolver(); 896 String mimeType = cr.getType(uri); 897 if (mimeType.startsWith("video/")) { 898 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 899 mDataAdapter.addNewVideo(uri); 900 } else if (mimeType.startsWith("image/")) { 901 CameraUtil.broadcastNewPicture(mAppContext, uri); 902 mDataAdapter.addNewPhoto(uri); 903 } else if (mimeType.startsWith(PlaceholderManager.PLACEHOLDER_MIME_TYPE)) { 904 mDataAdapter.addNewPhoto(uri); 905 } else { 906 android.util.Log.w(TAG, "Unknown new media with MIME type:" 907 + mimeType + ", uri:" + uri); 908 } 909 } 910 911 @Override 912 public void enableKeepScreenOn(boolean enabled) { 913 if (mPaused) { 914 return; 915 } 916 917 mKeepScreenOn = enabled; 918 if (mKeepScreenOn) { 919 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 920 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 921 } else { 922 keepScreenOnForAWhile(); 923 } 924 } 925 926 @Override 927 public CameraProvider getCameraProvider() { 928 return mCameraController; 929 } 930 931 private void removeData(int dataID) { 932 mDataAdapter.removeData(dataID); 933 if (mDataAdapter.getTotalNumber() > 1) { 934 showUndoDeletionBar(); 935 } else { 936 // If camera preview is the only view left in filmstrip, 937 // no need to show undo bar. 938 mPendingDeletion = true; 939 performDeletion(); 940 if (mFilmstripVisible) { 941 mCameraAppUI.getFilmstripContentPanel().animateHide(); 942 } 943 } 944 } 945 946 @Override 947 public boolean onOptionsItemSelected(MenuItem item) { 948 // Handle presses on the action bar items 949 switch (item.getItemId()) { 950 case android.R.id.home: 951 if (mFilmstripVisible && mUpAsGallery && startGallery()) { 952 return true; 953 } 954 onBackPressed(); 955 return true; 956 default: 957 return super.onOptionsItemSelected(item); 958 } 959 } 960 961 private boolean isCaptureIntent() { 962 if (MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction()) 963 || MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 964 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 965 return true; 966 } else { 967 return false; 968 } 969 } 970 971 @Override 972 public void onCreate(Bundle state) { 973 super.onCreate(state); 974 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_START); 975 mOnCreateTime = System.currentTimeMillis(); 976 mAppContext = getApplicationContext(); 977 GcamHelper.init(getContentResolver()); 978 979 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 980 setContentView(R.layout.activity_main); 981 mActionBar = getActionBar(); 982 mActionBar.addOnMenuVisibilityListener(this); 983 mMainHandler = new MainHandler(this, getMainLooper()); 984 mCameraController = 985 new CameraController(mAppContext, this, mMainHandler, 986 CameraManagerFactory.getAndroidCameraManager()); 987 mPreferences = new ComboPreferences(mAppContext); 988 mContentResolver = this.getContentResolver(); 989 990 mSettingsManager = new SettingsManager(mAppContext, this, 991 mCameraController.getNumberOfCameras()); 992 993 // Remove this after we get rid of ComboPreferences. 994 int cameraId = Integer.parseInt(mSettingsManager.get(SettingsManager.SETTING_CAMERA_ID)); 995 mPreferences.setLocalId(mAppContext, cameraId); 996 CameraSettings.upgradeGlobalPreferences(mPreferences, 997 mCameraController.getNumberOfCameras()); 998 // TODO: Try to move all the resources allocation to happen as soon as 999 // possible so we can call module.init() at the earliest time. 1000 mModuleManager = new ModuleManagerImpl(); 1001 ModulesInfo.setupModules(mAppContext, mModuleManager); 1002 1003 mModeListView = (ModeListView) findViewById(R.id.mode_list_layout); 1004 mModeListView.init(mModuleManager.getSupportedModeIndexList()); 1005 if (ApiHelper.HAS_ROTATION_ANIMATION) { 1006 setRotationAnimation(); 1007 } 1008 1009 // Check if this is in the secure camera mode. 1010 Intent intent = getIntent(); 1011 String action = intent.getAction(); 1012 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 1013 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 1014 mSecureCamera = true; 1015 } else { 1016 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 1017 } 1018 1019 if (mSecureCamera) { 1020 // Foreground event caused by lock screen startup. 1021 // It is necessary to log this in onCreate, to avoid the 1022 // onResume->onPause->onResume sequence. 1023 UsageStatistics.foregrounded( 1024 eventprotos.ForegroundEvent.ForegroundSource.LOCK_SCREEN); 1025 1026 // Change the window flags so that secure camera can show when 1027 // locked 1028 Window win = getWindow(); 1029 WindowManager.LayoutParams params = win.getAttributes(); 1030 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 1031 win.setAttributes(params); 1032 1033 // Filter for screen off so that we can finish secure camera 1034 // activity 1035 // when screen is off. 1036 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 1037 registerReceiver(mScreenOffReceiver, filter); 1038 } 1039 mCameraAppUI = new CameraAppUI(this, 1040 (MainActivityLayout) findViewById(R.id.activity_root_view), isCaptureIntent()); 1041 1042 mCameraAppUI.setFilmstripBottomControlsListener(mMyFilmstripBottomControlListener); 1043 1044 mAboveFilmstripControlLayout = 1045 (FrameLayout) findViewById(R.id.camera_filmstrip_content_layout); 1046 1047 // Add the session listener so we can track the session progress 1048 // updates. 1049 getServices().getCaptureSessionManager().addSessionListener(mSessionListener); 1050 mSessionProgressPanel = findViewById(R.id.pano_session_progress_panel); 1051 mBottomProgressBar = (ProgressBar) findViewById(R.id.pano_session_progress_bar); 1052 mBottomProgressText = (TextView) findViewById(R.id.pano_session_progress_text); 1053 mFilmstripController = ((FilmstripView) findViewById(R.id.filmstrip_view)).getController(); 1054 mFilmstripController.setImageGap( 1055 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 1056 mPanoramaViewHelper = new PanoramaViewHelper(this); 1057 mPanoramaViewHelper.onCreate(); 1058 // Set up the camera preview first so the preview shows up ASAP. 1059 mDataAdapter = new CameraDataAdapter(mAppContext, 1060 new ColorDrawable(getResources().getColor(R.color.photo_placeholder))); 1061 mDataAdapter.setLocalDataListener(mLocalDataListener); 1062 1063 mCameraAppUI.getFilmstripContentPanel().setFilmstripListener(mFilmstripListener); 1064 1065 mLocationManager = new LocationManager(mAppContext); 1066 1067 int modeIndex = -1; 1068 int photoIndex = getResources().getInteger(R.integer.camera_mode_photo); 1069 int videoIndex = getResources().getInteger(R.integer.camera_mode_video); 1070 int gcamIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1071 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 1072 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 1073 modeIndex = videoIndex; 1074 } else if (MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA.equals(getIntent().getAction()) 1075 || MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(getIntent() 1076 .getAction())) { 1077 modeIndex = photoIndex; 1078 if (mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX) 1079 == gcamIndex && GcamHelper.hasGcamCapture()) { 1080 modeIndex = gcamIndex; 1081 } 1082 } else if (MediaStore.ACTION_IMAGE_CAPTURE.equals(getIntent().getAction()) 1083 || MediaStore.ACTION_IMAGE_CAPTURE_SECURE.equals(getIntent().getAction())) { 1084 modeIndex = photoIndex; 1085 } else { 1086 // If the activity has not been started using an explicit intent, 1087 // read the module index from the last time the user changed modes 1088 modeIndex = mSettingsManager.getInt(SettingsManager.SETTING_STARTUP_MODULE_INDEX); 1089 if ((modeIndex == gcamIndex && 1090 !GcamHelper.hasGcamCapture()) || modeIndex < 0) { 1091 modeIndex = photoIndex; 1092 } 1093 } 1094 1095 mOrientationManager = new OrientationManagerImpl(this); 1096 mOrientationManager.addOnOrientationChangeListener(mMainHandler, this); 1097 1098 setModuleFromModeIndex(modeIndex); 1099 mCameraAppUI.prepareModuleUI(); 1100 mCurrentModule.init(this, isSecureCamera(), isCaptureIntent()); 1101 1102 if (!mSecureCamera) { 1103 mFilmstripController.setDataAdapter(mDataAdapter); 1104 if (!isCaptureIntent()) { 1105 mDataAdapter.requestLoad(); 1106 } 1107 } else { 1108 // Put a lock placeholder as the last image by setting its date to 1109 // 0. 1110 ImageView v = (ImageView) getLayoutInflater().inflate( 1111 R.layout.secure_album_placeholder, null); 1112 v.setOnClickListener(new View.OnClickListener() { 1113 @Override 1114 public void onClick(View view) { 1115 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, 1116 InteractionCause.BUTTON); 1117 startGallery(); 1118 finish(); 1119 } 1120 }); 1121 mDataAdapter = new FixedLastDataAdapter( 1122 mAppContext, 1123 mDataAdapter, 1124 new SimpleViewData( 1125 v, 1126 v.getDrawable().getIntrinsicWidth(), 1127 v.getDrawable().getIntrinsicHeight(), 1128 0, 0)); 1129 // Flush out all the original data. 1130 mDataAdapter.flush(); 1131 mFilmstripController.setDataAdapter(mDataAdapter); 1132 } 1133 1134 setupNfcBeamPush(); 1135 1136 mLocalImagesObserver = new LocalMediaObserver(); 1137 mLocalVideosObserver = new LocalMediaObserver(); 1138 1139 getContentResolver().registerContentObserver( 1140 MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, 1141 mLocalImagesObserver); 1142 getContentResolver().registerContentObserver( 1143 MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, 1144 mLocalVideosObserver); 1145 if (FeedbackHelper.feedbackAvailable()) { 1146 mFeedbackHelper = new FeedbackHelper(mAppContext); 1147 } 1148 } 1149 1150 private void setRotationAnimation() { 1151 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 1152 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 1153 Window win = getWindow(); 1154 WindowManager.LayoutParams winParams = win.getAttributes(); 1155 winParams.rotationAnimation = rotationAnimation; 1156 win.setAttributes(winParams); 1157 } 1158 1159 @Override 1160 public void onUserInteraction() { 1161 super.onUserInteraction(); 1162 if (!isFinishing()) { 1163 keepScreenOnForAWhile(); 1164 } 1165 } 1166 1167 @Override 1168 public boolean dispatchTouchEvent(MotionEvent ev) { 1169 boolean result = super.dispatchTouchEvent(ev); 1170 if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) { 1171 // Real deletion is postponed until the next user interaction after 1172 // the gesture that triggers deletion. Until real deletion is 1173 // performed, users can click the undo button to bring back the 1174 // image that they chose to delete. 1175 if (mPendingDeletion && !mIsUndoingDeletion) { 1176 performDeletion(); 1177 } 1178 } 1179 return result; 1180 } 1181 1182 @Override 1183 public void onPause() { 1184 mPaused = true; 1185 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_PAUSE); 1186 1187 // Delete photos that are pending deletion 1188 performDeletion(); 1189 mCurrentModule.pause(); 1190 mOrientationManager.pause(); 1191 // Close the camera and wait for the operation done. 1192 mCameraController.closeCamera(); 1193 mPanoramaViewHelper.onPause(); 1194 1195 mLocalImagesObserver.setActivityPaused(true); 1196 mLocalVideosObserver.setActivityPaused(true); 1197 resetScreenOn(); 1198 super.onPause(); 1199 } 1200 1201 @Override 1202 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 1203 if (requestCode == REQ_CODE_DONT_SWITCH_TO_PREVIEW) { 1204 mResetToPreviewOnResume = false; 1205 } else { 1206 super.onActivityResult(requestCode, resultCode, data); 1207 } 1208 } 1209 1210 @Override 1211 public void onResume() { 1212 mPaused = false; 1213 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.ACTIVITY_RESUME); 1214 1215 mLastLayoutOrientation = getResources().getConfiguration().orientation; 1216 1217 // TODO: Handle this in OrientationManager. 1218 // Auto-rotate off 1219 if (Settings.System.getInt(getContentResolver(), 1220 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) { 1221 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 1222 mAutoRotateScreen = false; 1223 } else { 1224 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 1225 mAutoRotateScreen = true; 1226 } 1227 1228 if (isCaptureIntent()) { 1229 // Foreground event caused by photo or video capure intent. 1230 UsageStatistics.foregrounded( 1231 eventprotos.ForegroundEvent.ForegroundSource.INTENT_PICKER); 1232 } else if (!mSecureCamera) { 1233 // Foreground event that is not caused by an intent. 1234 UsageStatistics.foregrounded( 1235 eventprotos.ForegroundEvent.ForegroundSource.ICON_LAUNCHER); 1236 } 1237 1238 Drawable galleryLogo; 1239 if (mSecureCamera) { 1240 mGalleryIntent = null; 1241 galleryLogo = null; 1242 } else { 1243 mGalleryIntent = IntentHelper.getDefaultGalleryIntent(mAppContext); 1244 galleryLogo = IntentHelper.getGalleryIcon(mAppContext, mGalleryIntent); 1245 } 1246 if (galleryLogo == null) { 1247 try { 1248 galleryLogo = getPackageManager().getActivityLogo(getComponentName()); 1249 } catch (PackageManager.NameNotFoundException e) { 1250 Log.e(TAG, "Can't get the activity logo"); 1251 } 1252 } 1253 mActionBar.setLogo(galleryLogo); 1254 mOrientationManager.resume(); 1255 super.onResume(); 1256 mCurrentModule.resume(); 1257 setSwipingEnabled(true); 1258 1259 if (mResetToPreviewOnResume) { 1260 mCameraAppUI.resume(); 1261 } else { 1262 LocalData data = mDataAdapter.getLocalData(mFilmstripController.getCurrentId()); 1263 if (data != null) { 1264 mDataAdapter.refresh(data.getContentUri(), false); 1265 } 1266 } 1267 // The share button might be disabled to avoid double tapping. 1268 mCameraAppUI.getFilmstripBottomControls().setShareEnabled(true); 1269 // Default is showing the preview, unless disabled by explicitly 1270 // starting an activity we want to return from to the filmstrip rather 1271 // than the preview. 1272 mResetToPreviewOnResume = true; 1273 1274 if (mLocalVideosObserver.isMediaDataChangedDuringPause() 1275 || mLocalImagesObserver.isMediaDataChangedDuringPause()) { 1276 if (!mSecureCamera) { 1277 // If it's secure camera, requestLoad() should not be called 1278 // as it will load all the data. 1279 if (!mFilmstripVisible) { 1280 mDataAdapter.requestLoad(); 1281 } 1282 } 1283 } 1284 mLocalImagesObserver.setActivityPaused(false); 1285 mLocalVideosObserver.setActivityPaused(false); 1286 1287 keepScreenOnForAWhile(); 1288 1289 // Lights-out mode at all times. 1290 findViewById(R.id.activity_root_view) 1291 .setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); 1292 mPanoramaViewHelper.onResume(); 1293 } 1294 1295 @Override 1296 public void onStart() { 1297 super.onStart(); 1298 mPanoramaViewHelper.onStart(); 1299 boolean recordLocation = RecordLocationPreference.get( 1300 mPreferences, mContentResolver); 1301 mLocationManager.recordLocation(recordLocation); 1302 } 1303 1304 @Override 1305 protected void onStop() { 1306 mPanoramaViewHelper.onStop(); 1307 if (mFeedbackHelper != null) { 1308 mFeedbackHelper.stopFeedback(); 1309 } 1310 1311 mLocationManager.disconnect(); 1312 CameraManagerFactory.recycle(); 1313 super.onStop(); 1314 } 1315 1316 @Override 1317 public void onDestroy() { 1318 if (mSecureCamera) { 1319 unregisterReceiver(mScreenOffReceiver); 1320 } 1321 mActionBar.removeOnMenuVisibilityListener(this); 1322 mSettingsManager.removeAllListeners(); 1323 mCameraController.removeCallbackReceiver(); 1324 getContentResolver().unregisterContentObserver(mLocalImagesObserver); 1325 getContentResolver().unregisterContentObserver(mLocalVideosObserver); 1326 getServices().getCaptureSessionManager().removeSessionListener(mSessionListener); 1327 mCameraAppUI.onDestroy(); 1328 mCameraController = null; 1329 mSettingsManager = null; 1330 mCameraAppUI = null; 1331 mOrientationManager = null; 1332 mButtonManager = null; 1333 super.onDestroy(); 1334 } 1335 1336 @Override 1337 public void onConfigurationChanged(Configuration config) { 1338 super.onConfigurationChanged(config); 1339 Log.v(TAG, "onConfigurationChanged"); 1340 if (config.orientation == Configuration.ORIENTATION_UNDEFINED) { 1341 return; 1342 } 1343 1344 if (mLastLayoutOrientation != config.orientation) { 1345 mLastLayoutOrientation = config.orientation; 1346 mCurrentModule.onLayoutOrientationChanged( 1347 mLastLayoutOrientation == Configuration.ORIENTATION_LANDSCAPE); 1348 } 1349 } 1350 1351 @Override 1352 public boolean onKeyDown(int keyCode, KeyEvent event) { 1353 if (!mFilmstripVisible) { 1354 if (mCurrentModule.onKeyDown(keyCode, event)) { 1355 return true; 1356 } 1357 // Prevent software keyboard or voice search from showing up. 1358 if (keyCode == KeyEvent.KEYCODE_SEARCH 1359 || keyCode == KeyEvent.KEYCODE_MENU) { 1360 if (event.isLongPress()) { 1361 return true; 1362 } 1363 } 1364 } 1365 1366 return super.onKeyDown(keyCode, event); 1367 } 1368 1369 @Override 1370 public boolean onKeyUp(int keyCode, KeyEvent event) { 1371 if (!mFilmstripVisible && mCurrentModule.onKeyUp(keyCode, event)) { 1372 return true; 1373 } 1374 return super.onKeyUp(keyCode, event); 1375 } 1376 1377 @Override 1378 public void onBackPressed() { 1379 if (!mCameraAppUI.onBackPressed()) { 1380 if (!mCurrentModule.onBackPressed()) { 1381 super.onBackPressed(); 1382 } 1383 } 1384 } 1385 1386 public boolean isAutoRotateScreen() { 1387 return mAutoRotateScreen; 1388 } 1389 1390 protected void updateStorageSpace() { 1391 mStorageSpaceBytes = Storage.getAvailableSpace(); 1392 } 1393 1394 protected long getStorageSpaceBytes() { 1395 return mStorageSpaceBytes; 1396 } 1397 1398 protected void updateStorageSpaceAndHint() { 1399 updateStorageSpace(); 1400 updateStorageHint(mStorageSpaceBytes); 1401 } 1402 1403 protected void updateStorageHint(long storageSpace) { 1404 String message = null; 1405 if (storageSpace == Storage.UNAVAILABLE) { 1406 message = getString(R.string.no_storage); 1407 } else if (storageSpace == Storage.PREPARING) { 1408 message = getString(R.string.preparing_sd); 1409 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 1410 message = getString(R.string.access_sd_fail); 1411 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { 1412 message = getString(R.string.spaceIsLow_content); 1413 } 1414 1415 if (message != null) { 1416 if (mStorageHint == null) { 1417 mStorageHint = OnScreenHint.makeText(mAppContext, message); 1418 } else { 1419 mStorageHint.setText(message); 1420 } 1421 mStorageHint.show(); 1422 } else if (mStorageHint != null) { 1423 mStorageHint.cancel(); 1424 mStorageHint = null; 1425 } 1426 } 1427 1428 protected void setResultEx(int resultCode) { 1429 mResultCodeForTesting = resultCode; 1430 setResult(resultCode); 1431 } 1432 1433 protected void setResultEx(int resultCode, Intent data) { 1434 mResultCodeForTesting = resultCode; 1435 mResultDataForTesting = data; 1436 setResult(resultCode, data); 1437 } 1438 1439 public int getResultCode() { 1440 return mResultCodeForTesting; 1441 } 1442 1443 public Intent getResultData() { 1444 return mResultDataForTesting; 1445 } 1446 1447 public boolean isSecureCamera() { 1448 return mSecureCamera; 1449 } 1450 1451 @Override 1452 public boolean isPaused() { 1453 return mPaused; 1454 } 1455 1456 @Override 1457 public void onModeSelected(int modeIndex) { 1458 if (mCurrentModeIndex == modeIndex) { 1459 return; 1460 } 1461 1462 CameraPerformanceTracker.onEvent(CameraPerformanceTracker.MODE_SWITCH_START); 1463 // Record last used camera mode for quick switching 1464 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo) 1465 || modeIndex == getResources().getInteger(R.integer.camera_mode_gcam)) { 1466 mSettingsManager.setInt(SettingsManager.SETTING_KEY_CAMERA_MODULE_LAST_USED_INDEX, 1467 modeIndex); 1468 } 1469 1470 closeModule(mCurrentModule); 1471 int oldModuleIndex = mCurrentModeIndex; 1472 1473 // Refocus and Gcam are modes that cannot be selected 1474 // from the mode list view, because they are not list items. 1475 // Check whether we should interpret MODULE_CRAFT as either. 1476 if (modeIndex == getResources().getInteger(R.integer.camera_mode_photo)) { 1477 boolean hdrPlusOn = mSettingsManager.isHdrPlusOn(); 1478 if (hdrPlusOn && GcamHelper.hasGcamCapture()) { 1479 modeIndex = getResources().getInteger(R.integer.camera_mode_gcam); 1480 } 1481 } 1482 1483 setModuleFromModeIndex(modeIndex); 1484 mCameraAppUI.clearModuleUI(); 1485 1486 mCameraAppUI.resetBottomControls(mCurrentModule, modeIndex); 1487 openModule(mCurrentModule); 1488 mCurrentModule.onOrientationChanged(mLastRawOrientation); 1489 // Store the module index so we can use it the next time the Camera 1490 // starts up. 1491 SharedPreferences prefs = PreferenceManager 1492 .getDefaultSharedPreferences(mAppContext); 1493 prefs.edit().putInt(CameraSettings.KEY_STARTUP_MODULE_INDEX, modeIndex).apply(); 1494 } 1495 1496 /** 1497 * Shows the settings dialog. 1498 */ 1499 @Override 1500 public void onSettingsSelected() { 1501 Intent intent = new Intent(this, CameraSettingsActivity.class); 1502 startActivity(intent); 1503 } 1504 1505 /** 1506 * Sets the mCurrentModuleIndex, creates a new module instance for the given 1507 * index an sets it as mCurrentModule. 1508 */ 1509 private void setModuleFromModeIndex(int modeIndex) { 1510 ModuleManagerImpl.ModuleAgent agent = mModuleManager.getModuleAgent(modeIndex); 1511 if (agent == null) { 1512 return; 1513 } 1514 if (!agent.requestAppForCamera()) { 1515 mCameraController.closeCamera(); 1516 } 1517 mCurrentModeIndex = agent.getModuleId(); 1518 mCurrentModule = (CameraModule) agent.createModule(this); 1519 } 1520 1521 @Override 1522 public SettingsManager getSettingsManager() { 1523 return mSettingsManager; 1524 } 1525 1526 @Override 1527 public CameraServices getServices() { 1528 return (CameraServices) getApplication(); 1529 } 1530 1531 public List<String> getSupportedModeNames() { 1532 List<Integer> indices = mModuleManager.getSupportedModeIndexList(); 1533 List<String> supported = new ArrayList<String>(); 1534 1535 for (Integer modeIndex : indices) { 1536 String name = CameraUtil.getCameraModeText(modeIndex, mAppContext); 1537 if (name != null && !name.equals("")) { 1538 supported.add(name); 1539 } 1540 } 1541 return supported; 1542 } 1543 1544 @Override 1545 public ButtonManager getButtonManager() { 1546 if (mButtonManager == null) { 1547 mButtonManager = new ButtonManager(this); 1548 } 1549 return mButtonManager; 1550 } 1551 1552 /** 1553 * Creates an AlertDialog appropriate for choosing whether to enable 1554 * location on the first run of the app. 1555 */ 1556 public AlertDialog getFirstTimeLocationAlert() { 1557 AlertDialog.Builder builder = new AlertDialog.Builder(this); 1558 builder = SettingsUtil.getFirstTimeLocationAlertBuilder(builder, new Callback<Boolean>() { 1559 @Override 1560 public void onCallback(Boolean locationOn) { 1561 mSettingsManager.setLocation(locationOn, mLocationManager); 1562 } 1563 }); 1564 if (builder != null) { 1565 return builder.create(); 1566 } else { 1567 return null; 1568 } 1569 } 1570 1571 /** 1572 * Launches an ACTION_EDIT intent for the given local data item. If 1573 * 'withTinyPlanet' is set, this will show a disambig dialog first to let 1574 * the user start either the tiny planet editor or another photo edior. 1575 * 1576 * @param data The data item to edit. 1577 */ 1578 public void launchEditor(LocalData data) { 1579 Intent intent = new Intent(Intent.ACTION_EDIT) 1580 .setDataAndType(data.getContentUri(), data.getMimeType()) 1581 .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); 1582 try { 1583 launchActivityByIntent(intent); 1584 } catch (ActivityNotFoundException e) { 1585 launchActivityByIntent(Intent.createChooser(intent, null)); 1586 } 1587 } 1588 1589 @Override 1590 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 1591 super.onCreateContextMenu(menu, v, menuInfo); 1592 1593 MenuInflater inflater = getMenuInflater(); 1594 inflater.inflate(R.menu.filmstrip_context_menu, menu); 1595 } 1596 1597 @Override 1598 public boolean onContextItemSelected(MenuItem item) { 1599 switch (item.getItemId()) { 1600 case R.id.tiny_planet_editor: 1601 mMyFilmstripBottomControlListener.onTinyPlanet(); 1602 return true; 1603 case R.id.photo_editor: 1604 mMyFilmstripBottomControlListener.onEdit(); 1605 return true; 1606 } 1607 return false; 1608 } 1609 1610 /** 1611 * Launch the tiny planet editor. 1612 * 1613 * @param data The data must be a 360 degree stereographically mapped 1614 * panoramic image. It will not be modified, instead a new item 1615 * with the result will be added to the filmstrip. 1616 */ 1617 public void launchTinyPlanetEditor(LocalData data) { 1618 TinyPlanetFragment fragment = new TinyPlanetFragment(); 1619 Bundle bundle = new Bundle(); 1620 bundle.putString(TinyPlanetFragment.ARGUMENT_URI, data.getContentUri().toString()); 1621 bundle.putString(TinyPlanetFragment.ARGUMENT_TITLE, data.getTitle()); 1622 fragment.setArguments(bundle); 1623 fragment.show(getFragmentManager(), "tiny_planet"); 1624 } 1625 1626 private void openModule(CameraModule module) { 1627 module.init(this, isSecureCamera(), isCaptureIntent()); 1628 module.resume(); 1629 module.onPreviewVisibilityChanged(!mFilmstripVisible); 1630 } 1631 1632 private void closeModule(CameraModule module) { 1633 module.pause(); 1634 } 1635 1636 private void performDeletion() { 1637 if (!mPendingDeletion) { 1638 return; 1639 } 1640 hideUndoDeletionBar(false); 1641 mDataAdapter.executeDeletion(); 1642 } 1643 1644 public void showUndoDeletionBar() { 1645 if (mPendingDeletion) { 1646 performDeletion(); 1647 } 1648 Log.v(TAG, "showing undo bar"); 1649 mPendingDeletion = true; 1650 if (mUndoDeletionBar == null) { 1651 ViewGroup v = (ViewGroup) getLayoutInflater().inflate(R.layout.undo_bar, 1652 mAboveFilmstripControlLayout, true); 1653 mUndoDeletionBar = (ViewGroup) v.findViewById(R.id.camera_undo_deletion_bar); 1654 View button = mUndoDeletionBar.findViewById(R.id.camera_undo_deletion_button); 1655 button.setOnClickListener(new View.OnClickListener() { 1656 @Override 1657 public void onClick(View view) { 1658 mDataAdapter.undoDataRemoval(); 1659 hideUndoDeletionBar(true); 1660 } 1661 }); 1662 // Setting undo bar clickable to avoid touch events going through 1663 // the bar to the buttons (eg. edit button, etc) underneath the bar. 1664 mUndoDeletionBar.setClickable(true); 1665 // When there is user interaction going on with the undo button, we 1666 // do not want to hide the undo bar. 1667 button.setOnTouchListener(new View.OnTouchListener() { 1668 @Override 1669 public boolean onTouch(View v, MotionEvent event) { 1670 if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { 1671 mIsUndoingDeletion = true; 1672 } else if (event.getActionMasked() == MotionEvent.ACTION_UP) { 1673 mIsUndoingDeletion = false; 1674 } 1675 return false; 1676 } 1677 }); 1678 } 1679 mUndoDeletionBar.setAlpha(0f); 1680 mUndoDeletionBar.setVisibility(View.VISIBLE); 1681 mUndoDeletionBar.animate().setDuration(200).alpha(1f).setListener(null).start(); 1682 } 1683 1684 private void hideUndoDeletionBar(boolean withAnimation) { 1685 Log.v(TAG, "Hiding undo deletion bar"); 1686 mPendingDeletion = false; 1687 if (mUndoDeletionBar != null) { 1688 if (withAnimation) { 1689 mUndoDeletionBar.animate().setDuration(200).alpha(0f) 1690 .setListener(new Animator.AnimatorListener() { 1691 @Override 1692 public void onAnimationStart(Animator animation) { 1693 // Do nothing. 1694 } 1695 1696 @Override 1697 public void onAnimationEnd(Animator animation) { 1698 mUndoDeletionBar.setVisibility(View.GONE); 1699 } 1700 1701 @Override 1702 public void onAnimationCancel(Animator animation) { 1703 // Do nothing. 1704 } 1705 1706 @Override 1707 public void onAnimationRepeat(Animator animation) { 1708 // Do nothing. 1709 } 1710 }).start(); 1711 } else { 1712 mUndoDeletionBar.setVisibility(View.GONE); 1713 } 1714 } 1715 } 1716 1717 @Override 1718 public void onOrientationChanged(int orientation) { 1719 // We keep the last known orientation. So if the user first orient 1720 // the camera then point the camera to floor or sky, we still have 1721 // the correct orientation. 1722 if (orientation == OrientationManager.ORIENTATION_UNKNOWN) { 1723 return; 1724 } 1725 mLastRawOrientation = orientation; 1726 if (mCurrentModule != null) { 1727 mCurrentModule.onOrientationChanged(orientation); 1728 } 1729 } 1730 1731 /** 1732 * Enable/disable swipe-to-filmstrip. Will always disable swipe if in 1733 * capture intent. 1734 * 1735 * @param enable {@code true} to enable swipe. 1736 */ 1737 public void setSwipingEnabled(boolean enable) { 1738 // TODO: Bring back the functionality. 1739 if (isCaptureIntent()) { 1740 // lockPreview(true); 1741 } else { 1742 // lockPreview(!enable); 1743 } 1744 } 1745 1746 // Accessor methods for getting latency times used in performance testing 1747 public long getFirstPreviewTime() { 1748 if (mCurrentModule instanceof PhotoModule) { 1749 long coverHiddenTime = getCameraAppUI().getCoverHiddenTime(); 1750 if (coverHiddenTime != -1) { 1751 return coverHiddenTime - mOnCreateTime; 1752 } 1753 } 1754 return -1; 1755 } 1756 1757 public long getAutoFocusTime() { 1758 return (mCurrentModule instanceof PhotoModule) ? 1759 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 1760 } 1761 1762 public long getShutterLag() { 1763 return (mCurrentModule instanceof PhotoModule) ? 1764 ((PhotoModule) mCurrentModule).mShutterLag : -1; 1765 } 1766 1767 public long getShutterToPictureDisplayedTime() { 1768 return (mCurrentModule instanceof PhotoModule) ? 1769 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 1770 } 1771 1772 public long getPictureDisplayedToJpegCallbackTime() { 1773 return (mCurrentModule instanceof PhotoModule) ? 1774 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 1775 } 1776 1777 public long getJpegCallbackFinishTime() { 1778 return (mCurrentModule instanceof PhotoModule) ? 1779 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 1780 } 1781 1782 public long getCaptureStartTime() { 1783 return (mCurrentModule instanceof PhotoModule) ? 1784 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 1785 } 1786 1787 public boolean isRecording() { 1788 return (mCurrentModule instanceof VideoModule) ? 1789 ((VideoModule) mCurrentModule).isRecording() : false; 1790 } 1791 1792 public CameraManager.CameraOpenCallback getCameraOpenErrorCallback() { 1793 return mCameraController; 1794 } 1795 1796 // For debugging purposes only. 1797 public CameraModule getCurrentModule() { 1798 return mCurrentModule; 1799 } 1800 1801 /** 1802 * Reads the current location recording settings and passes it on to the 1803 * location manager. 1804 */ 1805 public void syncLocationManagerSetting() { 1806 mSettingsManager.syncLocationManager(mLocationManager); 1807 } 1808 1809 private void keepScreenOnForAWhile() { 1810 if (mKeepScreenOn) { 1811 return; 1812 } 1813 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1814 getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1815 mMainHandler.sendEmptyMessageDelayed(MSG_CLEAR_SCREEN_ON_FLAG, SCREEN_DELAY_MS); 1816 } 1817 1818 private void resetScreenOn() { 1819 mKeepScreenOn = false; 1820 mMainHandler.removeMessages(MSG_CLEAR_SCREEN_ON_FLAG); 1821 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1822 } 1823 1824 /** 1825 * @return {@code true} if the Gallery is launched successfully. 1826 */ 1827 private boolean startGallery() { 1828 if (mGalleryIntent == null) { 1829 return false; 1830 } 1831 try { 1832 UsageStatistics.changeScreen(NavigationChange.Mode.GALLERY, InteractionCause.BUTTON); 1833 Intent startGalleryIntent = new Intent(mGalleryIntent); 1834 int currentDataId = mFilmstripController.getCurrentId(); 1835 LocalData currentLocalData = mDataAdapter.getLocalData(currentDataId); 1836 if (currentLocalData != null) { 1837 GalleryHelper.setContentUri(startGalleryIntent, currentLocalData.getContentUri()); 1838 } 1839 launchActivityByIntent(startGalleryIntent); 1840 } catch (ActivityNotFoundException e) { 1841 Log.w(TAG, "Failed to launch gallery activity, closing"); 1842 } 1843 return false; 1844 } 1845 1846 private void setNfcBeamPushUriFromData(LocalData data) { 1847 final Uri uri = data.getContentUri(); 1848 if (uri != Uri.EMPTY) { 1849 mNfcPushUris[0] = uri; 1850 } else { 1851 mNfcPushUris[0] = null; 1852 } 1853 } 1854 1855 /** 1856 * Updates the visibility of the filmstrip bottom controls. 1857 */ 1858 private void updateUiByData(final int dataId) { 1859 if (isSecureCamera()) { 1860 // We cannot show buttons in secure camera since go to other 1861 // activities might create a security hole. 1862 return; 1863 } 1864 1865 final LocalData currentData = mDataAdapter.getLocalData(dataId); 1866 if (currentData == null) { 1867 Log.w(TAG, "Current data ID not found."); 1868 hideSessionProgress(); 1869 return; 1870 } 1871 1872 setNfcBeamPushUriFromData(currentData); 1873 1874 /* Bottom controls. */ 1875 1876 updateBottomControlsByData(currentData); 1877 if (!mDataAdapter.isMetadataUpdated(dataId)) { 1878 mDataAdapter.updateMetadata(dataId); 1879 } 1880 } 1881 1882 /** 1883 * Updates the bottom controls based on the data. 1884 */ 1885 private void updateBottomControlsByData(final LocalData currentData) { 1886 1887 final CameraAppUI.BottomControls filmstripBottomControls = 1888 mCameraAppUI.getFilmstripBottomControls(); 1889 filmstripBottomControls.setEditButtonVisibility( 1890 currentData.isDataActionSupported(LocalData.DATA_ACTION_EDIT)); 1891 filmstripBottomControls.setShareButtonVisibility( 1892 currentData.isDataActionSupported(LocalData.DATA_ACTION_SHARE)); 1893 filmstripBottomControls.setDeleteButtonVisibility( 1894 currentData.isDataActionSupported(LocalData.DATA_ACTION_DELETE)); 1895 1896 /* Progress bar */ 1897 1898 Uri contentUri = currentData.getContentUri(); 1899 CaptureSessionManager sessionManager = getServices() 1900 .getCaptureSessionManager(); 1901 int sessionProgress = sessionManager.getSessionProgress(contentUri); 1902 1903 if (sessionProgress < 0) { 1904 hideSessionProgress(); 1905 } else { 1906 CharSequence progressMessage = sessionManager 1907 .getSessionProgressMessage(contentUri); 1908 showSessionProgress(progressMessage); 1909 updateSessionProgress(sessionProgress); 1910 } 1911 1912 /* View button */ 1913 1914 // We need to add this to a separate DB. 1915 final int viewButtonVisibility; 1916 if (PanoramaMetadataLoader.isPanorama(currentData)) { 1917 viewButtonVisibility = CameraAppUI.BottomControls.VIEWER_PHOTO_SPHERE; 1918 } else if (RgbzMetadataLoader.hasRGBZData(currentData)) { 1919 viewButtonVisibility = CameraAppUI.BottomControls.VIEWER_REFOCUS; 1920 } else { 1921 viewButtonVisibility = CameraAppUI.BottomControls.VIEWER_NONE; 1922 } 1923 1924 filmstripBottomControls.setTinyPlanetEnabled( 1925 PanoramaMetadataLoader.isPanorama360(currentData)); 1926 filmstripBottomControls.setViewerButtonVisibility(viewButtonVisibility); 1927 } 1928} 1929