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