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