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