CameraActivity.java revision fae11a165e344a38811770c7d348eda214683edc
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.app.ActionBar; 20import android.app.Activity; 21import android.content.BroadcastReceiver; 22import android.content.ComponentName; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.IntentFilter; 27import android.content.ServiceConnection; 28import android.content.pm.ActivityInfo; 29import android.content.res.Configuration; 30import android.graphics.drawable.ColorDrawable; 31import android.net.Uri; 32import android.os.AsyncTask; 33import android.os.Bundle; 34import android.os.Handler; 35import android.os.IBinder; 36import android.provider.MediaStore; 37import android.provider.Settings; 38import android.util.Log; 39import android.view.KeyEvent; 40import android.view.LayoutInflater; 41import android.view.Menu; 42import android.view.MenuInflater; 43import android.view.MenuItem; 44import android.view.OrientationEventListener; 45import android.view.View; 46import android.view.ViewGroup; 47import android.view.Window; 48import android.view.WindowManager; 49import android.widget.ImageView; 50import android.widget.ProgressBar; 51 52import com.android.camera.data.CameraDataAdapter; 53import com.android.camera.data.CameraPreviewData; 54import com.android.camera.data.FixedFirstDataAdapter; 55import com.android.camera.data.FixedLastDataAdapter; 56import com.android.camera.data.LocalData; 57import com.android.camera.data.LocalDataAdapter; 58import com.android.camera.data.MediaDetails; 59import com.android.camera.data.SimpleViewData; 60import com.android.camera.util.ApiHelper; 61import com.android.camera.ui.CameraSwitcher; 62import com.android.camera.ui.CameraSwitcher.CameraSwitchListener; 63import com.android.camera.ui.DetailsDialog; 64import com.android.camera.ui.FilmStripView; 65import com.android.camera.util.CameraUtil; 66import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; 67import com.android.camera.util.PhotoSphereHelper; 68import com.android.camera2.R; 69 70public class CameraActivity extends Activity 71 implements CameraSwitchListener { 72 73 private static final String TAG = "CAM_Activity"; 74 75 private static final String INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE = 76 "android.media.action.STILL_IMAGE_CAMERA_SECURE"; 77 public static final String ACTION_IMAGE_CAPTURE_SECURE = 78 "android.media.action.IMAGE_CAPTURE_SECURE"; 79 80 // The intent extra for camera from secure lock screen. True if the gallery 81 // should only show newly captured pictures. sSecureAlbumId does not 82 // increment. This is used when switching between camera, camcorder, and 83 // panorama. If the extra is not set, it is in the normal camera mode. 84 public static final String SECURE_CAMERA_EXTRA = "secure_camera"; 85 86 // Supported operations at FilmStripView. Different data has different 87 // set of supported operations. 88 private static final int SUPPORT_DELETE = 1 << 0; 89 private static final int SUPPORT_ROTATE = 1 << 1; 90 private static final int SUPPORT_INFO = 1 << 2; 91 private static final int SUPPORT_CROP = 1 << 3; 92 private static final int SUPPORT_SETAS = 1 << 4; 93 private static final int SUPPORT_EDIT = 1 << 5; 94 private static final int SUPPORT_TRIM = 1 << 6; 95 private static final int SUPPORT_MUTE = 1 << 7; 96 private static final int SUPPORT_SHOW_ON_MAP = 1 << 8; 97 private static final int SUPPORT_ALL = 0xffffffff; 98 99 /** This data adapter is used by FilmStripView. */ 100 private LocalDataAdapter mDataAdapter; 101 /** This data adapter represents the real local camera data. */ 102 private LocalDataAdapter mWrappedDataAdapter; 103 104 private PanoramaStitchingManager mPanoramaManager; 105 private int mCurrentModuleIndex; 106 private CameraModule mCurrentModule; 107 private View mRootView; 108 private FilmStripView mFilmStripView; 109 private ProgressBar mBottomProgress; 110 private View mPanoStitchingPanel; 111 private int mResultCodeForTesting; 112 private Intent mResultDataForTesting; 113 private OnScreenHint mStorageHint; 114 private long mStorageSpace = Storage.LOW_STORAGE_THRESHOLD; 115 private boolean mAutoRotateScreen; 116 private boolean mSecureCamera; 117 // This is a hack to speed up the start of SecureCamera. 118 private static boolean sFirstStartAfterScreenOn = true; 119 private boolean mShowCameraPreview; 120 private int mLastRawOrientation; 121 private MyOrientationEventListener mOrientationListener; 122 private Handler mMainHandler; 123 private PanoramaViewHelper mPanoramaViewHelper; 124 private CameraPreviewData mCameraPreviewData; 125 private ActionBar mActionBar; 126 private Menu mActionBarMenu; 127 128 private class MyOrientationEventListener 129 extends OrientationEventListener { 130 public MyOrientationEventListener(Context context) { 131 super(context); 132 } 133 134 @Override 135 public void onOrientationChanged(int orientation) { 136 // We keep the last known orientation. So if the user first orient 137 // the camera then point the camera to floor or sky, we still have 138 // the correct orientation. 139 if (orientation == ORIENTATION_UNKNOWN) return; 140 mLastRawOrientation = orientation; 141 mCurrentModule.onOrientationChanged(orientation); 142 } 143 } 144 145 private MediaSaveService mMediaSaveService; 146 private ServiceConnection mConnection = new ServiceConnection() { 147 @Override 148 public void onServiceConnected(ComponentName className, IBinder b) { 149 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 150 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 151 } 152 @Override 153 public void onServiceDisconnected(ComponentName className) { 154 mMediaSaveService = null; 155 }}; 156 157 // close activity when screen turns off 158 private BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() { 159 @Override 160 public void onReceive(Context context, Intent intent) { 161 finish(); 162 } 163 }; 164 165 private static BroadcastReceiver sScreenOffReceiver; 166 private static class ScreenOffReceiver extends BroadcastReceiver { 167 @Override 168 public void onReceive(Context context, Intent intent) { 169 sFirstStartAfterScreenOn = true; 170 } 171 } 172 173 public static boolean isFirstStartAfterScreenOn() { 174 return sFirstStartAfterScreenOn; 175 } 176 177 public static void resetFirstStartAfterScreenOn() { 178 sFirstStartAfterScreenOn = false; 179 } 180 181 private FilmStripView.Listener mFilmStripListener = 182 new FilmStripView.Listener() { 183 @Override 184 public void onDataPromoted(int dataID) { 185 removeData(dataID); 186 } 187 188 @Override 189 public void onDataDemoted(int dataID) { 190 removeData(dataID); 191 } 192 193 @Override 194 public void onDataFullScreenChange(int dataID, boolean full) { 195 } 196 197 @Override 198 public void onSwitchMode(boolean toCamera) { 199 mCurrentModule.onSwitchMode(toCamera); 200 if (toCamera) { 201 mActionBar.hide(); 202 } else { 203 mActionBar.show(); 204 } 205 } 206 207 @Override 208 public void onCurrentDataChanged(int dataID, boolean current) { 209 if (!current) { 210 hidePanoStitchingProgress(); 211 } else { 212 LocalData currentData = mDataAdapter.getLocalData(dataID); 213 if (currentData == null) { 214 Log.w(TAG, "Current data ID not found."); 215 hidePanoStitchingProgress(); 216 return; 217 } 218 updateActionBarMenu(dataID); 219 220 Uri contentUri = currentData.getContentUri(); 221 if (contentUri == null) { 222 hidePanoStitchingProgress(); 223 return; 224 } 225 int panoStitchingProgress = mPanoramaManager.getTaskProgress(contentUri); 226 if (panoStitchingProgress < 0) { 227 hidePanoStitchingProgress(); 228 return; 229 } 230 showPanoStitchingProgress(); 231 updateStitchingProgress(panoStitchingProgress); 232 } 233 } 234 }; 235 236 private void hidePanoStitchingProgress() { 237 mPanoStitchingPanel.setVisibility(View.GONE); 238 } 239 240 private void showPanoStitchingProgress() { 241 mPanoStitchingPanel.setVisibility(View.VISIBLE); 242 } 243 244 private void updateStitchingProgress(int progress) { 245 mBottomProgress.setProgress(progress); 246 } 247 248 /** 249 * According to the data type, make the menu items for supported operations 250 * visible. 251 * @param dataID the data ID of the current item. 252 */ 253 private void updateActionBarMenu(int dataID) { 254 LocalData currentData = mDataAdapter.getLocalData(dataID); 255 int type = currentData.getLocalDataType(); 256 257 if (mActionBarMenu == null) { 258 return; 259 } 260 261 int supported = 0; 262 switch (type) { 263 case LocalData.LOCAL_IMAGE: 264 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 265 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 266 | SUPPORT_SHOW_ON_MAP; 267 break; 268 case LocalData.LOCAL_VIDEO: 269 supported |= SUPPORT_DELETE | SUPPORT_INFO | SUPPORT_TRIM 270 | SUPPORT_MUTE; 271 break; 272 case LocalData.LOCAL_PHOTO_SPHERE: 273 supported |= SUPPORT_DELETE | SUPPORT_ROTATE | SUPPORT_INFO 274 | SUPPORT_CROP | SUPPORT_SETAS | SUPPORT_EDIT 275 | SUPPORT_SHOW_ON_MAP; 276 break; 277 default: 278 break; 279 } 280 281 setMenuItemVisible(mActionBarMenu, R.id.action_delete, 282 (supported & SUPPORT_DELETE) != 0); 283 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_ccw, 284 (supported & SUPPORT_ROTATE) != 0); 285 setMenuItemVisible(mActionBarMenu, R.id.action_rotate_cw, 286 (supported & SUPPORT_ROTATE) != 0); 287 setMenuItemVisible(mActionBarMenu, R.id.action_crop, 288 (supported & SUPPORT_CROP) != 0); 289 setMenuItemVisible(mActionBarMenu, R.id.action_trim, 290 (supported & SUPPORT_TRIM) != 0); 291 setMenuItemVisible(mActionBarMenu, R.id.action_mute, 292 (supported & SUPPORT_MUTE) != 0); 293 setMenuItemVisible(mActionBarMenu, R.id.action_setas, 294 (supported & SUPPORT_SETAS) != 0); 295 setMenuItemVisible(mActionBarMenu, R.id.action_edit, 296 (supported & SUPPORT_EDIT) != 0); 297 setMenuItemVisible(mActionBarMenu, R.id.action_details, 298 (supported & SUPPORT_INFO) != 0); 299 300 boolean itemHasLocation = currentData.getLatLong() != null; 301 setMenuItemVisible(mActionBarMenu, R.id.action_show_on_map, 302 itemHasLocation && (supported & SUPPORT_SHOW_ON_MAP) != 0); 303 } 304 305 private void setMenuItemVisible(Menu menu, int itemId, boolean visible) { 306 MenuItem item = menu.findItem(itemId); 307 if (item != null) 308 item.setVisible(visible); 309 } 310 311 private Runnable mDeletionRunnable = new Runnable() { 312 @Override 313 public void run() { 314 mDataAdapter.executeDeletion(CameraActivity.this); 315 } 316 }; 317 318 private ImageTaskManager.TaskListener mStitchingListener = 319 new ImageTaskManager.TaskListener() { 320 @Override 321 public void onTaskQueued(String filePath, final Uri imageUri) { 322 mMainHandler.post(new Runnable() { 323 @Override 324 public void run() { 325 notifyNewMedia(imageUri); 326 } 327 }); 328 } 329 330 @Override 331 public void onTaskDone(String filePath, final Uri imageUri) { 332 Log.v(TAG, "onTaskDone:" + filePath); 333 mMainHandler.post(new Runnable() { 334 @Override 335 public void run() { 336 int doneID = mDataAdapter.findDataByContentUri(imageUri); 337 int currentDataId = mFilmStripView.getCurrentId(); 338 339 if (currentDataId == doneID) { 340 hidePanoStitchingProgress(); 341 updateStitchingProgress(0); 342 } 343 344 mDataAdapter.refresh(getContentResolver(), imageUri); 345 } 346 }); 347 } 348 349 @Override 350 public void onTaskProgress( 351 String filePath, final Uri imageUri, final int progress) { 352 mMainHandler.post(new Runnable() { 353 @Override 354 public void run() { 355 int currentDataId = mFilmStripView.getCurrentId(); 356 if (currentDataId == -1) { 357 return; 358 } 359 if (imageUri.equals( 360 mDataAdapter.getLocalData(currentDataId).getContentUri())) { 361 updateStitchingProgress(progress); 362 } 363 } 364 }); 365 } 366 }; 367 368 public MediaSaveService getMediaSaveService() { 369 return mMediaSaveService; 370 } 371 372 public void notifyNewMedia(Uri uri) { 373 ContentResolver cr = getContentResolver(); 374 String mimeType = cr.getType(uri); 375 if (mimeType.startsWith("video/")) { 376 sendBroadcast(new Intent(CameraUtil.ACTION_NEW_VIDEO, uri)); 377 mDataAdapter.addNewVideo(cr, uri); 378 } else if (mimeType.startsWith("image/")) { 379 CameraUtil.broadcastNewPicture(this, uri); 380 mDataAdapter.addNewPhoto(cr, uri); 381 } else if (mimeType.startsWith("application/stitching-preview")) { 382 mDataAdapter.addNewPhoto(cr, uri); 383 } else { 384 android.util.Log.w(TAG, "Unknown new media with MIME type:" 385 + mimeType + ", uri:" + uri); 386 } 387 } 388 389 private void removeData(int dataID) { 390 mDataAdapter.removeData(CameraActivity.this, dataID); 391 mMainHandler.removeCallbacks(mDeletionRunnable); 392 mMainHandler.postDelayed(mDeletionRunnable, 3000); 393 } 394 395 private void bindMediaSaveService() { 396 Intent intent = new Intent(this, MediaSaveService.class); 397 startService(intent); // start service before binding it so the 398 // service won't be killed if we unbind it. 399 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 400 } 401 402 private void unbindMediaSaveService() { 403 if (mMediaSaveService != null) { 404 mMediaSaveService.setListener(null); 405 } 406 if (mConnection != null) { 407 unbindService(mConnection); 408 } 409 } 410 411 @Override 412 public boolean onCreateOptionsMenu(Menu menu) { 413 // Inflate the menu items for use in the action bar 414 MenuInflater inflater = getMenuInflater(); 415 inflater.inflate(R.menu.operations, menu); 416 mActionBarMenu = menu; 417 return super.onCreateOptionsMenu(menu); 418 } 419 420 @Override 421 public boolean onOptionsItemSelected(MenuItem item) { 422 int currentDataId = mFilmStripView.getCurrentId(); 423 if (currentDataId < 0) { 424 return false; 425 } 426 final LocalData localData = mDataAdapter.getLocalData(currentDataId); 427 428 // Handle presses on the action bar items 429 switch (item.getItemId()) { 430 case R.id.action_delete: 431 // TODO: add the functionality. 432 return true; 433 case R.id.action_edit: 434 // TODO: add the functionality. 435 return true; 436 case R.id.action_trim: 437 // TODO: add the functionality. 438 return true; 439 case R.id.action_mute: 440 // TODO: add the functionality. 441 return true; 442 case R.id.action_rotate_ccw: 443 // TODO: add the functionality. 444 return true; 445 case R.id.action_rotate_cw: 446 // TODO: add the functionality. 447 return true; 448 case R.id.action_crop: 449 // TODO: add the functionality. 450 return true; 451 case R.id.action_setas: 452 // TODO: add the functionality. 453 return true; 454 case R.id.action_details: 455 (new AsyncTask<Void, Void, MediaDetails>() { 456 @Override 457 protected MediaDetails doInBackground(Void... params) { 458 return localData.getMediaDetails(CameraActivity.this); 459 } 460 461 @Override 462 protected void onPostExecute(MediaDetails mediaDetails) { 463 DetailsDialog.create(CameraActivity.this, mediaDetails).show(); 464 } 465 }).execute(); 466 return true; 467 case R.id.action_show_on_map: 468 double[] latLong = localData.getLatLong(); 469 if (latLong != null) { 470 CameraUtil.showOnMap(this, latLong); 471 } 472 return true; 473 default: 474 return super.onOptionsItemSelected(item); 475 } 476 } 477 478 @Override 479 public void onCreate(Bundle state) { 480 super.onCreate(state); 481 getWindow().requestFeature(Window.FEATURE_ACTION_BAR); 482 setContentView(R.layout.camera_filmstrip); 483 mActionBar = getActionBar(); 484 // Hide action bar first since we are in full screen mode first. 485 mActionBar.hide(); 486 487 if (ApiHelper.HAS_ROTATION_ANIMATION) { 488 setRotationAnimation(); 489 } 490 // Check if this is in the secure camera mode. 491 Intent intent = getIntent(); 492 String action = intent.getAction(); 493 if (INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE.equals(action) 494 || ACTION_IMAGE_CAPTURE_SECURE.equals(action)) { 495 mSecureCamera = true; 496 } else { 497 mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); 498 } 499 500 if (mSecureCamera) { 501 // Change the window flags so that secure camera can show when locked 502 Window win = getWindow(); 503 WindowManager.LayoutParams params = win.getAttributes(); 504 params.flags |= WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED; 505 win.setAttributes(params); 506 507 // Filter for screen off so that we can finish secure camera activity 508 // when screen is off. 509 IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF); 510 registerReceiver(mScreenOffReceiver, filter); 511 // TODO: This static screen off event receiver is a workaround to the 512 // double onResume() invocation (onResume->onPause->onResume). We should 513 // find a better solution to this. 514 if (sScreenOffReceiver == null) { 515 sScreenOffReceiver = new ScreenOffReceiver(); 516 registerReceiver(sScreenOffReceiver, filter); 517 } 518 } 519 mPanoramaManager = new PanoramaStitchingManager(CameraActivity.this); 520 mPanoramaManager.addTaskListener(mStitchingListener); 521 LayoutInflater inflater = getLayoutInflater(); 522 View rootLayout = inflater.inflate(R.layout.camera, null, false); 523 mRootView = rootLayout.findViewById(R.id.camera_app_root); 524 mPanoStitchingPanel = findViewById(R.id.pano_stitching_progress_panel); 525 mBottomProgress = (ProgressBar) findViewById(R.id.pano_stitching_progress_bar); 526 mCameraPreviewData = new CameraPreviewData(rootLayout, 527 FilmStripView.ImageData.SIZE_FULL, 528 FilmStripView.ImageData.SIZE_FULL); 529 // Put a CameraPreviewData at the first position. 530 mWrappedDataAdapter = new FixedFirstDataAdapter( 531 new CameraDataAdapter(new ColorDrawable( 532 getResources().getColor(R.color.photo_placeholder))), 533 mCameraPreviewData); 534 mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); 535 mFilmStripView.setViewGap( 536 getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); 537 mPanoramaViewHelper = new PanoramaViewHelper(this); 538 mPanoramaViewHelper.onCreate(); 539 mFilmStripView.setPanoramaViewHelper(mPanoramaViewHelper); 540 // Set up the camera preview first so the preview shows up ASAP. 541 mFilmStripView.setListener(mFilmStripListener); 542 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 543 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 544 mCurrentModule = new VideoModule(); 545 mCurrentModuleIndex = CameraSwitcher.VIDEO_MODULE_INDEX; 546 } else { 547 mCurrentModule = new PhotoModule(); 548 } 549 mCurrentModule.init(this, mRootView); 550 mOrientationListener = new MyOrientationEventListener(this); 551 mMainHandler = new Handler(getMainLooper()); 552 bindMediaSaveService(); 553 554 if (!mSecureCamera) { 555 mDataAdapter = mWrappedDataAdapter; 556 mFilmStripView.setDataAdapter(mDataAdapter); 557 mDataAdapter.requestLoad(getContentResolver()); 558 } else { 559 // Put a lock placeholder as the last image by setting its date to 0. 560 ImageView v = (ImageView) getLayoutInflater().inflate( 561 R.layout.secure_album_placeholder, null); 562 mDataAdapter = new FixedLastDataAdapter( 563 mWrappedDataAdapter, 564 new SimpleViewData( 565 v, 566 v.getDrawable().getIntrinsicWidth(), 567 v.getDrawable().getIntrinsicHeight(), 568 0, 0)); 569 // Flush out all the original data. 570 mDataAdapter.flush(); 571 mFilmStripView.setDataAdapter(mDataAdapter); 572 } 573 } 574 575 private void setRotationAnimation() { 576 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 577 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 578 Window win = getWindow(); 579 WindowManager.LayoutParams winParams = win.getAttributes(); 580 winParams.rotationAnimation = rotationAnimation; 581 win.setAttributes(winParams); 582 } 583 584 @Override 585 public void onUserInteraction() { 586 super.onUserInteraction(); 587 mCurrentModule.onUserInteraction(); 588 } 589 590 @Override 591 public void onPause() { 592 mOrientationListener.disable(); 593 mCurrentModule.onPauseBeforeSuper(); 594 super.onPause(); 595 mCurrentModule.onPauseAfterSuper(); 596 } 597 598 @Override 599 public void onResume() { 600 if (Settings.System.getInt(getContentResolver(), 601 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 602 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 603 mAutoRotateScreen = false; 604 } else { 605 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 606 mAutoRotateScreen = true; 607 } 608 mOrientationListener.enable(); 609 mCurrentModule.onResumeBeforeSuper(); 610 super.onResume(); 611 mCurrentModule.onResumeAfterSuper(); 612 613 setSwipingEnabled(true); 614 } 615 616 @Override 617 public void onStart() { 618 super.onStart(); 619 620 mPanoramaViewHelper.onStart(); 621 } 622 623 @Override 624 protected void onStop() { 625 super.onStop(); 626 mPanoramaViewHelper.onStop(); 627 } 628 629 @Override 630 public void onDestroy() { 631 unbindMediaSaveService(); 632 if (mSecureCamera) unregisterReceiver(mScreenOffReceiver); 633 super.onDestroy(); 634 } 635 636 @Override 637 public void onConfigurationChanged(Configuration config) { 638 super.onConfigurationChanged(config); 639 mCurrentModule.onConfigurationChanged(config); 640 } 641 642 @Override 643 public boolean onKeyDown(int keyCode, KeyEvent event) { 644 if (mCurrentModule.onKeyDown(keyCode, event)) return true; 645 // Prevent software keyboard or voice search from showing up. 646 if (keyCode == KeyEvent.KEYCODE_SEARCH 647 || keyCode == KeyEvent.KEYCODE_MENU) { 648 if (event.isLongPress()) return true; 649 } 650 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 651 return true; 652 } 653 654 return super.onKeyDown(keyCode, event); 655 } 656 657 @Override 658 public boolean onKeyUp(int keyCode, KeyEvent event) { 659 if (mCurrentModule.onKeyUp(keyCode, event)) return true; 660 if (keyCode == KeyEvent.KEYCODE_MENU && mShowCameraPreview) { 661 return true; 662 } 663 return super.onKeyUp(keyCode, event); 664 } 665 666 public boolean isAutoRotateScreen() { 667 return mAutoRotateScreen; 668 } 669 670 protected void updateStorageSpace() { 671 mStorageSpace = Storage.getAvailableSpace(); 672 } 673 674 protected long getStorageSpace() { 675 return mStorageSpace; 676 } 677 678 protected void updateStorageSpaceAndHint() { 679 updateStorageSpace(); 680 updateStorageHint(mStorageSpace); 681 } 682 683 protected void updateStorageHint() { 684 updateStorageHint(mStorageSpace); 685 } 686 687 protected boolean updateStorageHintOnResume() { 688 return true; 689 } 690 691 protected void updateStorageHint(long storageSpace) { 692 String message = null; 693 if (storageSpace == Storage.UNAVAILABLE) { 694 message = getString(R.string.no_storage); 695 } else if (storageSpace == Storage.PREPARING) { 696 message = getString(R.string.preparing_sd); 697 } else if (storageSpace == Storage.UNKNOWN_SIZE) { 698 message = getString(R.string.access_sd_fail); 699 } else if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD) { 700 message = getString(R.string.spaceIsLow_content); 701 } 702 703 if (message != null) { 704 if (mStorageHint == null) { 705 mStorageHint = OnScreenHint.makeText(this, message); 706 } else { 707 mStorageHint.setText(message); 708 } 709 mStorageHint.show(); 710 } else if (mStorageHint != null) { 711 mStorageHint.cancel(); 712 mStorageHint = null; 713 } 714 } 715 716 protected void setResultEx(int resultCode) { 717 mResultCodeForTesting = resultCode; 718 setResult(resultCode); 719 } 720 721 protected void setResultEx(int resultCode, Intent data) { 722 mResultCodeForTesting = resultCode; 723 mResultDataForTesting = data; 724 setResult(resultCode, data); 725 } 726 727 public int getResultCode() { 728 return mResultCodeForTesting; 729 } 730 731 public Intent getResultData() { 732 return mResultDataForTesting; 733 } 734 735 public boolean isSecureCamera() { 736 return mSecureCamera; 737 } 738 739 @Override 740 public void onCameraSelected(int i) { 741 if (mCurrentModuleIndex == i) return; 742 743 CameraHolder.instance().keep(); 744 closeModule(mCurrentModule); 745 mCurrentModuleIndex = i; 746 switch (i) { 747 case CameraSwitcher.VIDEO_MODULE_INDEX: 748 mCurrentModule = new VideoModule(); 749 break; 750 case CameraSwitcher.PHOTO_MODULE_INDEX: 751 mCurrentModule = new PhotoModule(); 752 break; 753 case CameraSwitcher.LIGHTCYCLE_MODULE_INDEX: 754 mCurrentModule = PhotoSphereHelper.createPanoramaModule(); 755 break; 756 default: 757 break; 758 } 759 760 openModule(mCurrentModule); 761 mCurrentModule.onOrientationChanged(mLastRawOrientation); 762 if (mMediaSaveService != null) { 763 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 764 } 765 } 766 767 private void openModule(CameraModule module) { 768 module.init(this, mRootView); 769 module.onResumeBeforeSuper(); 770 module.onResumeAfterSuper(); 771 } 772 773 private void closeModule(CameraModule module) { 774 module.onPauseBeforeSuper(); 775 module.onPauseAfterSuper(); 776 ((ViewGroup) mRootView).removeAllViews(); 777 } 778 779 @Override 780 public void onShowSwitcherPopup() { 781 } 782 783 public void setSwipingEnabled(boolean enable) { 784 mCameraPreviewData.lockPreview(!enable); 785 } 786 787 // Accessor methods for getting latency times used in performance testing 788 public long getAutoFocusTime() { 789 return (mCurrentModule instanceof PhotoModule) ? 790 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 791 } 792 793 public long getShutterLag() { 794 return (mCurrentModule instanceof PhotoModule) ? 795 ((PhotoModule) mCurrentModule).mShutterLag : -1; 796 } 797 798 public long getShutterToPictureDisplayedTime() { 799 return (mCurrentModule instanceof PhotoModule) ? 800 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 801 } 802 803 public long getPictureDisplayedToJpegCallbackTime() { 804 return (mCurrentModule instanceof PhotoModule) ? 805 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 806 } 807 808 public long getJpegCallbackFinishTime() { 809 return (mCurrentModule instanceof PhotoModule) ? 810 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 811 } 812 813 public long getCaptureStartTime() { 814 return (mCurrentModule instanceof PhotoModule) ? 815 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 816 } 817 818 public boolean isRecording() { 819 return (mCurrentModule instanceof VideoModule) ? 820 ((VideoModule) mCurrentModule).isRecording() : false; 821 } 822} 823