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