/* * Copyright (C) 2012 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.camera; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.IBinder; import android.provider.MediaStore; import android.provider.Settings; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.OrientationEventListener; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import com.android.camera.ui.CameraSwitcher; import com.android.gallery3d.R; import com.android.gallery3d.app.PhotoPage; import com.android.gallery3d.common.ApiHelper; import com.android.gallery3d.util.LightCycleHelper; public class CameraActivity extends ActivityBase implements CameraSwitcher.CameraSwitchListener { public static final int PHOTO_MODULE_INDEX = 0; public static final int VIDEO_MODULE_INDEX = 1; public static final int PANORAMA_MODULE_INDEX = 2; public static final int LIGHTCYCLE_MODULE_INDEX = 3; CameraModule mCurrentModule; private FrameLayout mFrame; private ShutterButton mShutter; private CameraSwitcher mSwitcher; private View mCameraControls; private View mControlsBackground; private View mPieMenuButton; private Drawable[] mDrawables; private int mCurrentModuleIndex; private MotionEvent mDown; private boolean mAutoRotateScreen; private int mHeightOrWidth = -1; private MyOrientationEventListener mOrientationListener; // The degrees of the device rotated clockwise from its natural orientation. private int mLastRawOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; private MediaSaveService mMediaSaveService; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName className, IBinder b) { mMediaSaveService = ((MediaSaveService.LocalBinder) b).getService(); mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); } @Override public void onServiceDisconnected(ComponentName className) { mMediaSaveService = null; }}; private static final String TAG = "CAM_activity"; private static final int[] DRAW_IDS = { R.drawable.ic_switch_camera, R.drawable.ic_switch_video, R.drawable.ic_switch_pan, R.drawable.ic_switch_photosphere }; @Override public void onCreate(Bundle state) { super.onCreate(state); setContentView(R.layout.camera_main); mFrame = (FrameLayout) findViewById(R.id.camera_app_root); mDrawables = new Drawable[DRAW_IDS.length]; for (int i = 0; i < DRAW_IDS.length; i++) { mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]); } init(); if (MediaStore.INTENT_ACTION_VIDEO_CAMERA.equals(getIntent().getAction()) || MediaStore.ACTION_VIDEO_CAPTURE.equals(getIntent().getAction())) { mCurrentModule = new VideoModule(); mCurrentModuleIndex = VIDEO_MODULE_INDEX; } else { mCurrentModule = new PhotoModule(); mCurrentModuleIndex = PHOTO_MODULE_INDEX; } mCurrentModule.init(this, mFrame, true); mSwitcher.setCurrentIndex(mCurrentModuleIndex); mOrientationListener = new MyOrientationEventListener(this); bindMediaSaveService(); } public void init() { boolean landscape = Util.getDisplayRotation(this) % 180 == 90; mControlsBackground = findViewById(R.id.blocker); mCameraControls = findViewById(R.id.camera_controls); mShutter = (ShutterButton) findViewById(R.id.shutter_button); mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher); mPieMenuButton = findViewById(R.id.menu); int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this) ? DRAW_IDS.length : DRAW_IDS.length - 1); if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--; int[] drawids = new int[totaldrawid]; int[] moduleids = new int[totaldrawid]; int ix = 0; for (int i = 0; i < mDrawables.length; i++) { if (i == PANORAMA_MODULE_INDEX && !ApiHelper.HAS_OLD_PANORAMA) { continue; // not enabled, so don't add to UI } if (i == LIGHTCYCLE_MODULE_INDEX && !LightCycleHelper.hasLightCycleCapture(this)) { continue; // not enabled, so don't add to UI } moduleids[ix] = i; drawids[ix++] = DRAW_IDS[i]; } mSwitcher.setIds(moduleids, drawids); mSwitcher.setSwitchListener(this); mSwitcher.setCurrentIndex(mCurrentModuleIndex); } @Override public void onDestroy() { unbindMediaSaveService(); super.onDestroy(); } // Return whether the auto-rotate screen in system settings // is turned on. public boolean isAutoRotateScreen() { return mAutoRotateScreen; } private class MyOrientationEventListener extends OrientationEventListener { public MyOrientationEventListener(Context context) { super(context); } @Override public void onOrientationChanged(int orientation) { // We keep the last known orientation. So if the user first orient // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == ORIENTATION_UNKNOWN) return; mLastRawOrientation = orientation; mCurrentModule.onOrientationChanged(orientation); } } private ObjectAnimator mCameraSwitchAnimator; @Override public void onCameraSelected(final int i) { if (mPaused) return; if (i != mCurrentModuleIndex) { mPaused = true; CameraScreenNail screenNail = getCameraScreenNail(); if (screenNail != null) { if (mCameraSwitchAnimator != null && mCameraSwitchAnimator.isRunning()) { mCameraSwitchAnimator.cancel(); } mCameraSwitchAnimator = ObjectAnimator.ofFloat( screenNail, "alpha", screenNail.getAlpha(), 0f); mCameraSwitchAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { super.onAnimationEnd(animation); doChangeCamera(i); } }); mCameraSwitchAnimator.start(); } else { doChangeCamera(i); } } } private void doChangeCamera(int i) { boolean canReuse = canReuseScreenNail(); CameraHolder.instance().keep(); closeModule(mCurrentModule); mCurrentModuleIndex = i; switch (i) { case VIDEO_MODULE_INDEX: mCurrentModule = new VideoModule(); break; case PHOTO_MODULE_INDEX: mCurrentModule = new PhotoModule(); break; case PANORAMA_MODULE_INDEX: mCurrentModule = new PanoramaModule(); break; case LIGHTCYCLE_MODULE_INDEX: mCurrentModule = LightCycleHelper.createPanoramaModule(); break; } showPieMenuButton(mCurrentModule.needsPieMenu()); openModule(mCurrentModule, canReuse); mCurrentModule.onOrientationChanged(mLastRawOrientation); if (mMediaSaveService != null) { mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); } getCameraScreenNail().setAlpha(0f); getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn); } public void showPieMenuButton(boolean show) { if (show) { findViewById(R.id.blocker).setVisibility(View.VISIBLE); findViewById(R.id.menu).setVisibility(View.VISIBLE); findViewById(R.id.on_screen_indicators).setVisibility(View.VISIBLE); } else { findViewById(R.id.blocker).setVisibility(View.INVISIBLE); findViewById(R.id.menu).setVisibility(View.INVISIBLE); findViewById(R.id.on_screen_indicators).setVisibility(View.INVISIBLE); } } private Runnable mOnFrameDrawn = new Runnable() { @Override public void run() { runOnUiThread(mFadeInCameraScreenNail); } }; private Runnable mFadeInCameraScreenNail = new Runnable() { @Override public void run() { mCameraSwitchAnimator = ObjectAnimator.ofFloat( getCameraScreenNail(), "alpha", 0f, 1f); mCameraSwitchAnimator.setStartDelay(50); mCameraSwitchAnimator.start(); } }; @Override public void onShowSwitcherPopup() { mCurrentModule.onShowSwitcherPopup(); } private void openModule(CameraModule module, boolean canReuse) { module.init(this, mFrame, canReuse && canReuseScreenNail()); mPaused = false; module.onResumeBeforeSuper(); module.onResumeAfterSuper(); } private void closeModule(CameraModule module) { module.onPauseBeforeSuper(); module.onPauseAfterSuper(); mFrame.removeAllViews(); } public ShutterButton getShutterButton() { return mShutter; } public void hideUI() { mCameraControls.setVisibility(View.INVISIBLE); hideSwitcher(); mShutter.setVisibility(View.GONE); } public void showUI() { mCameraControls.setVisibility(View.VISIBLE); showSwitcher(); mShutter.setVisibility(View.VISIBLE); // Force a layout change to show shutter button mShutter.requestLayout(); } public void hideSwitcher() { mSwitcher.closePopup(); mSwitcher.setVisibility(View.INVISIBLE); } public void showSwitcher() { if (mCurrentModule.needsSwitcher()) { mSwitcher.setVisibility(View.VISIBLE); } } public boolean isInCameraApp() { return mShowCameraAppView; } @Override public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); mCurrentModule.onConfigurationChanged(config); } @Override public void onPause() { mPaused = true; mOrientationListener.disable(); mCurrentModule.onPauseBeforeSuper(); super.onPause(); mCurrentModule.onPauseAfterSuper(); } @Override public void onResume() { mPaused = false; if (Settings.System.getInt(getContentResolver(), Settings.System.ACCELEROMETER_ROTATION, 0) == 0) {// auto-rotate off setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED); mAutoRotateScreen = false; } else { setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); mAutoRotateScreen = true; } mOrientationListener.enable(); mCurrentModule.onResumeBeforeSuper(); super.onResume(); mCurrentModule.onResumeAfterSuper(); } private void bindMediaSaveService() { Intent intent = new Intent(this, MediaSaveService.class); bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } private void unbindMediaSaveService() { if (mMediaSaveService != null) { mMediaSaveService.setListener(null); } if (mConnection != null) { unbindService(mConnection); } } @Override protected void onFullScreenChanged(boolean full) { if (full) { showUI(); } else { hideUI(); } super.onFullScreenChanged(full); if (ApiHelper.HAS_ROTATION_ANIMATION) { setRotationAnimation(full); } mCurrentModule.onFullScreenChanged(full); } private void setRotationAnimation(boolean fullscreen) { int rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE; if (fullscreen) { rotationAnimation = WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE; } Window win = getWindow(); WindowManager.LayoutParams winParams = win.getAttributes(); winParams.rotationAnimation = rotationAnimation; win.setAttributes(winParams); } @Override protected void onStop() { super.onStop(); mCurrentModule.onStop(); getStateManager().clearTasks(); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); getStateManager().clearActivityResult(); } @Override protected void installIntentFilter() { super.installIntentFilter(); mCurrentModule.installIntentFilter(); } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { // Only PhotoPage understands ProxyLauncher.RESULT_USER_CANCELED if (resultCode == ProxyLauncher.RESULT_USER_CANCELED && !(getStateManager().getTopState() instanceof PhotoPage)) { resultCode = RESULT_CANCELED; } super.onActivityResult(requestCode, resultCode, data); // Unmap cancel vs. reset if (resultCode == ProxyLauncher.RESULT_USER_CANCELED) { resultCode = RESULT_CANCELED; } mCurrentModule.onActivityResult(requestCode, resultCode, data); } // Preview area is touched. Handle touch focus. // Touch to focus is handled by PreviewGestures, this function call // is no longer needed. TODO: Clean it up in the next refactor @Override protected void onSingleTapUp(View view, int x, int y) { } @Override public void onBackPressed() { if (!mCurrentModule.onBackPressed()) { super.onBackPressed(); } } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mCurrentModule.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event); } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mCurrentModule.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event); } public void cancelActivityTouchHandling() { if (mDown != null) { MotionEvent cancel = MotionEvent.obtain(mDown); cancel.setAction(MotionEvent.ACTION_CANCEL); super.dispatchTouchEvent(cancel); } } @Override public boolean dispatchTouchEvent(MotionEvent m) { if (m.getActionMasked() == MotionEvent.ACTION_DOWN) { mDown = m; } if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) { return mSwitcher.onTouch(null, m); } else if ((mSwitcher != null) && mSwitcher.isInsidePopup(m)) { return superDispatchTouchEvent(m); } else { return mCurrentModule.dispatchTouchEvent(m); } } @Override public void startActivityForResult(Intent intent, int requestCode) { Intent proxyIntent = new Intent(this, ProxyLauncher.class); proxyIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); proxyIntent.putExtra(Intent.EXTRA_INTENT, intent); super.startActivityForResult(proxyIntent, requestCode); } public boolean superDispatchTouchEvent(MotionEvent m) { return super.dispatchTouchEvent(m); } // Preview texture has been copied. Now camera can be released and the // animation can be started. @Override public void onPreviewTextureCopied() { mCurrentModule.onPreviewTextureCopied(); } @Override public void onCaptureTextureCopied() { mCurrentModule.onCaptureTextureCopied(); } @Override public void onUserInteraction() { super.onUserInteraction(); mCurrentModule.onUserInteraction(); } @Override protected boolean updateStorageHintOnResume() { return mCurrentModule.updateStorageHintOnResume(); } @Override public void updateCameraAppView() { super.updateCameraAppView(); mCurrentModule.updateCameraAppView(); } private boolean canReuseScreenNail() { return mCurrentModuleIndex == PHOTO_MODULE_INDEX || mCurrentModuleIndex == VIDEO_MODULE_INDEX || mCurrentModuleIndex == LIGHTCYCLE_MODULE_INDEX; } @Override public boolean isPanoramaActivity() { return (mCurrentModuleIndex == PANORAMA_MODULE_INDEX); } // Accessor methods for getting latency times used in performance testing public long getAutoFocusTime() { return (mCurrentModule instanceof PhotoModule) ? ((PhotoModule) mCurrentModule).mAutoFocusTime : -1; } public long getShutterLag() { return (mCurrentModule instanceof PhotoModule) ? ((PhotoModule) mCurrentModule).mShutterLag : -1; } public long getShutterToPictureDisplayedTime() { return (mCurrentModule instanceof PhotoModule) ? ((PhotoModule) mCurrentModule).mShutterToPictureDisplayedTime : -1; } public long getPictureDisplayedToJpegCallbackTime() { return (mCurrentModule instanceof PhotoModule) ? ((PhotoModule) mCurrentModule).mPictureDisplayedToJpegCallbackTime : -1; } public long getJpegCallbackFinishTime() { return (mCurrentModule instanceof PhotoModule) ? ((PhotoModule) mCurrentModule).mJpegCallbackFinishTime : -1; } public long getCaptureStartTime() { return (mCurrentModule instanceof PhotoModule) ? ((PhotoModule) mCurrentModule).mCaptureStartTime : -1; } public boolean isRecording() { return (mCurrentModule instanceof VideoModule) ? ((VideoModule) mCurrentModule).isRecording() : false; } public CameraScreenNail getCameraScreenNail() { return (CameraScreenNail) mCameraScreenNail; } public MediaSaveService getMediaSaveService() { return mMediaSaveService; } }