diff options
Diffstat (limited to 'src/com/android/camera/ui')
-rw-r--r-- | src/com/android/camera/ui/CameraControls.java | 150 | ||||
-rw-r--r-- | src/com/android/camera/ui/ModuleSwitcher.java | 2 | ||||
-rw-r--r-- | src/com/android/camera/ui/RecordingTime.java | 298 | ||||
-rw-r--r-- | src/com/android/camera/ui/ReversibleLinearLayout.java | 57 |
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; + } +} |