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