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