Animate between camera modules

 Bug: 7265781

Change-Id: Id226392fc1b14ebe5374d1952c830c960c5588d1
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java
index 731b9f5..9e644e9 100644
--- a/src/com/android/camera/CameraActivity.java
+++ b/src/com/android/camera/CameraActivity.java
@@ -16,6 +16,9 @@
 
 package com.android.camera;
 
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Configuration;
@@ -135,34 +138,79 @@
         }
     }
 
+    private ObjectAnimator mCameraSwitchAnimator;
+
     @Override
-    public void onCameraSelected(int i) {
+    public void onCameraSelected(final int i) {
         if (mPaused) return;
         if (i != mCurrentModuleIndex) {
             mPaused = true;
-            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;
+            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);
             }
-            openModule(mCurrentModule, canReuse);
-            mCurrentModule.onOrientationChanged(mOrientation);
+
         }
     }
 
+    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;
+        }
+        openModule(mCurrentModule, canReuse);
+        mCurrentModule.onOrientationChanged(mOrientation);
+        getCameraScreenNail().setAlpha(0f);
+        getCameraScreenNail().setOnFrameDrawnOneShot(mOnFrameDrawn);
+    }
+
+    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();
diff --git a/src/com/android/camera/CameraScreenNail.java b/src/com/android/camera/CameraScreenNail.java
index 4464ab5..e2b77ff 100644
--- a/src/com/android/camera/CameraScreenNail.java
+++ b/src/com/android/camera/CameraScreenNail.java
@@ -77,6 +77,8 @@
     private float mScaleX = 1f, mScaleY = 1f;
     private boolean mFullScreen;
     private boolean mEnableAspectRatioClamping = false;
+    private float mAlpha = 1f;
+    private Runnable mOnFrameDrawnListener;
 
     public interface Listener {
         void requestRender();
@@ -147,7 +149,7 @@
      * Tells the ScreenNail to override the default aspect ratio scaling
      * and instead perform custom scaling to basically do a centerCrop instead
      * of the default centerInside
-     * 
+     *
      * Note that calls to setSize will disable this
      */
     public void enableAspectRatioClamping() {
@@ -290,6 +292,12 @@
             if (!mVisible) mVisible = true;
             SurfaceTexture surfaceTexture = getSurfaceTexture();
             if (surfaceTexture == null || !mFirstFrameArrived) return;
+            if (mOnFrameDrawnListener != null) {
+                mOnFrameDrawnListener.run();
+                mOnFrameDrawnListener = null;
+            }
+            float oldAlpha = canvas.getAlpha();
+            canvas.setAlpha(mAlpha);
 
             switch (mAnimState) {
                 case ANIM_NONE:
@@ -345,6 +353,7 @@
                     super.draw(canvas, x, y, width, height);
                 }
             }
+            canvas.setAlpha(oldAlpha);
             callbackIfNeeded();
         } // mLock
     }
@@ -413,4 +422,23 @@
             mOneTimeFrameDrawnListener = l;
         }
     }
+
+    public void setOnFrameDrawnOneShot(Runnable run) {
+        synchronized (mLock) {
+            mOnFrameDrawnListener = run;
+        }
+    }
+
+    public float getAlpha() {
+        synchronized (mLock) {
+            return mAlpha;
+        }
+    }
+
+    public void setAlpha(float alpha) {
+        synchronized (mLock) {
+            mAlpha = alpha;
+            mListener.requestRender();
+        }
+    }
 }