CameraActivity.java revision 8d2d0f5f0f7fffce9dbcc70da6924af545eba26d
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.animation.AnimatorListenerAdapter; 21import android.animation.ObjectAnimator; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.Intent; 25import android.content.ServiceConnection; 26import android.content.pm.ActivityInfo; 27import android.content.res.Configuration; 28import android.graphics.drawable.Drawable; 29import android.os.Bundle; 30import android.os.IBinder; 31import android.provider.MediaStore; 32import android.provider.Settings; 33import android.view.KeyEvent; 34import android.view.LayoutInflater; 35import android.view.MotionEvent; 36import android.view.OrientationEventListener; 37import android.view.View; 38import android.view.ViewGroup; 39import android.view.Window; 40import android.view.WindowManager; 41import android.widget.FrameLayout; 42 43import com.android.camera.ui.CameraSwitcher; 44import com.android.camera.ui.RotatableLayout; 45import com.android.gallery3d.R; 46import com.android.gallery3d.app.PhotoPage; 47import com.android.gallery3d.common.ApiHelper; 48import com.android.gallery3d.util.LightCycleHelper; 49 50public class CameraActivity extends ActivityBase 51 implements CameraSwitcher.CameraSwitchListener { 52 public static final int PHOTO_MODULE_INDEX = 0; 53 public static final int VIDEO_MODULE_INDEX = 1; 54 public static final int PANORAMA_MODULE_INDEX = 2; 55 public static final int LIGHTCYCLE_MODULE_INDEX = 3; 56 57 CameraModule mCurrentModule; 58 private FrameLayout mFrame; 59 private ShutterButton mShutter; 60 private CameraSwitcher mSwitcher; 61 private View mCameraControls; 62 private View mControlsBackground; 63 private View mPieMenuButton; 64 private Drawable[] mDrawables; 65 private int mCurrentModuleIndex; 66 private MotionEvent mDown; 67 private boolean mAutoRotateScreen; 68 private int mHeightOrWidth = -1; 69 70 private MyOrientationEventListener mOrientationListener; 71 // The degrees of the device rotated clockwise from its natural orientation. 72 private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 73 74 private MediaSaveService mMediaSaveService; 75 private ServiceConnection mConnection = new ServiceConnection() { 76 @Override 77 public void onServiceConnected(ComponentName className, IBinder b) { 78 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); 79 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 80 } 81 @Override 82 public void onServiceDisconnected(ComponentName className) { 83 mMediaSaveService = null; 84 }}; 85 86 private static final String TAG = "CAM_activity"; 87 88 private static final int[] DRAW_IDS = { 89 R.drawable.ic_switch_camera, 90 R.drawable.ic_switch_video, 91 R.drawable.ic_switch_pan, 92 R.drawable.ic_switch_photosphere 93 }; 94 95 @Override 96 public void onCreate(Bundle state) { 97 super.onCreate(state); 98 setContentView(R.layout.camera_main); 99 mFrame = (FrameLayout) findViewById(R.id.camera_app_root); 100 mDrawables = new Drawable[DRAW_IDS.length]; 101 for (int i = 0; i < DRAW_IDS.length; i++) { 102 mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]); 103 } 104 init(); 105 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 106 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 107 mCurrentModule = new VideoModule(); 108 mCurrentModuleIndex = VIDEO_MODULE_INDEX; 109 } else { 110 mCurrentModule = new PhotoModule(); 111 mCurrentModuleIndex = PHOTO_MODULE_INDEX; 112 } 113 mCurrentModule.init(this, mFrame, true); 114 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 115 mOrientationListener = new MyOrientationEventListener(this); 116 bindMediaSaveService(); 117 } 118 119 public void init() { 120 mControlsBackground = findViewById(R.id.blocker); 121 mCameraControls = findViewById(R.id.camera_controls); 122 mShutter = (ShutterButton) findViewById(R.id.shutter_button); 123 mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher); 124 mPieMenuButton = findViewById(R.id.menu); 125 int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this) 126 ? DRAW_IDS.length : DRAW_IDS.length - 1); 127 if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--; 128 129 int[] drawids = new int[totaldrawid]; 130 int[] moduleids = new int[totaldrawid]; 131 int ix = 0; 132 for (int i = 0; i < mDrawables.length; i++) { 133 if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) { 134 continue; // not enabled, so don't add to UI 135 } 136 if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) { 137 continue; // not enabled, so don't add to UI 138 } 139 moduleids[ix] = i; 140 drawids[ix++] = DRAW_IDS[i]; 141 } 142 mSwitcher.setIds(moduleids, drawids); 143 mSwitcher.setSwitchListener(this); 144 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 145 } 146 147 @Override 148 public void onDestroy() { 149 unbindMediaSaveService(); 150 super.onDestroy(); 151 } 152 153 // Return whether the auto-rotate screen in system settings 154 // is turned on. 155 public boolean isAutoRotateScreen() { 156 return mAutoRotateScreen; 157 } 158 159 private class MyOrientationEventListener 160 extends OrientationEventListener { 161 public MyOrientationEventListener(Context context) { 162 super(context); 163 } 164 165 @Override 166 public void onOrientationChanged(int orientation) { 167 // We keep the last known orientation. So if the user first orient 168 // the camera then point the camera to floor or sky, we still have 169 // the correct orientation. 170 if (orientation == ORIENTATION_UNKNOWN) return; 171 mLastRawOrientation = orientation; 172 mCurrentModule.onOrientationChanged(orientation); 173 } 174 } 175 176 private ObjectAnimator mCameraSwitchAnimator; 177 178 @Override 179 public void onCameraSelected(final int i) { 180 if (mPaused) return; 181 if (i != mCurrentModuleIndex) { 182 mPaused = true; 183 CameraScreenNail screenNail = getCameraScreenNail(); 184 if (screenNail != null) { 185 if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) { 186 mCameraSwitchAnimator.cancel(); 187 } 188 mCameraSwitchAnimator = ObjectAnimator.ofFloat( 189 screenNail, "alpha", screenNail.getAlpha(), 0f); 190 mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() { 191 @Override 192 public void onAnimationEnd(Animator animation) { 193 super.onAnimationEnd(animation); 194 doChangeCamera(i); 195 } 196 }); 197 mCameraSwitchAnimator.start(); 198 } else { 199 doChangeCamera(i); 200 } 201 } 202 } 203 204 private void doChangeCamera(int i) { 205 boolean canReuse = canReuseScreenNail(); 206 CameraHolder.instance().keep(); 207 closeModule(mCurrentModule); 208 mCurrentModuleIndex = i; 209 switch (i) { 210 case VIDEO_MODULE_INDEX: 211 mCurrentModule = new VideoModule(); 212 break; 213 case PHOTO_MODULE_INDEX: 214 mCurrentModule = new PhotoModule(); 215 break; 216 case PANORAMA_MODULE_INDEX: 217 mCurrentModule = new PanoramaModule(); 218 break; 219 case LIGHTCYCLE_MODULE_INDEX: 220 mCurrentModule = LightCycleHelper.createPanoramaModule(); 221 break; 222 } 223 showPieMenuButton(mCurrentModule.needsPieMenu()); 224 225 openModule(mCurrentModule, canReuse); 226 mCurrentModule.onOrientationChanged(mLastRawOrientation); 227 if (mMediaSaveService != null) { 228 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); 229 } 230 getCameraScreenNail().setAlpha(0f); 231 getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn); 232 } 233 234 public void showPieMenuButton(boolean show) { 235 if (show) { 236 findViewById(R.id.blocker).setVisibility(View.VISIBLE); 237 findViewById(R.id.menu).setVisibility(View.VISIBLE); 238 findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE); 239 } else { 240 findViewById(R.id.blocker).setVisibility(View.INVISIBLE); 241 findViewById(R.id.menu).setVisibility(View.INVISIBLE); 242 findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE); 243 } 244 } 245 246 private Runnable mOnFrameDrawn = new Runnable() { 247 248 @Override 249 public void run() { 250 runOnUiThread(mFadeInCameraScreenNail); 251 } 252 }; 253 254 private Runnable mFadeInCameraScreenNail = new Runnable() { 255 256 @Override 257 public void run() { 258 mCameraSwitchAnimator = ObjectAnimator.ofFloat( 259 getCameraScreenNail(), "alpha", 0f, 1f); 260 mCameraSwitchAnimator.setStartDelay(50); 261 mCameraSwitchAnimator.start(); 262 } 263 }; 264 265 @Override 266 public void onShowSwitcherPopup() { 267 mCurrentModule.onShowSwitcherPopup(); 268 } 269 270 private void openModule(CameraModule module, boolean canReuse) { 271 module.init(this, mFrame, canReuse && canReuseScreenNail()); 272 mPaused = false; 273 module.onResumeBeforeSuper(); 274 module.onResumeAfterSuper(); 275 } 276 277 private void closeModule(CameraModule module) { 278 module.onPauseBeforeSuper(); 279 module.onPauseAfterSuper(); 280 mFrame.removeAllViews(); 281 } 282 283 public ShutterButton getShutterButton() { 284 return mShutter; 285 } 286 287 public void hideUI() { 288 mCameraControls.setVisibility(View.INVISIBLE); 289 hideSwitcher(); 290 mShutter.setVisibility(View.GONE); 291 } 292 293 public void showUI() { 294 mCameraControls.setVisibility(View.VISIBLE); 295 showSwitcher(); 296 mShutter.setVisibility(View.VISIBLE); 297 // Force a layout change to show shutter button 298 mShutter.requestLayout(); 299 } 300 301 public void hideSwitcher() { 302 mSwitcher.closePopup(); 303 mSwitcher.setVisibility(View.INVISIBLE); 304 } 305 306 public void showSwitcher() { 307 if (mCurrentModule.needsSwitcher()) { 308 mSwitcher.setVisibility(View.VISIBLE); 309 } 310 } 311 312 public boolean isInCameraApp() { 313 return mShowCameraAppView; 314 } 315 316 @Override 317 public void onConfigurationChanged(Configuration config) { 318 super.onConfigurationChanged(config); 319 320 ViewGroup appRoot = (ViewGroup) findViewById(R.id.content); 321 boolean landscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE); 322 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) appRoot.getLayoutParams(); 323 int offset = getResources().getDimensionPixelSize(R.dimen.margin_systemui_offset); 324 int navBarHeight = getResources().getDimensionPixelSize(R.dimen.navigation_bar_height); 325 if (landscape) { 326 lp.rightMargin = offset; 327 } else { 328 lp.rightMargin = 0; 329 } 330 appRoot.setLayoutParams(lp); 331 332 // Set padding to move camera controls away from the edge of the screen 333 // so that they are in the same place as if there was a navigation bar between 334 // the screen edge and the controls 335 if (landscape) { 336 mCameraControls.setPadding(navBarHeight, 0, 0, 0); 337 } else { 338 mCameraControls.setPadding(0, navBarHeight, 0, 0); 339 } 340 mCurrentModule.onConfigurationChanged(config); 341 } 342 343 @Override 344 public void onPause() { 345 mPaused = true; 346 mOrientationListener.disable(); 347 mCurrentModule.onPauseBeforeSuper(); 348 super.onPause(); 349 mCurrentModule.onPauseAfterSuper(); 350 } 351 352 @Override 353 public void onResume() { 354 mPaused = false; 355 if (Settings.System.getInt(getContentResolver(), 356 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off 357 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); 358 mAutoRotateScreen = false; 359 } else { 360 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); 361 mAutoRotateScreen = true; 362 } 363 mOrientationListener.enable(); 364 mCurrentModule.onResumeBeforeSuper(); 365 super.onResume(); 366 mCurrentModule.onResumeAfterSuper(); 367 } 368 369 private void bindMediaSaveService() { 370 Intent intent = new Intent(this, MediaSaveService.class); 371 startService(intent); // start service before binding it so the 372 // service won't be killed if we unbind it. 373 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); 374 } 375 376 private void unbindMediaSaveService() { 377 mMediaSaveService.setListener(null); 378 unbindService(mConnection); 379 } 380 381 @Override 382 protected void onFullScreenChanged(boolean full) { 383 if (full) { 384 showUI(); 385 } else { 386 hideUI(); 387 } 388 super.onFullScreenChanged(full); 389 if (ApiHelper.HAS_ROTATION_ANIMATION) { 390 setRotationAnimation(full); 391 } 392 mCurrentModule.onFullScreenChanged(full); 393 } 394 395 private void setRotationAnimation(boolean fullscreen) { 396 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; 397 if (fullscreen) { 398 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; 399 } 400 Window win = getWindow(); 401 WindowManager.LayoutParams winParams = win.getAttributes(); 402 winParams.rotationAnimation = rotationAnimation; 403 win.setAttributes(winParams); 404 } 405 406 @Override 407 protected void onStop() { 408 super.onStop(); 409 mCurrentModule.onStop(); 410 getStateManager().clearTasks(); 411 } 412 413 @Override 414 protected void onNewIntent(Intent intent) { 415 super.onNewIntent(intent); 416 getStateManager().clearActivityResult(); 417 } 418 419 @Override 420 protected void installIntentFilter() { 421 super.installIntentFilter(); 422 mCurrentModule.installIntentFilter(); 423 } 424 425 @Override 426 protected void onActivityResult( 427 int requestCode, int resultCode, Intent data) { 428 // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED 429 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED 430 && !(getStateManager().getTopState() instanceof PhotoPage)) { 431 resultCode = RESULT_CANCELED; 432 } 433 super.onActivityResult(requestCode, resultCode, data); 434 // Unmap cancel vs. reset 435 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { 436 resultCode = RESULT_CANCELED; 437 } 438 mCurrentModule.onActivityResult(requestCode, resultCode, data); 439 } 440 441 // Preview area is touched. Handle touch focus. 442 @Override 443 protected void onSingleTapUp(View view, int x, int y) { 444 mCurrentModule.onSingleTapUp(view, x, y); 445 } 446 447 @Override 448 public void onBackPressed() { 449 if (!mCurrentModule.onBackPressed()) { 450 super.onBackPressed(); 451 } 452 } 453 454 @Override 455 public boolean onKeyDown(int keyCode, KeyEvent event) { 456 return mCurrentModule.onKeyDown(keyCode, event) 457 || super.onKeyDown(keyCode, event); 458 } 459 460 @Override 461 public boolean onKeyUp(int keyCode, KeyEvent event) { 462 return mCurrentModule.onKeyUp(keyCode, event) 463 || super.onKeyUp(keyCode, event); 464 } 465 466 public void cancelActivityTouchHandling() { 467 if (mDown != null) { 468 MotionEvent cancel = MotionEvent.obtain(mDown); 469 cancel.setAction(MotionEvent.ACTION_CANCEL); 470 super.dispatchTouchEvent(cancel); 471 } 472 } 473 474 @Override 475 public boolean dispatchTouchEvent(MotionEvent m) { 476 if (m.getActionMasked() == MotionEvent.ACTION_DOWN) { 477 mDown = m; 478 } 479 if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) { 480 return mSwitcher.onTouch(null, m); 481 } else { 482 return mCameraControls.dispatchTouchEvent(m) 483 || mCurrentModule.dispatchTouchEvent(m); 484 } 485 } 486 487 @Override 488 public void startActivityForResult(Intent intent, int requestCode) { 489 Intent proxyIntent = new Intent(this, ProxyLauncher.class); 490 proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 491 proxyIntent.putExtra(Intent.EXTRA_INTENT, intent); 492 super.startActivityForResult(proxyIntent, requestCode); 493 } 494 495 public boolean superDispatchTouchEvent(MotionEvent m) { 496 return super.dispatchTouchEvent(m); 497 } 498 499 // Preview texture has been copied. Now camera can be released and the 500 // animation can be started. 501 @Override 502 public void onPreviewTextureCopied() { 503 mCurrentModule.onPreviewTextureCopied(); 504 } 505 506 @Override 507 public void onCaptureTextureCopied() { 508 mCurrentModule.onCaptureTextureCopied(); 509 } 510 511 @Override 512 public void onUserInteraction() { 513 super.onUserInteraction(); 514 mCurrentModule.onUserInteraction(); 515 } 516 517 @Override 518 protected boolean updateStorageHintOnResume() { 519 return mCurrentModule.updateStorageHintOnResume(); 520 } 521 522 @Override 523 public void updateCameraAppView() { 524 super.updateCameraAppView(); 525 mCurrentModule.updateCameraAppView(); 526 } 527 528 private boolean canReuseScreenNail() { 529 return mCurrentModuleIndex == PHOTO_MODULE_INDEX 530 || mCurrentModuleIndex == VIDEO_MODULE_INDEX 531 || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX; 532 } 533 534 @Override 535 public boolean isPanoramaActivity() { 536 return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX); 537 } 538 539 // Accessor methods for getting latency times used in performance testing 540 public long getAutoFocusTime() { 541 return (mCurrentModule instanceof PhotoModule) ? 542 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 543 } 544 545 public long getShutterLag() { 546 return (mCurrentModule instanceof PhotoModule) ? 547 ((PhotoModule) mCurrentModule).mShutterLag : -1; 548 } 549 550 public long getShutterToPictureDisplayedTime() { 551 return (mCurrentModule instanceof PhotoModule) ? 552 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 553 } 554 555 public long getPictureDisplayedToJpegCallbackTime() { 556 return (mCurrentModule instanceof PhotoModule) ? 557 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 558 } 559 560 public long getJpegCallbackFinishTime() { 561 return (mCurrentModule instanceof PhotoModule) ? 562 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 563 } 564 565 public long getCaptureStartTime() { 566 return (mCurrentModule instanceof PhotoModule) ? 567 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 568 } 569 570 public boolean isRecording() { 571 return (mCurrentModule instanceof VideoModule) ? 572 ((VideoModule) mCurrentModule).isRecording() : false; 573 } 574 575 public CameraScreenNail getCameraScreenNail() { 576 return (CameraScreenNail) mCameraScreenNail; 577 } 578 579 public MediaSaveService getMediaSaveService() { 580 return mMediaSaveService; 581 } 582} 583