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