summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/ui
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/ui')
-rw-r--r--src/com/android/camera/ui/CameraControls.java150
-rw-r--r--src/com/android/camera/ui/ModuleSwitcher.java2
-rw-r--r--src/com/android/camera/ui/RecordingTime.java298
-rw-r--r--src/com/android/camera/ui/ReversibleLinearLayout.java57
4 files changed, 441 insertions, 66 deletions
diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java
index a5f7db685..740a07ea1 100644
--- a/src/com/android/camera/ui/CameraControls.java
+++ b/src/com/android/camera/ui/CameraControls.java
@@ -21,15 +21,17 @@ import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
+import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.RippleDrawable;
import android.util.AttributeSet;
import android.util.Log;
-import android.view.MotionEvent;
+import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
@@ -60,17 +62,18 @@ public class CameraControls extends RotatableLayout {
private View mVideoShutter;
private ModuleSwitcher mSwitcher;
private View mTsMakeupSwitcher;
- private View mPreview;
+ private View mThumbnail;
private View mAutoHdrNotice;
private HistogramView mHistogramView;
private ArrowTextView mRefocusToast;
+ private View mFrontBackSwitcher;
+ private View mMenu;
private View mReviewDoneButton;
private View mReviewCancelButton;
private View mReviewRetakeButton;
private final List<View> mViews = new ArrayList<>();
- private final List<View> mFreeList = new ArrayList<>();
private static final int WIDTH_GRID = 5;
private static final int HEIGHT_GRID = 7;
@@ -102,7 +105,9 @@ public class CameraControls extends RotatableLayout {
}
@Override
public void onAnimationEnd(Animator animation) {
+ setChildrenVisibility(mBottomBar, false);
if (mFullyHidden) {
+ setChildrenVisibility(mTopBar, false);
setVisibility(View.INVISIBLE);
}
}
@@ -111,7 +116,9 @@ public class CameraControls extends RotatableLayout {
AnimatorListener inlistener = new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
+ setChildrenVisibility(mBottomBar, true);
if (mFullyHidden) {
+ setChildrenVisibility(mTopBar, true);
setVisibility(View.VISIBLE);
mFullyHidden = false;
}
@@ -122,14 +129,26 @@ public class CameraControls extends RotatableLayout {
}
};
+ 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 CameraControls(Context context, AttributeSet attrs) {
super(context, attrs);
-
mRefocusToast = new ArrowTextView(context);
addView(mRefocusToast);
setClipChildren(false);
setMeasureAllChildren(true);
+
+ Pair<Integer, Integer> margins = CameraUtil.calculateMargins((Activity)context);
+ mTopMargin = margins.first;
+ mBottomMargin = margins.second;
}
public CameraControls(Context context) {
@@ -142,12 +161,15 @@ public class CameraControls extends RotatableLayout {
public void enableTouch(boolean enable) {
Log.d(TAG, "ENABLE TOUCH " + enable + " mViews.size=" + mViews.size());
- for (View v : mViews) {
- if (v.getVisibility() != View.GONE) {
- if (enable) {
- v.setPressed(false);
+
+ synchronized (mViews) {
+ for (View v : mViews) {
+ if (v.getVisibility() != View.GONE) {
+ if (enable) {
+ v.setPressed(false);
+ }
+ v.setEnabled(enable);
}
- v.setEnabled(enable);
}
}
@@ -158,11 +180,13 @@ public class CameraControls extends RotatableLayout {
}
public void removeFromViewList(View view) {
- synchronized (mFreeList) {
+ synchronized (mViews) {
if (view == null || !mViews.contains(view)) {
return;
}
- mFreeList.add(view);
+ view.setVisibility(View.GONE);
+ removeView(view);
+ mViews.remove(view);
requestLayout();
}
}
@@ -176,26 +200,30 @@ public class CameraControls extends RotatableLayout {
mShutter = (ShutterButton) findViewById(R.id.shutter_button);
mVideoShutter = findViewById(R.id.video_button);
mTsMakeupSwitcher = findViewById(R.id.ts_makeup_switcher);
- mPreview = findViewById(R.id.preview_thumb);
+ mThumbnail = findViewById(R.id.preview_thumb);
mRemainingPhotos = (LinearLayout) findViewById(R.id.remaining_photos);
mRemainingPhotosText = (TextView) findViewById(R.id.remaining_photos_text);
mAutoHdrNotice = (TextView) findViewById(R.id.auto_hdr_notice);
mHistogramView = (HistogramView) findViewById(R.id.histogram);
+ mFrontBackSwitcher = findViewById(R.id.front_back_switcher);
+ mMenu = findViewById(R.id.menu);
if (!TsMakeupManager.HAS_TS_MAKEUP) {
mTopBar.removeView(mTsMakeupSwitcher);
}
- for (int i = 0; i < mTopBar.getChildCount(); i++) {
- mViews.add(mTopBar.getChildAt(i));
- }
+ synchronized (mViews) {
+ for (int i = 0; i < mTopBar.getChildCount(); i++) {
+ mViews.add(mTopBar.getChildAt(i));
+ }
- for (int i = 0; i < mBottomBar.getChildCount(); i++) {
- mViews.add(mBottomBar.getChildAt(i));
- }
+ for (int i = 0; i < mBottomBar.getChildCount(); i++) {
+ mViews.add(mBottomBar.getChildAt(i));
+ }
- mViews.add(mAutoHdrNotice);
- mViews.add(mHistogramView);
+ mViews.add(mAutoHdrNotice);
+ mViews.add(mHistogramView);
+ }
mShutter.addOnShutterButtonListener(mShutterListener);
@@ -222,13 +250,13 @@ public class CameraControls extends RotatableLayout {
public void hideSwitcher() {
if (mSwitcher != null) {
mSwitcher.closePopup();
- mSwitcher.setVisibility(View.INVISIBLE);
+ mSwitcher.setEnabled(false);
}
}
public void showSwitcher() {
if (mSwitcher != null) {
- mSwitcher.setVisibility(View.VISIBLE);
+ mSwitcher.setEnabled(true);
}
}
@@ -271,26 +299,9 @@ public class CameraControls extends RotatableLayout {
@Override
public void onLayout(boolean changed, int l, int t, int r, int b) {
- synchronized (mFreeList) {
- if (mFreeList.size() > 0) {
- for (View v : mFreeList) {
- v.setVisibility(View.GONE);
- removeView(v);
- mViews.remove(v);
- }
- }
- }
+ Log.d(TAG, String.format("onLayout changed=%b l=%d t=%d r=%d b=%d", changed, l, t, r, b));
- // As l,t,r,b are positions relative to parents, we need to convert them
- // to child's coordinates
- r = r - l;
- b = b - t;
- l = 0;
- t = 0;
- for (int i = 0; i < getChildCount(); i++) {
- View v = getChildAt(i);
- v.layout(l, t, r, b);
- }
+ super.onLayout(changed, l, t, r, b);
ViewGroup.LayoutParams lpTop = mTopBar.getLayoutParams();
lpTop.height = mTopMargin;
@@ -346,6 +357,19 @@ 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);
@@ -452,6 +476,11 @@ public class CameraControls extends RotatableLayout {
mAnimator.start();
}
+ public void setMenuAndSwitcherEnabled(boolean enable) {
+ mMenu.setEnabled(enable);
+ mFrontBackSwitcher.setEnabled(enable);
+ }
+
public void hideUI(boolean toBlack) {
if (mHidden) {
return;
@@ -493,10 +522,10 @@ public class CameraControls extends RotatableLayout {
}
private void layoutRemaingPhotos() {
- int rl = mPreview.getLeft();
- int rt = mPreview.getTop();
- int rr = mPreview.getRight();
- int rb = mPreview.getBottom();
+ int rl = mThumbnail.getLeft();
+ int rt = mThumbnail.getTop();
+ int rr = mThumbnail.getRight();
+ int rb = mThumbnail.getBottom();
int w = mRemainingPhotos.getMeasuredWidth();
int h = mRemainingPhotos.getMeasuredHeight();
int m = getResources().getDimensionPixelSize(R.dimen.remaining_photos_margin);
@@ -537,25 +566,14 @@ public class CameraControls extends RotatableLayout {
return !mHidden;
}
- public void setMargins(int top, int bottom) {
- mTopMargin = top;
- mBottomMargin = bottom;
- }
-
- private void setBarsBackground(int resId) {
- mTopBar.setBackgroundResource(resId);
- mBottomBar.setBackgroundResource(resId);
- }
-
- public void setPreviewRatio(float ratio, boolean panorama) {
- int r = CameraUtil.determineRatio(ratio);
+ public void setPreviewRect(RectF rectL) {
+ int r = CameraUtil.determineRatio(Math.round(rectL.width()), Math.round(rectL.height()));
mPreviewRatio = r;
if (mPreviewRatio == CameraUtil.RATIO_4_3 && mTopMargin != 0) {
- setBarsBackground(R.drawable.camera_controls_bg_opaque);
+ mBottomBar.setBackgroundResource(R.drawable.camera_controls_bg_opaque);
} else {
- setBarsBackground(R.drawable.camera_controls_bg_translucent);
+ mBottomBar.setBackgroundResource(R.drawable.camera_controls_bg_translucent);
}
- requestLayout();
}
public void showRefocusToast(boolean show) {
@@ -577,11 +595,13 @@ public class CameraControls extends RotatableLayout {
public void setOrientation(int orientation, boolean animation) {
mOrientation = orientation;
- for (View v : mViews) {
- if (v instanceof RotateImageView) {
- ((RotateImageView) v).setOrientation(orientation, animation);
- } else if (v instanceof HistogramView) {
- ((HistogramView) v).setRotation(-orientation);
+ synchronized (mViews) {
+ for (View v : mViews) {
+ if (v instanceof RotateImageView) {
+ ((RotateImageView) v).setOrientation(orientation, animation);
+ } else if (v instanceof HistogramView) {
+ ((HistogramView) v).setRotation(-orientation);
+ }
}
}
layoutRemaingPhotos();
diff --git a/src/com/android/camera/ui/ModuleSwitcher.java b/src/com/android/camera/ui/ModuleSwitcher.java
index d51edb19a..90ba05484 100644
--- a/src/com/android/camera/ui/ModuleSwitcher.java
+++ b/src/com/android/camera/ui/ModuleSwitcher.java
@@ -159,7 +159,7 @@ public class ModuleSwitcher extends RotateImageView {
// Closes the popup window when touch outside of it - when looses focus
popup.setOutsideTouchable(true);
popup.setFocusable(true);
- popup.setBackgroundDrawable(new ColorDrawable(Color.WHITE));
+ popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
popup.setAnimationStyle(android.R.style.Animation_Dialog);
return popup;
}
diff --git a/src/com/android/camera/ui/RecordingTime.java b/src/com/android/camera/ui/RecordingTime.java
new file mode 100644
index 000000000..6e07fa69b
--- /dev/null
+++ b/src/com/android/camera/ui/RecordingTime.java
@@ -0,0 +1,298 @@
+/*
+ * 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.ui;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.transition.*;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.camera.PauseButton;
+
+import org.codeaurora.snapcam.R;
+
+public class RecordingTime extends RotateLayout implements PauseButton.OnPauseButtonListener {
+
+ private static final String TAG = "CAM_" + RecordingTime.class.getSimpleName();
+
+ private PauseButton mPauseButton;
+ private TextView mRecordingTimeText;
+ private TextView mTimeLapseLabel;
+ private ReversibleLinearLayout mRecordingTimeContainer;
+
+ private PauseButton.OnPauseButtonListener mPauseListener;
+
+ private boolean mPaused = false;
+ private boolean mStarted = false;
+ private boolean mTimeLapse = false;
+
+ private long mRecordingStartTime;
+ private long mRecordingTotalTime;
+
+ private int mFrameRate = 0;
+ private long mInterval = 0;
+ private long mDurationMs = 0;
+ private boolean mRecordingTimeCountsDown = false;
+
+ private static final int UPDATE_RECORD_TIME = 0;
+
+ private final Handler mHandler = new Handler() {
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case UPDATE_RECORD_TIME:
+ updateRecordingTime();
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ public RecordingTime(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ mPauseButton = (PauseButton) findViewById(R.id.video_pause);
+ mRecordingTimeText = (TextView) findViewById(R.id.recording_time_text);
+ mRecordingTimeContainer = (ReversibleLinearLayout) findViewById(R.id.recording_time_container);
+ mTimeLapseLabel = (TextView) findViewById(R.id.time_lapse_label);
+ mPauseButton.setOnPauseButtonListener(this);
+ setAlpha(0.0f);
+ }
+
+ public void setOrientation(int orientation) {
+ if (mRecordingTimeText != null) {
+ Log.d(TAG, "orientation=" + orientation);
+ setRotation(orientation);
+ float topBar = getContext().getResources().getDimension(R.dimen.preview_top_margin);
+
+ if (orientation == 0 || orientation == 270) {
+ mRecordingTimeContainer.setOrder(ReversibleLinearLayout.FORWARD);
+ } else {
+ mRecordingTimeContainer.setOrder(ReversibleLinearLayout.REVERSE);
+ }
+
+ if (orientation == 0 || orientation == 180) {
+ setTranslationX(0);
+ setTranslationY(0);
+ mRecordingTimeText.setRotation(0);
+ if (mTimeLapse) {
+ mTimeLapseLabel.setRotation(0);
+ }
+ } else {
+ setTranslationX(-topBar);
+ setTranslationY(topBar);
+ mRecordingTimeText.setRotation(180);
+ if (mTimeLapse) {
+ mTimeLapseLabel.setRotation(180);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onButtonPause() {
+ mPaused = true;
+ mRecordingTotalTime += SystemClock.uptimeMillis() - mRecordingStartTime;
+
+ mRecordingTimeText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_pausing_indicator, 0, 0, 0);
+ if (mPauseListener != null) {
+ mPauseListener.onButtonPause();
+ }
+ }
+
+ @Override
+ public void onButtonContinue() {
+ mPaused = false;
+ mRecordingStartTime = SystemClock.uptimeMillis();
+
+ mRecordingTimeText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_recording_indicator, 0, 0, 0);
+ if (mPauseListener != null) {
+ mPauseListener.onButtonContinue();
+ }
+ updateRecordingTime();
+ }
+
+ public void reset() {
+ mRecordingTimeText.setCompoundDrawablesWithIntrinsicBounds(
+ R.drawable.ic_recording_indicator, 0, 0, 0);
+ mStarted = false;
+ mPauseButton.setPaused(false);
+ mPaused = false;
+ mRecordingTimeText.setText("");
+ }
+
+ public void start() {
+ start(0, 0, 0);
+ }
+
+ public void start(int frameRate, long frameInterval, long durationMs) {
+ reset();
+ mFrameRate = frameRate;
+ mInterval = frameInterval;
+ mDurationMs = durationMs;
+ mRecordingTotalTime = 0L;
+ mRecordingStartTime = SystemClock.uptimeMillis();
+ mStarted = true;
+ animate().alpha(1.0f).withStartAction(new Runnable() {
+ @Override
+ public void run() {
+ updateRecordingTime();
+ setVisibility(View.VISIBLE);
+ }
+ });
+
+ Log.d(TAG, "started: frameRate=" + frameRate + " frameInterval=" + frameInterval + " maxDuration=" + durationMs);
+ }
+
+ public void stop() {
+ mStarted = false;
+ animate().alpha(0.0f).withEndAction(new Runnable() {
+ @Override
+ public void run() {
+ setVisibility(View.GONE);
+ }
+ });
+ }
+
+ public void showTimeLapse(boolean show) {
+ mTimeLapse = show;
+ mTimeLapseLabel.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+
+ public long getTime() {
+ return mPaused ? mRecordingTotalTime :
+ SystemClock.uptimeMillis() - mRecordingStartTime + mRecordingTotalTime;
+ }
+
+ public void setPauseListener(PauseButton.OnPauseButtonListener listener) {
+ mPauseListener = listener;
+ }
+
+ private void updateRecordingTime() {
+ if (!mStarted || mPaused) {
+ return;
+ }
+
+ long now = SystemClock.uptimeMillis();
+ long delta = now - mRecordingStartTime + mRecordingTotalTime;
+
+ // Starting a minute before reaching the max duration
+ // limit, we'll countdown the remaining time instead.
+ boolean countdownRemainingTime = (mDurationMs != 0
+ && delta >= mDurationMs - 60000);
+
+ long deltaAdjusted = delta;
+ if (countdownRemainingTime) {
+ deltaAdjusted = Math.max(0, mDurationMs - deltaAdjusted) + 999;
+ }
+ String text;
+
+ long targetNextUpdateDelay;
+ if (mInterval <= 0) {
+ text = millisecondToTimeString(deltaAdjusted, false);
+ targetNextUpdateDelay = 1000;
+ } else {
+ // The length of time lapse video is different from the length
+ // of the actual wall clock time elapsed. Display the video length
+ // only in format hh:mm:ss.dd, where dd are the centi seconds.
+ text = millisecondToTimeString(getTimeLapseVideoLength(delta), true);
+ targetNextUpdateDelay = mInterval;
+ }
+
+ mRecordingTimeText.setText(text);
+
+ if (mRecordingTimeCountsDown != countdownRemainingTime) {
+ // Avoid setting the color on every update, do it only
+ // when it needs changing.
+ mRecordingTimeCountsDown = countdownRemainingTime;
+
+ int color = getContext().getResources().getColor(countdownRemainingTime
+ ? R.color.recording_time_remaining_text
+ : R.color.recording_time_elapsed_text);
+
+ mRecordingTimeText.setTextColor(color);
+ }
+
+ long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay);
+ mHandler.sendEmptyMessageDelayed(
+ UPDATE_RECORD_TIME, actualNextUpdateDelay);
+ }
+
+ private long getTimeLapseVideoLength(long deltaMs) {
+ // For better approximation calculate fractional number of frames captured.
+ // This will update the video time at a higher resolution.
+ double numberOfFrames = (double) deltaMs / mInterval;
+ return (long) (numberOfFrames / mFrameRate * 1000);
+ }
+
+ private static String millisecondToTimeString(long milliSeconds, boolean displayCentiSeconds) {
+ long seconds = milliSeconds / 1000; // round down to compute seconds
+ long minutes = seconds / 60;
+ long hours = minutes / 60;
+ long remainderMinutes = minutes - (hours * 60);
+ long remainderSeconds = seconds - (minutes * 60);
+
+ StringBuilder timeStringBuilder = new StringBuilder();
+
+ // Hours
+ if (hours > 0) {
+ if (hours < 10) {
+ timeStringBuilder.append('0');
+ }
+ timeStringBuilder.append(hours);
+
+ timeStringBuilder.append(':');
+ }
+
+ // Minutes
+ if (remainderMinutes < 10) {
+ timeStringBuilder.append('0');
+ }
+ timeStringBuilder.append(remainderMinutes);
+ timeStringBuilder.append(':');
+
+ // Seconds
+ if (remainderSeconds < 10) {
+ timeStringBuilder.append('0');
+ }
+ timeStringBuilder.append(remainderSeconds);
+
+ // Centi seconds
+ if (displayCentiSeconds) {
+ timeStringBuilder.append('.');
+ long remainderCentiSeconds = (milliSeconds - seconds * 1000) / 10;
+ if (remainderCentiSeconds < 10) {
+ timeStringBuilder.append('0');
+ }
+ timeStringBuilder.append(remainderCentiSeconds);
+ }
+
+ return timeStringBuilder.toString();
+ }
+}
diff --git a/src/com/android/camera/ui/ReversibleLinearLayout.java b/src/com/android/camera/ui/ReversibleLinearLayout.java
new file mode 100644
index 000000000..df9e921f0
--- /dev/null
+++ b/src/com/android/camera/ui/ReversibleLinearLayout.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+
+/**
+ * A LinearLayout which can have it's elements reversed
+ */
+public class ReversibleLinearLayout extends LinearLayout {
+
+ public static final int FORWARD = 0;
+ public static final int REVERSE = 1;
+
+ private int mState = FORWARD;
+
+ public ReversibleLinearLayout(Context context) {
+ super(context);
+ }
+
+ public ReversibleLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setOrder(int state) {
+ if (state < 0 || state > 1 || state == mState) {
+ return;
+ }
+ for(int k = getChildCount()-1 ; k >= 0 ; k--)
+ {
+ View item = getChildAt(k);
+ removeViewAt(k);
+ addView(item);
+ }
+ mState = state;
+ }
+
+ public int getOrder() {
+ return mState;
+ }
+}