diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/camera/CameraActivity.java | 19 | ||||
-rw-r--r-- | src/com/android/camera/PhotoModule.java | 8 | ||||
-rw-r--r-- | src/com/android/camera/PhotoUI.java | 13 | ||||
-rw-r--r-- | src/com/android/camera/VideoModule.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/VideoUI.java | 13 | ||||
-rw-r--r-- | src/com/android/camera/ui/CameraRootView.java | 4 | ||||
-rw-r--r-- | src/com/android/camera/ui/FilmStripView.java | 157 |
7 files changed, 191 insertions, 25 deletions
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 282c2fe21..6fce2ffb8 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -419,7 +419,7 @@ public class CameraActivity extends Activity * to lights-out mode. */ - private void setSystemBarsVisibility(boolean visible) { + public void setSystemBarsVisibility(boolean visible) { setSystemBarsVisibility(visible, false); } @@ -432,10 +432,17 @@ public class CameraActivity extends Activity mMainHandler.removeMessages(HIDE_ACTION_BAR); boolean currentlyVisible = mActionBar.isShowing(); + // We need to set these flags independently of the action bar + // visibility, as the system can change the system UI visibility + // independently. + // Note: setSystemUiVisibitliy will be a no-op if the flag passed in is + // the same as the current one, so it's OK to call this often. Adding an + // additional check here is therefore unnecessary. + int visibility = DEFAULT_SYSTEM_UI_VISIBILITY | (visible ? View.SYSTEM_UI_FLAG_VISIBLE + : View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN); + mAboveFilmstripControlLayout.setSystemUiVisibility(visibility); + if (visible != currentlyVisible) { - int visibility = DEFAULT_SYSTEM_UI_VISIBILITY | (visible ? View.SYSTEM_UI_FLAG_VISIBLE - : View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN); - mAboveFilmstripControlLayout.setSystemUiVisibility(visibility); if (visible) { mActionBar.show(); } else { @@ -853,7 +860,9 @@ public class CameraActivity extends Activity @Override protected void onPostExecute(MediaDetails mediaDetails) { - DetailsDialog.create(CameraActivity.this, mediaDetails).show(); + if (mediaDetails != null) { + DetailsDialog.create(CameraActivity.this, mediaDetails).show(); + } } }).execute(); return true; diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 1cebb4eb5..8699ef092 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -18,12 +18,10 @@ package com.android.camera; import android.annotation.TargetApi; import android.app.Activity; -import android.content.BroadcastReceiver; import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.graphics.Bitmap; @@ -530,7 +528,9 @@ public class PhotoModule // once only. We could have done these things in onCreate() but we want to // make preview screen appear as soon as possible. private void initializeFirstTime() { - if (mFirstTimeInitialized) return; + if (mFirstTimeInitialized || mPaused) { + return; + } // Initialize location service. boolean recordLocation = RecordLocationPreference.get( @@ -1202,6 +1202,7 @@ public class PhotoModule } else { initializeSecondTime(); } + mUI.initDisplayChangeListener(); keepScreenOnAwhile(); UsageStatistics.onContentViewChanged( @@ -1272,6 +1273,7 @@ public class PhotoModule if (s != null) { s.setListener(null); } + mUI.removeDisplayChangeListener(); } /** diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 58751efbf..9273e21fd 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -215,7 +215,6 @@ public class PhotoUI implements PieListener, setSurfaceTextureSizeChangedListener(mFaceView); } mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls); - ((CameraRootView) mRootView).setDisplayChangeListener(this); mAnimationManager = new AnimationManager(); } @@ -573,6 +572,10 @@ public class PhotoUI implements PieListener, mPopup = null; mMenu.popupDismissed(); showUI(); + + // Switch back into fullscreen/lights-out mode after popup + // is dimissed. + mActivity.setSystemBarsVisibility(false); } }); } @@ -761,6 +764,14 @@ public class PhotoUI implements PieListener, mPreviewHeight = 0; } + public void initDisplayChangeListener() { + ((CameraRootView) mRootView).setDisplayChangeListener(this); + } + + public void removeDisplayChangeListener() { + ((CameraRootView) mRootView).removeDisplayChangeListener(); + } + // focus UI implementation private FocusIndicator getFocusIndicator() { diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 4daf01a5a..012376c82 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -679,6 +679,7 @@ public class VideoModule implements CameraModule, mUI.enableShutter(true); } + mUI.initDisplayChangeListener(); // Initializing it here after the preview is started. mUI.initializeZoom(mParameters); @@ -827,6 +828,7 @@ public class VideoModule implements CameraModule, mPreferenceRead = false; mUI.collapseCameraControls(); + mUI.removeDisplayChangeListener(); } @Override diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 7080032f4..f097d9344 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -150,6 +150,10 @@ public class VideoUI implements PieRenderer.PieListener, popupDismissed(); showUI(); mVideoMenu.popupDismissed(topLevelOnly); + + // Switch back into fullscreen/lights-out mode after popup + // is dimissed. + mActivity.setSystemBarsVisibility(false); } @Override @@ -167,7 +171,6 @@ public class VideoUI implements PieRenderer.PieListener, mTextureView = (TextureView) mRootView.findViewById(R.id.preview_content); mTextureView.setSurfaceTextureListener(this); mTextureView.addOnLayoutChangeListener(mLayoutListener); - ((CameraRootView) mRootView).setDisplayChangeListener(this); mFlashOverlay = mRootView.findViewById(R.id.flash_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher); @@ -377,6 +380,14 @@ public class VideoUI implements PieRenderer.PieListener, } } + public void initDisplayChangeListener() { + ((CameraRootView) mRootView).setDisplayChangeListener(this); + } + + public void removeDisplayChangeListener() { + ((CameraRootView) mRootView).removeDisplayChangeListener(); + } + public void overrideSettings(final String... keyvalues) { mVideoMenu.overrideSettings(keyvalues); } diff --git a/src/com/android/camera/ui/CameraRootView.java b/src/com/android/camera/ui/CameraRootView.java index 1e4708ec7..505549c80 100644 --- a/src/com/android/camera/ui/CameraRootView.java +++ b/src/com/android/camera/ui/CameraRootView.java @@ -88,6 +88,10 @@ public class CameraRootView extends FrameLayout { } } + public void removeDisplayChangeListener() { + mListener = null; + } + public void setDisplayChangeListener(MyDisplayListener listener) { mListener = listener; } diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index b6f83adf1..f41f08114 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -17,6 +17,7 @@ package com.android.camera.ui; import android.animation.Animator; +import android.animation.AnimatorSet; import android.animation.TimeInterpolator; import android.animation.ValueAnimator; import android.app.Activity; @@ -25,7 +26,6 @@ import android.content.res.Configuration; import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.RectF; -import android.hardware.display.DisplayManager; import android.net.Uri; import android.os.Handler; import android.util.AttributeSet; @@ -34,7 +34,6 @@ import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.Scroller; @@ -42,6 +41,7 @@ import com.android.camera.CameraActivity; import com.android.camera.data.LocalData; import com.android.camera.ui.FilmStripView.ImageData.PanoramaSupportCallback; import com.android.camera.ui.FilmstripBottomControls.BottomControlsListener; +import com.android.camera.util.CameraUtil; import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; import com.android.camera2.R; @@ -53,6 +53,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private static final int BUFFER_SIZE = 5; private static final int GEOMETRY_ADJUST_TIME_MS = 400; private static final int SNAP_IN_CENTER_TIME_MS = 600; + private static final float FLING_COASTING_DURATION_S = 0.05f; private static final int ZOOM_ANIMATION_DURATION_MS = 200; private static final int CAMERA_PREVIEW_SWIPE_THRESHOLD = 300; private static final float FILM_STRIP_SCALE = 0.5f; @@ -61,6 +62,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private static final float TOLERANCE = 0.1f; // Only check for intercepting touch events within first 500ms private static final int SWIPE_TIME_OUT = 500; + private static final int DECELERATION_FACTOR = 4; private CameraActivity mActivity; private FilmStripGestureRecognizer mGestureRecognizer; @@ -377,6 +379,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { public void fling(float velocity); + public void flingInsideZoomView (float velocityX, float velocityY); + public void scrollToPosition(int position, int duration, boolean interruptible); public boolean goToNextItem(); @@ -878,16 +882,17 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { // not in transition. return; } - int nearest = findTheNearestView(mCenterX); + final int nearest = findTheNearestView(mCenterX); // no change made. - if (nearest == -1 || nearest == mCurrentItem) + if (nearest == -1 || nearest == mCurrentItem) { return; + } // Going to change the current item, notify the listener. if (mListener != null) { mListener.onCurrentDataChanged(mViewItem[mCurrentItem].getId(), false); } - int adjust = nearest - mCurrentItem; + final int adjust = nearest - mCurrentItem; if (adjust > 0) { for (int k = 0; k < adjust; k++) { removeItem(k); @@ -901,6 +906,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { mViewItem[k] = buildItemFromData(mViewItem[k - 1].getId() + 1); } } + adjustChildZOrder(); } else { for (int k = BUFFER_SIZE - 1; k >= BUFFER_SIZE + adjust; k--) { removeItem(k); @@ -915,6 +921,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } } } + invalidate(); if (mListener != null) { mListener.onCurrentDataChanged(mViewItem[mCurrentItem].getId(), true); } @@ -953,6 +960,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return stopScroll; } + /** + * Reorders the child views to be consistent with their data ID. This + * method should be called after adding/removing views. + */ private void adjustChildZOrder() { for (int i = BUFFER_SIZE - 1; i >= 0; i--) { if (mViewItem[i] == null) @@ -1024,14 +1035,12 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return; } - // We cannot rely on the requestIds to check for data changes, - // because an item hands its id to its rightmost neighbor on - // deletion. To avoid loading the ImageData, we check if the DataAdapter - // has fewer total items. We don't want to miss setting the controls - // for a photo while we're still in capture mode, so ignore its fixed - // requestId (0). + // We cannot rely on the requestIds alone to check for data changes, + // because an item hands its id to its rightmost neighbor on deletion. + // To avoid loading the ImageData, we also check if the DataAdapter + // has fewer total items. int total = mDataAdapter.getTotalNumber(); - if (!force && (requestId == 0 || mLastTotalNumber == total)) { + if (!force && requestId == mLastItemId && mLastTotalNumber == total) { return; } mLastTotalNumber = total; @@ -1325,7 +1334,6 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } stepIfNeeded(); - adjustChildZOrder(); updateBottomControls(false /* no forced update */); mLastItemId = getCurrentId(); } @@ -1372,6 +1380,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return; } mScale = FULL_SCREEN_SCALE; + mController.cancelZoomAnimation(); + mController.cancelFlingAnimation(); current.resetTransform(); mController.cancelLoadingZoomedImage(); mZoomView.setVisibility(GONE); @@ -1538,6 +1548,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } }) .start(); + adjustChildZOrder(); invalidate(); } @@ -1625,6 +1636,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { .setInterpolator(mViewAnimInterpolator) .setDuration(GEOMETRY_ADJUST_TIME_MS) .start(); + adjustChildZOrder(); invalidate(); } @@ -1740,6 +1752,8 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (clampCenterX()) { mController.stopScrolling(true); } + adjustChildZOrder(); + invalidate(); } /** Some of the data is changed. */ @@ -1801,6 +1815,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } } } + adjustChildZOrder(); // Request a layout to find the measured width/height of the view first. requestLayout(); // Update photo sphere visibility after metadata fully written. @@ -1855,6 +1870,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { mCenterX = -1; mScale = FULL_SCREEN_SCALE; + adjustChildZOrder(); invalidate(); if (mListener != null) { @@ -1882,6 +1898,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { private final ValueAnimator mScaleAnimator; private ValueAnimator mZoomAnimator; + private AnimatorSet mFlingAnimator; private final MyScroller mScroller; private boolean mCanStopScroll; @@ -2065,6 +2082,94 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } @Override + public void flingInsideZoomView(float velocityX, float velocityY) { + if (!isZoomStarted()) { + return; + } + + final ViewItem current = mViewItem[mCurrentItem]; + if (current == null) { + return; + } + + final int factor = DECELERATION_FACTOR; + // Deceleration curve for distance: + // S(t) = s + (e - s) * (1 - (1 - t/T) ^ factor) + // Need to find the ending distance (e), so that the starting velocity + // is the velocity of fling. + // Velocity is the derivative of distance + // V(t) = (e - s) * factor * (-1) * (1 - t/T) ^ (factor - 1) * (-1/T) + // = (e - s) * factor * (1 - t/T) ^ (factor - 1) / T + // Since V(0) = V0, we have e = T / factor * V0 + s + + // Duration T should be long enough so that at the end of the fling, + // image moves at 1 pixel/s for about P = 50ms = 0.05s + // i.e. V(T - P) = 1 + // V(T - P) = V0 * (1 - (T -P) /T) ^ (factor - 1) = 1 + // T = P * V0 ^ (1 / (factor -1)) + + final float velocity = Math.max(Math.abs(velocityX), Math.abs(velocityY)); + // Dynamically calculate duration + final float duration = (float) (FLING_COASTING_DURATION_S + * Math.pow(velocity, (1f/ (factor - 1f)))); + + final float translationX = current.getTranslationX(); + final float translationY = current.getTranslationY(); + + final ValueAnimator decelerationX = ValueAnimator.ofFloat(translationX, + translationX + duration / factor * velocityX); + final ValueAnimator decelerationY = ValueAnimator.ofFloat(translationY, + translationY + duration / factor * velocityY); + + decelerationY.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animation) { + float transX = (Float) decelerationX.getAnimatedValue(); + float transY = (Float) decelerationY.getAnimatedValue(); + + current.updateTransform(transX, transY, mScale, + mScale, mDrawArea.width(), mDrawArea.height()); + } + }); + + mFlingAnimator = new AnimatorSet(); + mFlingAnimator.play(decelerationX).with(decelerationY); + mFlingAnimator.setDuration((int) (duration * 1000)); + mFlingAnimator.setInterpolator(new TimeInterpolator() { + @Override + public float getInterpolation(float input) { + return (float)(1.0f - Math.pow((1.0f - input), factor)); + } + }); + mFlingAnimator.addListener(new Animator.AnimatorListener() { + private boolean mCancelled = false; + @Override + public void onAnimationStart(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + if (!mCancelled) { + loadZoomedImage(); + } + mFlingAnimator = null; + } + + @Override + public void onAnimationCancel(Animator animation) { + mCancelled = true; + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + }); + mFlingAnimator.start(); + } + + @Override public boolean stopScrolling(boolean forced) { if (!isScrolling()) { return true; @@ -2141,6 +2246,19 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { scaleTo(1f, GEOMETRY_ADJUST_TIME_MS); } + private void cancelFlingAnimation() { + // Cancels flinging for zoomed images + if (isFlingAnimationRunning()) { + mFlingAnimator.cancel(); + } + } + + private void cancelZoomAnimation() { + if (isZoomAnimationRunning()) { + mZoomAnimator.cancel(); + } + } + private void enterFullScreen() { if (mListener != null) { mListener.onDataFullScreenChange(mViewItem[mCurrentItem].getId(), true); @@ -2236,6 +2354,10 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return mScale > FULL_SCREEN_SCALE; } + public boolean isFlingAnimationRunning() { + return mFlingAnimator != null && mFlingAnimator.isRunning(); + } + public boolean isZoomAnimationRunning() { return mZoomAnimator != null && mZoomAnimator.isRunning(); } @@ -2398,6 +2520,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public boolean onDown(float x, float y) { + mController.cancelFlingAnimation(); if (!mController.stopScrolling(false)) { return false; } @@ -2417,7 +2540,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (currItem == null) { return false; } - if (mController.isZoomAnimationRunning()) { + if (mController.isZoomAnimationRunning() || mController.isFlingAnimationRunning()) { return false; } if (mController.isZoomStarted()) { @@ -2553,6 +2676,11 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { if (currItem == null) { return false; } + if (mController.isZoomStarted()) { + // Fling within the zoomed image + mController.flingInsideZoomView(velocityX, velocityY); + return true; + } if (Math.abs(velocityX) < Math.abs(velocityY)) { // ignore vertical fling. return true; @@ -2662,7 +2790,6 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return true; } - @Override public void onScaleEnd() { if (mScale > FULL_SCREEN_SCALE + TOLERANCE) { |