summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/camera/CameraActivity.java19
-rw-r--r--src/com/android/camera/PhotoModule.java8
-rw-r--r--src/com/android/camera/PhotoUI.java13
-rw-r--r--src/com/android/camera/VideoModule.java2
-rw-r--r--src/com/android/camera/VideoUI.java13
-rw-r--r--src/com/android/camera/ui/CameraRootView.java4
-rw-r--r--src/com/android/camera/ui/FilmStripView.java157
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) {