2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.camera;
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.ObjectAnimator;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.ServiceConnection;
26 import android.content.pm.ActivityInfo;
27 import android.content.res.Configuration;
28 import android.graphics.drawable.Drawable;
29 import android.os.Bundle;
30 import android.os.IBinder;
31 import android.provider.MediaStore;
32 import android.provider.Settings;
33 import android.view.KeyEvent;
34 import android.view.LayoutInflater;
35 import android.view.MotionEvent;
36 import android.view.OrientationEventListener;
37 import android.view.View;
38 import android.view.ViewGroup;
39 import android.view.Window;
40 import android.view.WindowManager;
41 import android.widget.FrameLayout;
43 import com.android.camera.ui.CameraSwitcher;
44 import com.android.camera.ui.RotatableLayout;
45 import com.android.gallery3d.R;
46 import com.android.gallery3d.app.PhotoPage;
47 import com.android.gallery3d.common.ApiHelper;
48 import com.android.gallery3d.util.LightCycleHelper;
50 public 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;
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;
70 private MyOrientationEventListener mOrientationListener;
71 // The degrees of the device rotated clockwise from its natural orientation.
72 private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
74 private MediaSaveService mMediaSaveService;
75 private ServiceConnection mConnection = new ServiceConnection() {
77 public void onServiceConnected(ComponentName className, IBinder b) {
78 mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService();
79 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
82 public void onServiceDisconnected(ComponentName className) {
83 mMediaSaveService = null;
86 private static final String TAG = "CAM_activity";
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
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]);
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;
110 mCurrentModule = new PhotoModule();
111 mCurrentModuleIndex = PHOTO_MODULE_INDEX;
113 mCurrentModule.init(this, mFrame, true);
114 mSwitcher.setCurrentIndex(mCurrentModuleIndex);
115 mOrientationListener = new MyOrientationEventListener(this);
116 bindMediaSaveService();
120 boolean landscape = Util.getDisplayRotation(this) % 180 == 90;
121 setMargins(landscape);
122 mControlsBackground = findViewById(R.id.blocker);
123 mCameraControls = findViewById(R.id.camera_controls);
124 mShutter = (ShutterButton) findViewById(R.id.shutter_button);
125 mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
126 mPieMenuButton = findViewById(R.id.menu);
127 int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
128 ? DRAW_IDS.length : DRAW_IDS.length - 1);
129 if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
131 int[] drawids = new int[totaldrawid];
132 int[] moduleids = new int[totaldrawid];
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
138 if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) {
139 continue; // not enabled, so don't add to UI
142 drawids[ix++] = DRAW_IDS[i];
144 mSwitcher.setIds(moduleids, drawids);
145 mSwitcher.setSwitchListener(this);
146 mSwitcher.setCurrentIndex(mCurrentModuleIndex);
150 public void onDestroy() {
151 unbindMediaSaveService();
155 // Return whether the auto-rotate screen in system settings
157 public boolean isAutoRotateScreen() {
158 return mAutoRotateScreen;
161 private class MyOrientationEventListener
162 extends OrientationEventListener {
163 public MyOrientationEventListener(Context context) {
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);
178 private ObjectAnimator mCameraSwitchAnimator;
181 public void onCameraSelected(final int i) {
183 if (i != mCurrentModuleIndex) {
185 CameraScreenNail screenNail = getCameraScreenNail();
186 if (screenNail != null) {
187 if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) {
188 mCameraSwitchAnimator.cancel();
190 mCameraSwitchAnimator = ObjectAnimator.ofFloat(
191 screenNail, "alpha", screenNail.getAlpha(), 0f);
192 mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() {
194 public void onAnimationEnd(Animator animation) {
195 super.onAnimationEnd(animation);
199 mCameraSwitchAnimator.start();
206 private void doChangeCamera(int i) {
207 boolean canReuse = canReuseScreenNail();
208 CameraHolder.instance().keep();
209 closeModule(mCurrentModule);
210 mCurrentModuleIndex = i;
212 case VIDEO_MODULE_INDEX:
213 mCurrentModule = new VideoModule();
215 case PHOTO_MODULE_INDEX:
216 mCurrentModule = new PhotoModule();
218 case PANORAMA_MODULE_INDEX:
219 mCurrentModule = new PanoramaModule();
221 case LIGHTCYCLE_MODULE_INDEX:
222 mCurrentModule = LightCycleHelper.createPanoramaModule();
225 showPieMenuButton(mCurrentModule.needsPieMenu());
227 openModule(mCurrentModule, canReuse);
228 mCurrentModule.onOrientationChanged(mLastRawOrientation);
229 if (mMediaSaveService != null) {
230 mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService);
232 getCameraScreenNail().setAlpha(0f);
233 getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn);
236 public void showPieMenuButton(boolean show) {
238 findViewById(R.id.blocker).setVisibility(View.VISIBLE);
239 findViewById(R.id.menu).setVisibility(View.VISIBLE);
240 findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE);
242 findViewById(R.id.blocker).setVisibility(View.INVISIBLE);
243 findViewById(R.id.menu).setVisibility(View.INVISIBLE);
244 findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE);
248 private Runnable mOnFrameDrawn = new Runnable() {
252 runOnUiThread(mFadeInCameraScreenNail);
256 private Runnable mFadeInCameraScreenNail = new Runnable() {
260 mCameraSwitchAnimator = ObjectAnimator.ofFloat(
261 getCameraScreenNail(), "alpha", 0f, 1f);
262 mCameraSwitchAnimator.setStartDelay(50);
263 mCameraSwitchAnimator.start();
268 public void onShowSwitcherPopup() {
269 mCurrentModule.onShowSwitcherPopup();
272 private void openModule(CameraModule module, boolean canReuse) {
273 module.init(this, mFrame, canReuse && canReuseScreenNail());
275 module.onResumeBeforeSuper();
276 module.onResumeAfterSuper();
279 private void closeModule(CameraModule module) {
280 module.onPauseBeforeSuper();
281 module.onPauseAfterSuper();
282 mFrame.removeAllViews();
285 public ShutterButton getShutterButton() {
289 public void hideUI() {
290 mCameraControls.setVisibility(View.INVISIBLE);
292 mShutter.setVisibility(View.GONE);
295 public void showUI() {
296 mCameraControls.setVisibility(View.VISIBLE);
298 mShutter.setVisibility(View.VISIBLE);
299 // Force a layout change to show shutter button
300 mShutter.requestLayout();
303 public void hideSwitcher() {
304 mSwitcher.closePopup();
305 mSwitcher.setVisibility(View.INVISIBLE);
308 public void showSwitcher() {
309 if (mCurrentModule.needsSwitcher()) {
310 mSwitcher.setVisibility(View.VISIBLE);
314 public boolean isInCameraApp() {
315 return mShowCameraAppView;
319 public void onConfigurationChanged(Configuration config) {
320 super.onConfigurationChanged(config);
321 boolean landscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE);
322 setMargins(landscape);
323 mCurrentModule.onConfigurationChanged(config);
326 private void setMargins(boolean landscape) {
327 ViewGroup appRoot = (ViewGroup) findViewById(R.id.content);
328 FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) appRoot.getLayoutParams();
329 int navBarWidth = getResources().getDimensionPixelSize(R.dimen.navigation_bar_width);
330 int navBarHeight = getResources().getDimensionPixelSize(R.dimen.navigation_bar_height);
332 lp.setMargins(navBarHeight, 0, navBarHeight - navBarWidth, 0);
334 lp.setMargins(0, navBarHeight, 0, 0);
336 appRoot.setLayoutParams(lp);
340 public void onPause() {
342 mOrientationListener.disable();
343 mCurrentModule.onPauseBeforeSuper();
345 mCurrentModule.onPauseAfterSuper();
349 public void onResume() {
351 if (Settings.System.getInt(getContentResolver(),
352 Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off
353 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
354 mAutoRotateScreen = false;
356 setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
357 mAutoRotateScreen = true;
359 mOrientationListener.enable();
360 mCurrentModule.onResumeBeforeSuper();
362 mCurrentModule.onResumeAfterSuper();
365 private void bindMediaSaveService() {
366 Intent intent = new Intent(this, MediaSaveService.class);
367 startService(intent); // start service before binding it so the
368 // service won't be killed if we unbind it.
369 bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
372 private void unbindMediaSaveService() {
373 mMediaSaveService.setListener(null);
374 unbindService(mConnection);
378 protected void onFullScreenChanged(boolean full) {
384 super.onFullScreenChanged(full);
385 if (ApiHelper.HAS_ROTATION_ANIMATION) {
386 setRotationAnimation(full);
388 mCurrentModule.onFullScreenChanged(full);
391 private void setRotationAnimation(boolean fullscreen) {
392 int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
394 rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
396 Window win = getWindow();
397 WindowManager.LayoutParams winParams = win.getAttributes();
398 winParams.rotationAnimation = rotationAnimation;
399 win.setAttributes(winParams);
403 protected void onStop() {
405 mCurrentModule.onStop();
406 getStateManager().clearTasks();
410 protected void onNewIntent(Intent intent) {
411 super.onNewIntent(intent);
412 getStateManager().clearActivityResult();
416 protected void installIntentFilter() {
417 super.installIntentFilter();
418 mCurrentModule.installIntentFilter();
422 protected void onActivityResult(
423 int requestCode, int resultCode, Intent data) {
424 // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED
425 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED
426 && !(getStateManager().getTopState() instanceof PhotoPage)) {
427 resultCode = RESULT_CANCELED;
429 super.onActivityResult(requestCode, resultCode, data);
430 // Unmap cancel vs. reset
431 if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) {
432 resultCode = RESULT_CANCELED;
434 mCurrentModule.onActivityResult(requestCode, resultCode, data);
437 // Preview area is touched. Handle touch focus.
439 protected void onSingleTapUp(View view, int x, int y) {
440 mCurrentModule.onSingleTapUp(view, x, y);
444 public void onBackPressed() {
445 if (!mCurrentModule.onBackPressed()) {
446 super.onBackPressed();
451 public boolean onKeyDown(int keyCode, KeyEvent event) {
452 return mCurrentModule.onKeyDown(keyCode, event)
453 || super.onKeyDown(keyCode, event);
457 public boolean onKeyUp(int keyCode, KeyEvent event) {
458 return mCurrentModule.onKeyUp(keyCode, event)
459 || super.onKeyUp(keyCode, event);
462 public void cancelActivityTouchHandling() {
464 MotionEvent cancel = MotionEvent.obtain(mDown);
465 cancel.setAction(MotionEvent.ACTION_CANCEL);
466 super.dispatchTouchEvent(cancel);
471 public boolean dispatchTouchEvent(MotionEvent m) {
472 if (m.getActionMasked() == MotionEvent.ACTION_DOWN) {
475 if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
476 return mSwitcher.onTouch(null, m);
477 } else if ((mSwitcher != null) && mSwitcher.isInsidePopup(m)) {
478 return superDispatchTouchEvent(m);
480 return mCurrentModule.dispatchTouchEvent(m);
485 public void startActivityForResult(Intent intent, int requestCode) {
486 Intent proxyIntent = new Intent(this, ProxyLauncher.class);
487 proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
488 proxyIntent.putExtra(Intent.EXTRA_INTENT, intent);
489 super.startActivityForResult(proxyIntent, requestCode);
492 public boolean superDispatchTouchEvent(MotionEvent m) {
493 return super.dispatchTouchEvent(m);
496 // Preview texture has been copied. Now camera can be released and the
497 // animation can be started.
499 public void onPreviewTextureCopied() {
500 mCurrentModule.onPreviewTextureCopied();
504 public void onCaptureTextureCopied() {
505 mCurrentModule.onCaptureTextureCopied();
509 public void onUserInteraction() {
510 super.onUserInteraction();
511 mCurrentModule.onUserInteraction();
515 protected boolean updateStorageHintOnResume() {
516 return mCurrentModule.updateStorageHintOnResume();
520 public void updateCameraAppView() {
521 super.updateCameraAppView();
522 mCurrentModule.updateCameraAppView();
525 private boolean canReuseScreenNail() {
526 return mCurrentModuleIndex == PHOTO_MODULE_INDEX
527 || mCurrentModuleIndex == VIDEO_MODULE_INDEX
528 || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX;
532 public boolean isPanoramaActivity() {
533 return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX);
536 // Accessor methods for getting latency times used in performance testing
537 public long getAutoFocusTime() {
538 return (mCurrentModule instanceof PhotoModule) ?
539 ((PhotoModule) mCurrentModule).mAutoFocusTime : -1;
542 public long getShutterLag() {
543 return (mCurrentModule instanceof PhotoModule) ?
544 ((PhotoModule) mCurrentModule).mShutterLag : -1;
547 public long getShutterToPictureDisplayedTime() {
548 return (mCurrentModule instanceof PhotoModule) ?
549 ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1;
552 public long getPictureDisplayedToJpegCallbackTime() {
553 return (mCurrentModule instanceof PhotoModule) ?
554 ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1;
557 public long getJpegCallbackFinishTime() {
558 return (mCurrentModule instanceof PhotoModule) ?
559 ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1;
562 public long getCaptureStartTime() {
563 return (mCurrentModule instanceof PhotoModule) ?
564 ((PhotoModule) mCurrentModule).mCaptureStartTime : -1;
567 public boolean isRecording() {
568 return (mCurrentModule instanceof VideoModule) ?
569 ((VideoModule) mCurrentModule).isRecording() : false;
572 public CameraScreenNail getCameraScreenNail() {
573 return (CameraScreenNail) mCameraScreenNail;
576 public MediaSaveService getMediaSaveService() {
577 return mMediaSaveService;