CameraActivity.java revision e319aeabdc825f221df5af8a6ec080c6f5d41a94
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.Context; 23import android.content.Intent; 24import android.content.res.Configuration; 25import android.graphics.drawable.Drawable; 26import android.os.Bundle; 27import android.provider.MediaStore; 28import android.view.KeyEvent; 29import android.view.LayoutInflater; 30import android.view.MotionEvent; 31import android.view.OrientationEventListener; 32import android.view.View; 33import android.view.ViewGroup; 34import android.widget.FrameLayout; 35 36import com.android.camera.ui.CameraSwitcher; 37import com.android.gallery3d.app.PhotoPage; 38import com.android.gallery3d.common.ApiHelper; 39import com.android.gallery3d.util.LightCycleHelper; 40 41public class CameraActivity extends ActivityBase 42 implements CameraSwitcher.CameraSwitchListener { 43 public static final int PHOTO_MODULE_INDEX = 0; 44 public static final int VIDEO_MODULE_INDEX = 1; 45 public static final int PANORAMA_MODULE_INDEX = 2; 46 public static final int LIGHTCYCLE_MODULE_INDEX = 3; 47 48 CameraModule mCurrentModule; 49 private FrameLayout mFrame; 50 private ShutterButton mShutter; 51 private CameraSwitcher mSwitcher; 52 private View mShutterSwitcher; 53 private View mControlsBackground; 54 private Drawable[] mDrawables; 55 private int mCurrentModuleIndex; 56 private MotionEvent mDown; 57 58 private MyOrientationEventListener mOrientationListener; 59 // The degrees of the device rotated clockwise from its natural orientation. 60 private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; 61 62 private static final String TAG = "CAM_activity"; 63 64 private static final int[] DRAW_IDS = { 65 R.drawable.ic_switch_camera, 66 R.drawable.ic_switch_video, 67 R.drawable.ic_switch_pan, 68 R.drawable.ic_switch_photosphere 69 }; 70 71 @Override 72 public void onCreate(Bundle state) { 73 super.onCreate(state); 74 setContentView(R.layout.camera_main); 75 mFrame = (FrameLayout) findViewById(R.id.main_content); 76 mDrawables = new Drawable[DRAW_IDS.length]; 77 for (int i = 0; i < DRAW_IDS.length; i++) { 78 mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]); 79 } 80 init(); 81 if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) 82 || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { 83 mCurrentModule = new VideoModule(); 84 mCurrentModuleIndex = VIDEO_MODULE_INDEX; 85 } else { 86 mCurrentModule = new PhotoModule(); 87 mCurrentModuleIndex = PHOTO_MODULE_INDEX; 88 } 89 mCurrentModule.init(this, mFrame, true); 90 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 91 mOrientationListener = new MyOrientationEventListener(this); 92 } 93 94 public void init() { 95 mControlsBackground = findViewById(R.id.controls); 96 mShutterSwitcher = findViewById(R.id.camera_shutter_switcher); 97 mShutter = (ShutterButton) findViewById(R.id.shutter_button); 98 mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher); 99 int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this) 100 ? DRAW_IDS.length : DRAW_IDS.length - 1); 101 if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--; 102 103 int[] drawids = new int[totaldrawid]; 104 int[] moduleids = new int[totaldrawid]; 105 int ix = 0; 106 for (int i = 0; i < mDrawables.length; i++) { 107 if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) { 108 continue; // not enabled, so don't add to UI 109 } 110 if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) { 111 continue; // not enabled, so don't add to UI 112 } 113 moduleids[ix] = i; 114 drawids[ix++] = DRAW_IDS[i]; 115 } 116 mSwitcher.setIds(moduleids, drawids); 117 mSwitcher.setSwitchListener(this); 118 mSwitcher.setCurrentIndex(mCurrentModuleIndex); 119 } 120 121 private class MyOrientationEventListener 122 extends OrientationEventListener { 123 public MyOrientationEventListener(Context context) { 124 super(context); 125 } 126 127 @Override 128 public void onOrientationChanged(int orientation) { 129 // We keep the last known orientation. So if the user first orient 130 // the camera then point the camera to floor or sky, we still have 131 // the correct orientation. 132 if (orientation == ORIENTATION_UNKNOWN) return; 133 mLastRawOrientation = orientation; 134 mCurrentModule.onOrientationChanged(orientation); 135 } 136 } 137 138 private ObjectAnimator mCameraSwitchAnimator; 139 140 @Override 141 public void onCameraSelected(final int i) { 142 if (mPaused) return; 143 if (i != mCurrentModuleIndex) { 144 mPaused = true; 145 CameraScreenNail screenNail = getCameraScreenNail(); 146 if (screenNail != null) { 147 if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) { 148 mCameraSwitchAnimator.cancel(); 149 } 150 mCameraSwitchAnimator = ObjectAnimator.ofFloat( 151 screenNail, "alpha", screenNail.getAlpha(), 0f); 152 mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() { 153 @Override 154 public void onAnimationEnd(Animator animation) { 155 super.onAnimationEnd(animation); 156 doChangeCamera(i); 157 } 158 }); 159 mCameraSwitchAnimator.start(); 160 } else { 161 doChangeCamera(i); 162 } 163 } 164 } 165 166 private void doChangeCamera(int i) { 167 boolean canReuse = canReuseScreenNail(); 168 CameraHolder.instance().keep(); 169 closeModule(mCurrentModule); 170 mCurrentModuleIndex = i; 171 switch (i) { 172 case VIDEO_MODULE_INDEX: 173 mCurrentModule = new VideoModule(); 174 break; 175 case PHOTO_MODULE_INDEX: 176 mCurrentModule = new PhotoModule(); 177 break; 178 case PANORAMA_MODULE_INDEX: 179 mCurrentModule = new PanoramaModule(); 180 break; 181 case LIGHTCYCLE_MODULE_INDEX: 182 mCurrentModule = LightCycleHelper.createPanoramaModule(); 183 break; 184 } 185 openModule(mCurrentModule, canReuse); 186 mCurrentModule.onOrientationChanged(mLastRawOrientation); 187 getCameraScreenNail().setAlpha(0f); 188 getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn); 189 } 190 191 private Runnable mOnFrameDrawn = new Runnable() { 192 193 @Override 194 public void run() { 195 runOnUiThread(mFadeInCameraScreenNail); 196 } 197 }; 198 199 private Runnable mFadeInCameraScreenNail = new Runnable() { 200 201 @Override 202 public void run() { 203 mCameraSwitchAnimator = ObjectAnimator.ofFloat( 204 getCameraScreenNail(), "alpha", 0f, 1f); 205 mCameraSwitchAnimator.setStartDelay(50); 206 mCameraSwitchAnimator.start(); 207 } 208 }; 209 210 @Override 211 public void onShowSwitcherPopup() { 212 mCurrentModule.onShowSwitcherPopup(); 213 } 214 215 private void openModule(CameraModule module, boolean canReuse) { 216 module.init(this, mFrame, canReuse && canReuseScreenNail()); 217 mPaused = false; 218 module.onResumeBeforeSuper(); 219 module.onResumeAfterSuper(); 220 } 221 222 private void closeModule(CameraModule module) { 223 module.onPauseBeforeSuper(); 224 module.onPauseAfterSuper(); 225 mFrame.removeAllViews(); 226 } 227 228 public ShutterButton getShutterButton() { 229 return mShutter; 230 } 231 232 public void hideUI() { 233 mControlsBackground.setVisibility(View.INVISIBLE); 234 hideSwitcher(); 235 mShutter.setVisibility(View.GONE); 236 } 237 238 public void showUI() { 239 mControlsBackground.setVisibility(View.VISIBLE); 240 showSwitcher(); 241 mShutter.setVisibility(View.VISIBLE); 242 // Force a layout change to show shutter button 243 mShutter.requestLayout(); 244 } 245 246 public void hideSwitcher() { 247 mSwitcher.closePopup(); 248 mSwitcher.setVisibility(View.INVISIBLE); 249 } 250 251 public void showSwitcher() { 252 if (mCurrentModule.needsSwitcher()) { 253 mSwitcher.setVisibility(View.VISIBLE); 254 } 255 } 256 257 public boolean isInCameraApp() { 258 return mShowCameraAppView; 259 } 260 261 @Override 262 public void onConfigurationChanged(Configuration config) { 263 super.onConfigurationChanged(config); 264 265 ViewGroup appRoot = (ViewGroup) findViewById(R.id.content); 266 // remove old switcher, shutter and shutter icon 267 View cameraControlsView = findViewById(R.id.camera_shutter_switcher); 268 appRoot.removeView(cameraControlsView); 269 270 // create new layout with the current orientation 271 LayoutInflater inflater = getLayoutInflater(); 272 inflater.inflate(R.layout.camera_shutter_switcher, appRoot); 273 init(); 274 275 if (mShowCameraAppView) { 276 showUI(); 277 } else { 278 hideUI(); 279 } 280 mCurrentModule.onConfigurationChanged(config); 281 } 282 283 @Override 284 public void onPause() { 285 mPaused = true; 286 mOrientationListener.disable(); 287 mCurrentModule.onPauseBeforeSuper(); 288 super.onPause(); 289 mCurrentModule.onPauseAfterSuper(); 290 } 291 292 @Override 293 public void onResume() { 294 mPaused = false; 295 mOrientationListener.enable(); 296 mCurrentModule.onResumeBeforeSuper(); 297 super.onResume(); 298 mCurrentModule.onResumeAfterSuper(); 299 } 300 301 @Override 302 protected void onFullScreenChanged(boolean full) { 303 if (full) { 304 showUI(); 305 } else { 306 hideUI(); 307 } 308 super.onFullScreenChanged(full); 309 mCurrentModule.onFullScreenChanged(full); 310 } 311 312 @Override 313 protected void onStop() { 314 super.onStop(); 315 mCurrentModule.onStop(); 316 getStateManager().clearTasks(); 317 } 318 319 @Override 320 protected void onNewIntent(Intent intent) { 321 super.onNewIntent(intent); 322 getStateManager().clearActivityResult(); 323 } 324 325 @Override 326 protected void installIntentFilter() { 327 super.installIntentFilter(); 328 mCurrentModule.installIntentFilter(); 329 } 330 331 @Override 332 protected void onActivityResult( 333 int requestCode, int resultCode, Intent data) { 334 // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED 335 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED 336 && !(getStateManager().getTopState() instanceof PhotoPage)) { 337 resultCode = RESULT_CANCELED; 338 } 339 super.onActivityResult(requestCode, resultCode, data); 340 // Unmap cancel vs. reset 341 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { 342 resultCode = RESULT_CANCELED; 343 } 344 mCurrentModule.onActivityResult(requestCode, resultCode, data); 345 } 346 347 // Preview area is touched. Handle touch focus. 348 @Override 349 protected void onSingleTapUp(View view, int x, int y) { 350 mCurrentModule.onSingleTapUp(view, x, y); 351 } 352 353 @Override 354 public void onBackPressed() { 355 if (!mCurrentModule.onBackPressed()) { 356 super.onBackPressed(); 357 } 358 } 359 360 @Override 361 public boolean onKeyDown(int keyCode, KeyEvent event) { 362 return mCurrentModule.onKeyDown(keyCode, event) 363 || super.onKeyDown(keyCode, event); 364 } 365 366 @Override 367 public boolean onKeyUp(int keyCode, KeyEvent event) { 368 return mCurrentModule.onKeyUp(keyCode, event) 369 || super.onKeyUp(keyCode, event); 370 } 371 372 public void cancelActivityTouchHandling() { 373 if (mDown != null) { 374 MotionEvent cancel = MotionEvent.obtain(mDown); 375 cancel.setAction(MotionEvent.ACTION_CANCEL); 376 super.dispatchTouchEvent(cancel); 377 } 378 } 379 380 @Override 381 public boolean dispatchTouchEvent(MotionEvent m) { 382 if (m.getActionMasked() == MotionEvent.ACTION_DOWN) { 383 mDown = m; 384 } 385 if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) { 386 return mSwitcher.onTouch(null, m); 387 } else { 388 return mShutterSwitcher.dispatchTouchEvent(m) 389 || mCurrentModule.dispatchTouchEvent(m); 390 } 391 } 392 393 @Override 394 public void startActivityForResult(Intent intent, int requestCode) { 395 Intent proxyIntent = new Intent(this, ProxyLauncher.class); 396 proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 397 proxyIntent.putExtra(Intent.EXTRA_INTENT, intent); 398 super.startActivityForResult(proxyIntent, requestCode); 399 } 400 401 public boolean superDispatchTouchEvent(MotionEvent m) { 402 return super.dispatchTouchEvent(m); 403 } 404 405 // Preview texture has been copied. Now camera can be released and the 406 // animation can be started. 407 @Override 408 public void onPreviewTextureCopied() { 409 mCurrentModule.onPreviewTextureCopied(); 410 } 411 412 @Override 413 public void onCaptureTextureCopied() { 414 mCurrentModule.onCaptureTextureCopied(); 415 } 416 417 @Override 418 public void onUserInteraction() { 419 super.onUserInteraction(); 420 mCurrentModule.onUserInteraction(); 421 } 422 423 @Override 424 protected boolean updateStorageHintOnResume() { 425 return mCurrentModule.updateStorageHintOnResume(); 426 } 427 428 @Override 429 public void updateCameraAppView() { 430 super.updateCameraAppView(); 431 mCurrentModule.updateCameraAppView(); 432 } 433 434 private boolean canReuseScreenNail() { 435 return mCurrentModuleIndex == PHOTO_MODULE_INDEX 436 || mCurrentModuleIndex == VIDEO_MODULE_INDEX 437 || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX; 438 } 439 440 @Override 441 public boolean isPanoramaActivity() { 442 return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX); 443 } 444 445 // Accessor methods for getting latency times used in performance testing 446 public long getAutoFocusTime() { 447 return (mCurrentModule instanceof PhotoModule) ? 448 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; 449 } 450 451 public long getShutterLag() { 452 return (mCurrentModule instanceof PhotoModule) ? 453 ((PhotoModule) mCurrentModule).mShutterLag : -1; 454 } 455 456 public long getShutterToPictureDisplayedTime() { 457 return (mCurrentModule instanceof PhotoModule) ? 458 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; 459 } 460 461 public long getPictureDisplayedToJpegCallbackTime() { 462 return (mCurrentModule instanceof PhotoModule) ? 463 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; 464 } 465 466 public long getJpegCallbackFinishTime() { 467 return (mCurrentModule instanceof PhotoModule) ? 468 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; 469 } 470 471 public long getCaptureStartTime() { 472 return (mCurrentModule instanceof PhotoModule) ? 473 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; 474 } 475 476 public boolean isRecording() { 477 return (mCurrentModule instanceof VideoModule) ? 478 ((VideoModule) mCurrentModule).isRecording() : false; 479 } 480 481 public CameraScreenNail getCameraScreenNail() { 482 return (CameraScreenNail) mCameraScreenNail; 483 } 484} 485