summaryrefslogtreecommitdiffstats
path: root/src/com/android/camera/ui/RecordingTime.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/camera/ui/RecordingTime.java')
-rw-r--r--src/com/android/camera/ui/RecordingTime.java298
1 files changed, 298 insertions, 0 deletions
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();
+ }
+}