From fffb62e7b5bb609b7c9952f7f7fb6023a373b209 Mon Sep 17 00:00:00 2001 From: Steve Kondik Date: Mon, 14 Nov 2016 13:42:04 -0800 Subject: snap: Improve transitions * Synchronize animation to filmstrip with input * More code cleanups, introduce BaseModule * Animations are hard Change-Id: Ie664bb2059a28b3a9f3957fb491ec28fede521e5 --- res/layout/camera_controls.xml | 15 +- src/com/android/camera/BaseModule.java | 47 +++ src/com/android/camera/BaseUI.java | 124 +++++- src/com/android/camera/CameraActivity.java | 54 +-- src/com/android/camera/CameraModule.java | 4 +- src/com/android/camera/CaptureModule.java | 24 +- src/com/android/camera/CaptureUI.java | 7 +- src/com/android/camera/PhotoController.java | 3 +- src/com/android/camera/PhotoModule.java | 28 +- src/com/android/camera/PhotoUI.java | 10 +- src/com/android/camera/VideoModule.java | 19 +- src/com/android/camera/VideoUI.java | 7 +- .../android/camera/WideAnglePanoramaModule.java | 27 +- src/com/android/camera/ui/CameraControls.java | 453 ++++++++++++++------- src/com/android/camera/ui/FilmStripView.java | 6 +- 15 files changed, 502 insertions(+), 326 deletions(-) create mode 100644 src/com/android/camera/BaseModule.java mode change 100755 => 100644 src/com/android/camera/PhotoModule.java diff --git a/res/layout/camera_controls.xml b/res/layout/camera_controls.xml index 1f125145f..2269a60d4 100644 --- a/res/layout/camera_controls.xml +++ b/res/layout/camera_controls.xml @@ -23,7 +23,7 @@ android:background="@drawable/camera_controls_bg_translucent" android:layout_width="match_parent" android:layout_height="@dimen/preview_top_margin" - android:gravity="center_vertical" + android:gravity="top" android:orientation="horizontal"> @@ -83,7 +83,6 @@ android:layout_gravity="center" android:background="@android:color/transparent" android:contentDescription="@string/switch_photo_filmstrip" - android:elevation="1dp" android:scaleType="fitCenter" /> @@ -159,9 +155,10 @@ implements CameraModule { + + // subclass initializes in init() for now + protected T mUI; + + @Override + public void showPreviewCover() { + mUI.showPreviewCover(); + } + + @Override + public void hidePreviewCover() { + mUI.hidePreviewCover(); + } + + @Override + public void animateControls(float alpha) { + mUI.animateControls(alpha); + } + + @Override + public boolean arePreviewControlsVisible() { + return mUI.arePreviewControlsVisible(); + } + + @Override + public void onPreviewFocusChanged(boolean previewFocused) { + mUI.onPreviewFocusChanged(previewFocused); + } +} diff --git a/src/com/android/camera/BaseUI.java b/src/com/android/camera/BaseUI.java index 384c166e8..83ea01550 100644 --- a/src/com/android/camera/BaseUI.java +++ b/src/com/android/camera/BaseUI.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2016 The CyanogenMod 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.graphics.Bitmap; @@ -7,7 +22,6 @@ import android.util.Log; import android.util.Pair; import android.view.View; import android.view.ViewGroup; -import android.view.ViewStub; import com.android.camera.ui.CameraControls; import com.android.camera.ui.CaptureAnimationOverlay; @@ -20,10 +34,14 @@ import org.codeaurora.snapcam.R; import java.util.ArrayList; import java.util.List; -/** we can start accumulating common code between UI classes here - * toward an eventual unification - WF */ +/** + * we can start accumulating common code between UI classes here + * toward an eventual unification - WF + */ public abstract class BaseUI { + private static final String TAG = "CAM_" + BaseUI.class.getSimpleName(); + protected final CaptureAnimationOverlay mCaptureOverlay; protected final View mPreviewCover; @@ -39,6 +57,8 @@ public abstract class BaseUI { protected int mOrientation = 0; private boolean mOverlaysDisabled; + private float mPreviewCoverAlpha; + private final List mDisabledViews = new ArrayList<>(); public BaseUI(CameraActivity activity, ViewGroup rootView, int layout) { @@ -61,36 +81,56 @@ public abstract class BaseUI { mBottomMargin = margins.second; } - private void calculateMargins(Point size) { - int l = size.x > size.y ? size.x : size.y; - int tm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); - int bm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); - mTopMargin = l / 4 * tm / (tm + bm); - mBottomMargin = l / 4 - mTopMargin; + public void onPreviewFocusChanged(boolean previewFocused) { + if (previewFocused) { + showUI(); + } else { + hideUI(true); + } } public void showPreviewCover() { - if (mPreviewCover != null && mPreviewCover.getVisibility() != View.VISIBLE) { - mPreviewCover.setVisibility(View.VISIBLE); - disableOverlays(); + if (mPreviewCover != null) { + synchronized (mPreviewCover) { + if (mPreviewCover.getVisibility() != View.VISIBLE) { + mPreviewCover.setAlpha(0.0f); + mPreviewCover.setVisibility(View.VISIBLE); + disableOverlays(); + } + } } } public void hidePreviewCover() { - if (mPreviewCover != null && mPreviewCover.getVisibility() != View.GONE) { - mPreviewCover.setVisibility(View.GONE); - enableOverlays(); + if (mPreviewCover != null) { + synchronized (mPreviewCover) { + if (mPreviewCover.getVisibility() != View.GONE) { + mPreviewCover.setAlpha(1.0f); + mPreviewCover.setVisibility(View.GONE); + enableOverlays(); + } + } } } - public void setPreviewCoverAlpha(float alpha) { + public void animateControls(float offset) { if (mPreviewCover != null) { - mPreviewCover.setAlpha(alpha); + synchronized (mPreviewCover) { + setPreviewCoverAlpha(offset, false); + if (mCameraControls != null) { + mCameraControls.setUIOffset(offset, true); + } + } } } public boolean isPreviewCoverVisible() { - return mPreviewCover != null && mPreviewCover.getVisibility() == View.VISIBLE; + if (mPreviewCover == null) { + return false; + } + synchronized (mPreviewCover) { + return mPreviewCover.getVisibility() == View.VISIBLE; + } } public void hideUI() { @@ -98,14 +138,28 @@ public abstract class BaseUI { } protected void hideUI(boolean toBlack) { - if (mCameraControls != null) { - mCameraControls.hideUI(toBlack); + if (mPreviewCover != null) { + synchronized (mPreviewCover) { + if (toBlack) { + setPreviewCoverAlpha(1.0f, true); + } else { + setPreviewCoverAlpha(0.4f, true); + } + if (mCameraControls != null) { + mCameraControls.hideUI(toBlack); + } + } } } protected void showUI() { - if (mCameraControls != null) { - mCameraControls.showUI(); + if (mPreviewCover != null) { + synchronized (mPreviewCover) { + setPreviewCoverAlpha(0.0f, true); + if (mCameraControls != null) { + mCameraControls.showUI(); + } + } } } @@ -240,4 +294,30 @@ public abstract class BaseUI { mRecordingTime.showTimeLapse(enable); } } + + private void setPreviewCoverAlpha(float alpha, boolean animate) { + if (mPreviewCover == null) { + return; + } + synchronized (mPreviewCover) { + if (alpha == mPreviewCoverAlpha || alpha < 0.0f || alpha > 1.0f) { + return; + } + + if (alpha == 0.0f) { + hidePreviewCover(); + } else { + showPreviewCover(); + } + + if (animate) { + mPreviewCover.animate().cancel(); + mPreviewCover.animate().alpha(alpha).start(); + } else { + mPreviewCover.setAlpha(alpha); + } + + mPreviewCoverAlpha = alpha; + } + } } diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 570fad1c4..b0aad3640 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -216,7 +216,6 @@ public class CameraActivity extends Activity private final Object mStorageSpaceLock = new Object(); private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES; private boolean mSecureCamera; - private boolean mInCameraApp = true; // Keep track of powershutter state public static boolean mPowerShutter = false; // Keep track of max brightness state @@ -267,6 +266,8 @@ public class CameraActivity extends Activity private static final int REFOCUS_ACTIVITY_CODE = 1; private int mDisplayWidth; + private boolean mShowingFilmstrip = false; + private class MyOrientationEventListener extends OrientationEventListener { public MyOrientationEventListener(Context context) { @@ -473,8 +474,7 @@ public class CameraActivity extends Activity return; } - if(!arePreviewControlsVisible()) { - setPreviewControlsVisibility(true); + if (!arePreviewControlsVisible()) { CameraActivity.this.setSystemBarsVisibility(false); } } @@ -485,10 +485,6 @@ public class CameraActivity extends Activity // For now, We ignore all items that are not the camera preview. return; } - - if (arePreviewControlsVisible()) { - setPreviewControlsVisibility(false); - } } @Override @@ -525,7 +521,7 @@ public class CameraActivity extends Activity LocalData.LOCAL_CAMERA_PREVIEW; if (!focused) { if (isCameraID) { - mCurrentModule.onPreviewFocusChanged(false); + //mCurrentModule.onPreviewFocusChanged(false); CameraActivity.this.setSystemBarsVisibility(true); } hidePanoStitchingProgress(); @@ -580,25 +576,18 @@ public class CameraActivity extends Activity CameraActivity.this.setSystemBarsVisibility(visible); } - private float previewCoverAlpha = 1.0f; - - private void setPreviewCoverAlpha(float alpha) { - if (alpha == previewCoverAlpha || alpha < 0.0f || alpha > 1.0f) { - return; - } - mCurrentModule.setPreviewCoverAlpha(alpha); - if (alpha == 0.0f) { - mCurrentModule.hidePreviewCover(); - } else if (previewCoverAlpha == 0.0f) { - mCurrentModule.showPreviewCover(); - } - previewCoverAlpha = alpha; - } - @Override public void onFilmStripScroll(int offset) { float rangePx = mDisplayWidth / 2f; - setPreviewCoverAlpha((float)Math.min(1.0, offset/rangePx)); + if (offset >= rangePx && !mShowingFilmstrip) { + mShowingFilmstrip = true; + setPreviewControlsVisibility(false); + } else if (offset == 0 && mShowingFilmstrip) { + mShowingFilmstrip = false; + setPreviewControlsVisibility(true); + } else { + mCurrentModule.animateControls((float) Math.min(1.0, offset / rangePx)); + } } }; @@ -1771,6 +1760,7 @@ public class CameraActivity extends Activity super.onResume(); mPaused = false; mCurrentModule.onResumeAfterSuper(); + mCurrentModule.animateControls(0); setSwipingEnabled(true); @@ -1963,7 +1953,7 @@ public class CameraActivity extends Activity if (!CameraUtil.hasCameraKey()) { mPowerShutter = val.equals(CameraSettings.VALUE_ON); } - if (mPowerShutter && mInCameraApp) { + if (mPowerShutter && arePreviewControlsVisible()) { getWindow().addPrivateFlags( WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY); } else { @@ -1981,7 +1971,7 @@ public class CameraActivity extends Activity mMaxBrightness = val.equals(CameraSettings.VALUE_ON); - if (mMaxBrightness && mInCameraApp) { + if (mMaxBrightness && arePreviewControlsVisible()) { params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL; } else { params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; @@ -2049,7 +2039,7 @@ public class CameraActivity extends Activity } public boolean isInCameraApp() { - return mInCameraApp; + return arePreviewControlsVisible(); } @Override @@ -2382,14 +2372,10 @@ public class CameraActivity extends Activity * Show or hide the {@link CameraControls} using the current module's * implementation of {@link #onPreviewFocusChanged}. * - * @param showControls whether to show camera controls. + * @param visible whether to show camera controls. */ - private void setPreviewControlsVisibility(boolean showControls) { - mCurrentModule.onPreviewFocusChanged(showControls); - - // controls are only shown when the camera app is active - // so we can assume to fetch this information from here - mInCameraApp = showControls; + private void setPreviewControlsVisibility(boolean visible) { + mCurrentModule.onPreviewFocusChanged(visible); } // Accessor methods for getting latency times used in performance testing diff --git a/src/com/android/camera/CameraModule.java b/src/com/android/camera/CameraModule.java index 7b591e1f9..a3a88fe41 100644 --- a/src/com/android/camera/CameraModule.java +++ b/src/com/android/camera/CameraModule.java @@ -21,7 +21,7 @@ import android.content.res.Configuration; import android.view.KeyEvent; import android.view.View; -public interface CameraModule { +public interface CameraModule { public void init(CameraActivity activity, View frame); @@ -79,5 +79,5 @@ public interface CameraModule { public void hidePreviewCover(); - public void setPreviewCoverAlpha(float alpha); + public void animateControls(float offset); } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 6ebbe2132..b573530a6 100755 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -104,11 +104,12 @@ import java.util.List; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; -public class CaptureModule implements CameraModule, PhotoController, +public class CaptureModule extends BaseModule implements PhotoController, MediaSaveService.Listener, ClearSightImageProcessor.Callback, SettingsManager.Listener, LocationManager.Listener, CountDownView.OnCountDownFinishedListener, PauseButton.OnPauseButtonListener { + public static final int DUAL_MODE = 0; public static final int BAYER_MODE = 1; public static final int MONO_MODE = 2; @@ -200,7 +201,6 @@ public class CaptureModule implements CameraModule, PhotoController, private boolean[] mCameraOpened = new boolean[MAX_NUM_CAM]; private CameraDevice[] mCameraDevice = new CameraDevice[MAX_NUM_CAM]; private String[] mCameraId = new String[MAX_NUM_CAM]; - private CaptureUI mUI; private CameraActivity mActivity; private List mCameraIdList; private float mZoomValue = 1f; @@ -1692,11 +1692,6 @@ public class CaptureModule implements CameraModule, PhotoController, } } - @Override - public void onPreviewFocusChanged(boolean previewFocused) { - mUI.onPreviewFocusChanged(previewFocused); - } - @Override public void onPauseBeforeSuper() { mPaused = true; @@ -3516,19 +3511,4 @@ public class CaptureModule implements CameraModule, PhotoController, buffer.get(bytes); return bytes; } - - @Override - public void showPreviewCover() { - mUI.showPreviewCover(); - } - - @Override - public void hidePreviewCover() { - mUI.hidePreviewCover(); - } - - @Override - public void setPreviewCoverAlpha(float alpha) { - mUI.setPreviewCoverAlpha(alpha); - } } diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index 93455a6b0..44d06cdbc 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -1428,11 +1428,8 @@ public class CaptureUI extends BaseUI implements PreviewGestures.SingleTapListen } public void onPreviewFocusChanged(boolean previewFocused) { - if (previewFocused) { - showUI(); - } else { - hideUI(true); - } + super.onPreviewFocusChanged(previewFocused); + if (mFaceView != null) { mFaceView.setBlockDraw(!previewFocused); } diff --git a/src/com/android/camera/PhotoController.java b/src/com/android/camera/PhotoController.java index 887f0d375..415eb6e7f 100644 --- a/src/com/android/camera/PhotoController.java +++ b/src/com/android/camera/PhotoController.java @@ -22,7 +22,8 @@ import android.view.View; import com.android.camera.ShutterButton.OnShutterButtonListener; -public interface PhotoController extends OnShutterButtonListener { +public interface PhotoController extends CameraModule, ShutterButton.OnShutterButtonListener { + public static final int INIT = -1; public static final int PREVIEW_STOPPED = 0; public static final int IDLE = 1; // preview is active diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java old mode 100755 new mode 100644 index 69976fdc3..14e358b88 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -110,8 +110,7 @@ import android.graphics.Color; import android.graphics.Paint; import android.os.SystemProperties; -public class PhotoModule - implements CameraModule, +public class PhotoModule extends BaseModule implements PhotoController, FocusOverlayManager.Listener, CameraPreference.OnPreferenceChangedListener, @@ -171,8 +170,6 @@ public class PhotoModule private boolean mPaused; private View mRootView; - private PhotoUI mUI; - // The activity is going to switch to the specified camera id. This is // needed because texture copy is done in GL thread. -1 means camera is not // switching. @@ -4970,23 +4967,11 @@ public class PhotoModule mHeading += 360; } } - @Override - public void onPreviewFocusChanged(boolean previewFocused) { - mUI.onPreviewFocusChanged(previewFocused); - } + // TODO: Delete this function after old camera code is removed @Override public void onRestorePreferencesClicked() {} -/* - * Provide a mapping for Jpeg encoding quality levels - * from String representation to numeric representation. - */ - @Override - public boolean arePreviewControlsVisible() { - return mUI.arePreviewControlsVisible(); - } - // For debugging only. public void setDebugUri(Uri uri) { mDebugUri = uri; @@ -5025,24 +5010,19 @@ public class PhotoModule @Override public void showPreviewCover() { + super.showPreviewCover(); disableAutoFocusMoveCallback(); stopFaceDetection(); mUI.getFocusRing().stopFocusAnimations(); - mUI.showPreviewCover(); } @Override public void hidePreviewCover() { - mUI.hidePreviewCover(); + super.showPreviewCover(); startFaceDetection(); updateAutoFocusMoveCallback(); } - @Override - public void setPreviewCoverAlpha(float alpha) { - mUI.setPreviewCoverAlpha(alpha); - } - public void onMakeupLevelSync(String key, String value) { Log.d(TAG, "PhotoModule.onMakeupLevel(): key is " + key + ", value is " + value); diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index f71eb31cc..96dda6f8a 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -26,7 +26,6 @@ import android.graphics.Point; import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.hardware.Camera; -import android.hardware.Camera.Face; import android.os.AsyncTask; import android.preference.PreferenceManager; import android.util.Log; @@ -703,11 +702,8 @@ public class PhotoUI extends BaseUI implements PieListener, } public void onPreviewFocusChanged(boolean previewFocused) { - if (previewFocused) { - showUI(); - } else { - hideUI(true); - } + super.onPreviewFocusChanged(previewFocused); + if (mFaceView != null) { mFaceView.setBlockDraw(!previewFocused); } @@ -1165,7 +1161,7 @@ public class PhotoUI extends BaseUI implements PieListener, } @Override - public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) { + public void onFaceDetection(Camera.Face[] faces, CameraManager.CameraProxy camera) { mFaceView.setFaces(faces); } diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 049ce0a46..b528a7625 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -84,7 +84,7 @@ import java.util.List; import java.util.HashMap; import java.lang.reflect.Method; -public class VideoModule implements CameraModule, +public class VideoModule extends BaseModule implements VideoController, FocusOverlayManager.Listener, CameraPreference.OnPreferenceChangedListener, @@ -208,7 +208,6 @@ public class VideoModule implements CameraModule, private LocationManager mLocationManager; private int mPendingSwitchCameraId; private final Handler mHandler = new MainHandler(); - private VideoUI mUI; private CameraProxy mCameraDevice; private static final String KEY_PREVIEW_FORMAT = "preview-format"; private static final String FORMAT_NV12_VENUS = "nv12-venus"; @@ -3154,16 +3153,11 @@ public class VideoModule implements CameraModule, @Override public void onPreviewFocusChanged(boolean previewFocused) { - mUI.onPreviewFocusChanged(previewFocused); + super.onPreviewFocusChanged(previewFocused); mHandler.sendEmptyMessageDelayed(HANDLE_FLASH_TORCH_DELAY, 800); mPreviewFocused = previewFocused; } - @Override - public boolean arePreviewControlsVisible() { - return mUI.arePreviewControlsVisible(); - } - private final class JpegPictureCallback implements CameraPictureCallback { Location mLocation; @@ -3344,20 +3338,15 @@ public class VideoModule implements CameraModule, @Override public void showPreviewCover() { + super.showPreviewCover(); stopFaceDetection(); mUI.getFocusRing().stopFocusAnimations(); - mUI.showPreviewCover(); } @Override public void hidePreviewCover() { - mUI.hidePreviewCover(); + super.hidePreviewCover(); startFaceDetection(); } - - @Override - public void setPreviewCoverAlpha(float alpha) { - mUI.setPreviewCoverAlpha(alpha); - } } diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 467990882..f2b725e85 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -885,11 +885,8 @@ public class VideoUI extends BaseUI implements PieRenderer.PieListener, } public void onPreviewFocusChanged(boolean previewFocused) { - if (previewFocused) { - showUI(); - } else { - hideUI(true); - } + super.onPreviewFocusChanged(previewFocused); + if (mGestures != null) { mGestures.setEnabled(previewFocused); } diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java index 9b712c6bf..9687be8af 100644 --- a/src/com/android/camera/WideAnglePanoramaModule.java +++ b/src/com/android/camera/WideAnglePanoramaModule.java @@ -65,8 +65,8 @@ import java.util.TimeZone; /** * Activity to handle panorama capturing. */ -public class WideAnglePanoramaModule - implements CameraModule, WideAnglePanoramaController, +public class WideAnglePanoramaModule extends BaseModule implements + WideAnglePanoramaController, SurfaceTexture.OnFrameAvailableListener { public static final int DEFAULT_SWEEP_ANGLE = 160; @@ -93,7 +93,6 @@ public class WideAnglePanoramaModule private static final boolean DEBUG = false; private ContentResolver mContentResolver; - private WideAnglePanoramaUI mUI; private MosaicPreviewRenderer mMosaicPreviewRenderer; private Object mRendererLock = new Object(); @@ -358,13 +357,8 @@ public class WideAnglePanoramaModule @Override public void onPreviewFocusChanged(boolean previewFocused) { + super.onPreviewFocusChanged(previewFocused); mPreviewFocused = previewFocused; - mUI.onPreviewFocusChanged(previewFocused); - } - - @Override - public boolean arePreviewControlsVisible() { - return mUI.arePreviewControlsVisible(); } /** @@ -1304,19 +1298,4 @@ public class WideAnglePanoramaModule public void onMediaSaveServiceConnected(MediaSaveService s) { // do nothing. } - - @Override - public void showPreviewCover() { - mUI.showPreviewCover(); - } - - @Override - public void hidePreviewCover() { - mUI.hidePreviewCover(); - } - - @Override - public void setPreviewCoverAlpha(float alpha) { - mUI.setPreviewCoverAlpha(alpha); - } } diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java index 740a07ea1..9102db3c3 100644 --- a/src/com/android/camera/ui/CameraControls.java +++ b/src/com/android/camera/ui/CameraControls.java @@ -21,6 +21,7 @@ import android.animation.Animator.AnimatorListener; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.app.Activity; import android.content.Context; import android.graphics.Canvas; @@ -31,6 +32,7 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.RippleDrawable; import android.util.AttributeSet; import android.util.Log; +import android.util.MathUtils; import android.util.Pair; import android.view.View; import android.view.ViewGroup; @@ -49,15 +51,14 @@ import com.android.camera.util.CameraUtil; import org.codeaurora.snapcam.R; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.Objects; public class CameraControls extends RotatableLayout { private static final String TAG = "CAM_Controls"; - private LinearLayout mTopBar; - private LinearLayout mBottomBar; - private ShutterButton mShutter; private View mVideoShutter; private ModuleSwitcher mSwitcher; @@ -69,6 +70,9 @@ public class CameraControls extends RotatableLayout { private View mFrontBackSwitcher; private View mMenu; + private ViewGroup mTopBar; + private ViewGroup mBottomBar; + private View mReviewDoneButton; private View mReviewCancelButton; private View mReviewRetakeButton; @@ -77,11 +81,6 @@ public class CameraControls extends RotatableLayout { private static final int WIDTH_GRID = 5; private static final int HEIGHT_GRID = 7; - private boolean mHidden = false; - private AnimatorSet mAnimator = null; - private boolean mFullyHidden = false; - - private static final int ANIME_DURATION = 300; private boolean mHideRemainingPhoto = false; private LinearLayout mRemainingPhotos; @@ -98,45 +97,7 @@ public class CameraControls extends RotatableLayout { private int mModuleIndex = -1; - AnimatorListener outlistener = new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - enableTouch(false); - } - @Override - public void onAnimationEnd(Animator animation) { - setChildrenVisibility(mBottomBar, false); - if (mFullyHidden) { - setChildrenVisibility(mTopBar, false); - setVisibility(View.INVISIBLE); - } - } - }; - - AnimatorListener inlistener = new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - setChildrenVisibility(mBottomBar, true); - if (mFullyHidden) { - setChildrenVisibility(mTopBar, true); - setVisibility(View.VISIBLE); - mFullyHidden = false; - } - } - @Override - public void onAnimationEnd(Animator animation) { - enableTouch(true); - } - }; - - private void setChildrenVisibility(ViewGroup parent, boolean visible) { - for (int i = 0; i < parent.getChildCount(); i++) { - View v = parent.getChildAt(i); - if (v.getVisibility() != View.GONE) { - v.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); - } - } - } + private final AnimationHelper mAnimationHelper; public CameraControls(Context context, AttributeSet attrs) { super(context, attrs); @@ -146,9 +107,11 @@ public class CameraControls extends RotatableLayout { setMeasureAllChildren(true); - Pair margins = CameraUtil.calculateMargins((Activity)context); + Pair margins = CameraUtil.calculateMargins((Activity) context); mTopMargin = margins.first; mBottomMargin = margins.second; + + mAnimationHelper = new AnimationHelper(); } public CameraControls(Context context) { @@ -156,12 +119,21 @@ public class CameraControls extends RotatableLayout { } public boolean isAnimating() { - return mAnimator != null && mAnimator.isRunning(); + return mAnimationHelper.isAnimating(); } - public void enableTouch(boolean enable) { - Log.d(TAG, "ENABLE TOUCH " + enable + " mViews.size=" + mViews.size()); + public void reset() { mAnimationHelper.reset(); } + private void setChildrenVisibility(ViewGroup parent, boolean visible) { + for (int i = 0; i < parent.getChildCount(); i++) { + View v = parent.getChildAt(i); + if (v.getVisibility() != View.GONE) { + v.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); + } + } + } + + public void enableTouch(boolean enable) { synchronized (mViews) { for (View v : mViews) { if (v.getVisibility() != View.GONE) { @@ -227,7 +199,7 @@ public class CameraControls extends RotatableLayout { mShutter.addOnShutterButtonListener(mShutterListener); - mSwitcher.setSwitchListener((CameraActivity)getContext()); + mSwitcher.setSwitchListener((CameraActivity) getContext()); mSwitcher.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { @@ -357,19 +329,6 @@ public class CameraControls extends RotatableLayout { }); } - @Override - public void onAttachedToWindow() { - super.onAttachedToWindow(); - mTopBar.setTranslationX(0); - mTopBar.setTranslationY(0); - mBottomBar.setTranslationX(0); - mBottomBar.setTranslationY(0); - mHidden = false; - mFullyHidden = false; - setChildrenVisibility(mTopBar, true); - setChildrenVisibility(mBottomBar, true); - } - private void setLocation(int w, int h) { int rotation = getUnifiedRotation(); layoutToast(mRefocusToast, w, h, rotation); @@ -422,103 +381,21 @@ public class CameraControls extends RotatableLayout { mAutoHdrNotice.setVisibility(enabled ? View.VISIBLE : View.GONE); } - private ObjectAnimator getViewAnimation(View v, float distance, boolean vertical) { - final ObjectAnimator anim = ObjectAnimator.ofFloat(v, - (vertical ? "translationY" : "translationX"), distance); - anim.setDuration(ANIME_DURATION); - return anim; - } - - private void animateViews(boolean enabled, boolean full, AnimatorListener listener) { - int rotation = getUnifiedRotation(); - int topSize = enabled ? 0 : -mTopMargin; - int bottomSize = enabled ? 0 : mBottomMargin; - boolean vertical = true; - switch (rotation) { - case 0: - break; - case 90: - vertical = false; - break; - case 180: - topSize = -topSize; - bottomSize = -bottomSize; - break; - case 270: - topSize = -topSize; - bottomSize = -bottomSize; - vertical = false; - break; - } - - mAnimator = new AnimatorSet(); - mAnimator.addListener(listener); - mAnimator.setDuration(ANIME_DURATION); - mAnimator.setInterpolator(enabled ? - new DecelerateInterpolator() : - new AccelerateInterpolator()); - - if (full) { - mAnimator.playTogether(getViewAnimation(mTopBar, topSize, vertical), - getViewAnimation(mBottomBar, bottomSize, vertical)); - } else { - final ArrayList anims = new ArrayList<>(); - for (int i = 0; i < mBottomBar.getChildCount(); i++) { - View v = mBottomBar.getChildAt(i); - if (v.getVisibility() != View.GONE) { - anims.add(getViewAnimation(v, bottomSize, vertical)); - } - } - if (anims.size() > 0) { - mAnimator.playTogether(anims.toArray(new ObjectAnimator[anims.size()])); - } - } - mAnimator.start(); - } - public void setMenuAndSwitcherEnabled(boolean enable) { mMenu.setEnabled(enable); mFrontBackSwitcher.setEnabled(enable); } - public void hideUI(boolean toBlack) { - if (mHidden) { - return; - } - - mHidden = true; - mFullyHidden = toBlack; - - if (isAnimating()) { - mAnimator.cancel(); - } - - collapse(); - - animateViews(false, toBlack, outlistener); + public void setUIOffset(float offset, boolean toBlack) { + mAnimationHelper.slideTo(offset, toBlack); + } - mRemainingPhotos.setVisibility(View.INVISIBLE); - mRefocusToast.setVisibility(View.GONE); + public void hideUI(boolean toBlack) { + mAnimationHelper.slideOut(toBlack); } public void showUI() { - if (!mHidden) { - return; - } - - mHidden = false; - - if (isAnimating()) { - mAnimator.cancel(); - } - - animateViews(true, mFullyHidden, inlistener); - - if ((mRemainingPhotos.getVisibility() == View.INVISIBLE) && - !mHideRemainingPhoto) { - mRemainingPhotos.setVisibility(View.VISIBLE); - } - mRefocusToast.setVisibility(View.GONE); + mAnimationHelper.slideIn(); } private void layoutRemaingPhotos() { @@ -563,7 +440,7 @@ public class CameraControls extends RotatableLayout { } public boolean arePreviewControlsVisible() { - return !mHidden; + return mAnimationHelper.areControlsVisible(); } public void setPreviewRect(RectF rectL) { @@ -574,6 +451,7 @@ public class CameraControls extends RotatableLayout { } else { mBottomBar.setBackgroundResource(R.drawable.camera_controls_bg_translucent); } + mAnimationHelper.reset(); } public void showRefocusToast(boolean show) { @@ -631,7 +509,272 @@ public class CameraControls extends RotatableLayout { mRemainingPhotosText.setVisibility(View.GONE); } - private class ArrowTextView extends TextView { + + private class AnimationHelper { + + private float mCurrentOffset = 0.0f; + private float mTargetOffset = 0.0f; + + private boolean mEntering = false; + private boolean mFullyHidden = false; + + private AnimatorSet mAnimator = null; + + private static final boolean DEBUG = false; + + public void slideOut(boolean hideFully) { + mTargetOffset = 1.0f; + mEntering = false; + mFullyHidden = hideFully; + dump("slideOut"); + animate(); + } + + public void slideIn() { + mTargetOffset = 0.0f; + mEntering = true; + dump("slideIn"); + animate(); + } + + public void slideTo(float offset, boolean hideFully) { + if (offset > 1.0f || offset < 0.0f || offset == mCurrentOffset) { + return; + } + + if (mAnimator != null && mAnimator.isRunning()) { + return; + } + + mTargetOffset = offset; + mEntering = mTargetOffset < mCurrentOffset; + + if (mEntering) { + preEnter(); + } else { + mFullyHidden = hideFully; + preExit(); + } + + dump("slideTo"); + + if (mFullyHidden) { + + mTopBar.setAlpha(1.0f - offset); + mBottomBar.setAlpha(1.0f - offset); + + if (isVertical()) { + mTopBar.setTranslationY(getTopTranslation()); + mBottomBar.setTranslationY(getBottomTranslation()); + } else { + mTopBar.setTranslationX(getTopTranslation()); + mBottomBar.setTranslationX(getBottomTranslation()); + } + } else { + for (int i = 0; i < mBottomBar.getChildCount(); i++) { + View v = mBottomBar.getChildAt(i); + if (v.getVisibility() != View.GONE) { + if (isVertical()) { + v.setTranslationY(getBottomTranslation()); + } else { + v.setTranslationX(getBottomTranslation()); + } + } + } + } + + if (mEntering) { + postEnter(); + } else { + postExit(); + } + } + + private void reset(View v) { + v.setVisibility(View.VISIBLE); + v.setTranslationX(0.0f); + v.setTranslationY(0.0f); + v.setAlpha(1.0f); + } + + public void reset() { + if (mAnimator != null) { + mAnimator.cancel(); + } + for (int i = 0; i < mBottomBar.getChildCount(); i++) { + View v = mBottomBar.getChildAt(i); + if (v.getVisibility() != View.GONE) { + reset(v); + } + } + reset(mTopBar); + reset(mBottomBar); + + mCurrentOffset = 0.0f; + mTargetOffset = 0.0f; + mFullyHidden = false; + } + + private void animate() { + if (mAnimator != null) { + mAnimator.cancel(); + } + + mAnimator = new AnimatorSet(); + + if (mFullyHidden) { + mAnimator.playTogether( + getViewAnimation(mTopBar, getTopTranslation()), + getViewAnimation(mBottomBar, getBottomTranslation())); + } else { + final ArrayList anims = new ArrayList<>(); + for (int i = 0; i < mBottomBar.getChildCount(); i++) { + View v = mBottomBar.getChildAt(i); + if (v.getVisibility() != View.GONE) { + anims.add(getViewAnimation(v, getBottomTranslation())); + } + } + if (anims.size() > 0) { + mAnimator.playTogether(anims.toArray(new ObjectAnimator[anims.size()])); + } + } + + mAnimator.start(); + } + + public boolean isAnimating() { + return (mAnimator != null && mAnimator.isRunning()) || !areControlsVisible(); + } + + public boolean areControlsVisible() { + dump("hasOffset"); + return mCurrentOffset == 0.0f; + } + + private float getBottomTranslation() { + return MathUtils.lerp(0.0f, mBottomMargin, mTargetOffset); + } + + private float getTopTranslation() { + return MathUtils.lerp(0.0f, -mTopMargin, mTargetOffset); + } + + private boolean isVertical() { + return true; + //(getUnifiedRotation() / 90) < 2; + } + + private int getDuration() { + return (mCurrentOffset == 0.0f && mTargetOffset == 1.0f) ? 200 : 20; + } + + private AnimatorListener getListener() { + return mEntering ? mEnterListener : mExitListener; + } + + private ObjectAnimator getViewAnimation(View v, float target) { + final ObjectAnimator anim = ObjectAnimator.ofFloat(v, + (isVertical() ? "translationY" : "translationX"), target); + anim.setDuration(getDuration()); + anim.addListener(getListener()); + return anim; + } + + private void preEnter() { + if (mCurrentOffset == 1.0f) { + if (mFullyHidden) { + mTopBar.setVisibility(View.VISIBLE); + mBottomBar.setVisibility(View.VISIBLE); + } else { + setChildrenVisibility(mBottomBar, true); + } + } + dump("preEnter"); + } + + private void postEnter() { + if (mTargetOffset == 0.0f) { + enableTouch(true); + if ((mRemainingPhotos.getVisibility() == View.INVISIBLE) && + !mHideRemainingPhoto) { + mRemainingPhotos.setVisibility(View.VISIBLE); + } + mRefocusToast.setVisibility(View.GONE); + } + mCurrentOffset = mTargetOffset; + dump("postEnter"); + } + + private void preExit() { + if (mCurrentOffset == 0.0f) { + collapse(); + enableTouch(false); + } + dump("preExit"); + } + + private void postExit() { + if (mTargetOffset == 1.0f) { + if (mFullyHidden) { + mTopBar.setVisibility(View.INVISIBLE); + mBottomBar.setVisibility(View.INVISIBLE); + } else { + setChildrenVisibility(mBottomBar, false); + } + mRemainingPhotos.setVisibility(View.INVISIBLE); + mRefocusToast.setVisibility(View.GONE); + } + mCurrentOffset = mTargetOffset; + dump("postExit"); + } + + private void dump(String info) { + if (!DEBUG) { + return; + } + float[] tx = new float[mBottomBar.getChildCount()]; + float[] ty = new float[mBottomBar.getChildCount()]; + for (int i = 0; i < mBottomBar.getChildCount(); i++) { + tx[i] = mBottomBar.getChildAt(i).getTranslationX(); + ty[i] = mBottomBar.getChildAt(i).getTranslationY(); + } + Log.d(TAG, String.format( + "animate: (%s) currentOffset=%f targetOffset=%f top=%f bottom=%f vertical=%b " + + " topX=%f topY=%f bottomX=%f bottomY=%f " + + " viewsX=%s viewsY=%s fullyHidden=%b entering=%b", + info, mCurrentOffset, mTargetOffset, + getTopTranslation(), getBottomTranslation(), isVertical(), + mTopBar.getTranslationX(), mTopBar.getTranslationY(), + mBottomBar.getTranslationX(), mBottomBar.getTranslationY(), + Arrays.toString(tx), Arrays.toString(ty), mFullyHidden, mEntering)); + } + + private final AnimatorListener mEnterListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + preEnter(); + } + + @Override + public void onAnimationEnd(Animator animation) { + postEnter(); + } + }; + + private final AnimatorListener mExitListener = new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + preExit(); + } + + @Override + public void onAnimationEnd(Animator animation) { + postExit(); + } + }; + } + + private static class ArrowTextView extends TextView { private static final int TEXT_SIZE = 14; private static final int PADDING_SIZE = 18; private static final int BACKGROUND = 0x80000000; diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index 47f480e57..53cff67da 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -100,6 +100,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private int mDataIdOnUserScrolling; private ValueAnimator.AnimatorUpdateListener mViewItemUpdateListener; private float mOverScaleFactor = 1f; + private int mCurrentOffset = 0; private int mLastTotalNumber = 0; private RenderOverlay mRenderOverlay; @@ -1473,7 +1474,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { // two values and compare that to a fixed reference point. Since we know that // the camera preview always starts at (0,0) we can use it as a reference point. int offset = -1 * (mDrawArea.centerX() + mCameraViewItem.getLeftPosition() - mCenterX); - mListener.onFilmStripScroll(offset); + if (offset != mCurrentOffset) { + mCurrentOffset = offset; + mListener.onFilmStripScroll(offset); + } } } -- cgit v1.2.3