summaryrefslogtreecommitdiffstats
path: root/src/com/android/deskclock/stopwatch/StopwatchFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/deskclock/stopwatch/StopwatchFragment.java')
-rw-r--r--src/com/android/deskclock/stopwatch/StopwatchFragment.java1133
1 files changed, 377 insertions, 756 deletions
diff --git a/src/com/android/deskclock/stopwatch/StopwatchFragment.java b/src/com/android/deskclock/stopwatch/StopwatchFragment.java
index 8a373d7ed..efb620f9b 100644
--- a/src/com/android/deskclock/stopwatch/StopwatchFragment.java
+++ b/src/com/android/deskclock/stopwatch/StopwatchFragment.java
@@ -13,916 +13,537 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package com.android.deskclock.stopwatch;
-import android.animation.LayoutTransition;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.res.Configuration;
import android.os.Bundle;
import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.preference.PreferenceManager;
-import android.text.format.DateUtils;
+import android.os.SystemClock;
+import android.transition.AutoTransition;
+import android.transition.Transition;
+import android.transition.TransitionManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityManager;
-import android.view.animation.Animation;
-import android.view.animation.TranslateAnimation;
-import android.widget.BaseAdapter;
import android.widget.ListView;
-import android.widget.TextView;
-import com.android.deskclock.CircleButtonsLayout;
-import com.android.deskclock.CircleTimerView;
import com.android.deskclock.DeskClock;
import com.android.deskclock.DeskClockFragment;
-import com.android.deskclock.HandleDeskClockApiCalls;
import com.android.deskclock.LogUtils;
import com.android.deskclock.R;
import com.android.deskclock.Utils;
+import com.android.deskclock.data.DataModel;
+import com.android.deskclock.data.Lap;
+import com.android.deskclock.data.Stopwatch;
import com.android.deskclock.events.Events;
import com.android.deskclock.timer.CountingTimerView;
-import java.util.ArrayList;
+import static android.content.Context.ACCESSIBILITY_SERVICE;
+import static android.content.Context.POWER_SERVICE;
+import static android.os.PowerManager.ON_AFTER_RELEASE;
+import static android.os.PowerManager.SCREEN_BRIGHT_WAKE_LOCK;
+import static android.view.View.GONE;
+import static android.view.View.INVISIBLE;
+import static android.view.View.VISIBLE;
-public class StopwatchFragment extends DeskClockFragment
- implements OnSharedPreferenceChangeListener {
- private static final boolean DEBUG = false;
+/**
+ * Fragment that shows the stopwatch and recorded laps.
+ */
+public final class StopwatchFragment extends DeskClockFragment {
private static final String TAG = "StopwatchFragment";
- private static final int STOPWATCH_REFRESH_INTERVAL_MILLIS = 25;
- // Lower the refresh rate in accessibility mode to give talkback time to catch up
- private static final int STOPWATCH_ACCESSIBILTY_REFRESH_INTERVAL_MILLIS = 500;
- int mState = Stopwatches.STOPWATCH_RESET;
+ /** Scheduled to update the stopwatch time and current lap time while stopwatch is running. */
+ private final Runnable mTimeUpdateRunnable = new TimeUpdateRunnable();
- // Stopwatch views that are accessed by the activity
- private CircleTimerView mTime;
- private CountingTimerView mTimeText;
- private ListView mLapsList;
- private WakeLock mWakeLock;
- private CircleButtonsLayout mCircleLayout;
+ /** Used to determine when talk back is on in order to lower the time update rate. */
+ private AccessibilityManager mAccessibilityManager;
- // Animation constants and objects
- private LayoutTransition mLayoutTransition;
- private LayoutTransition mCircleLayoutTransition;
- private View mStartSpace;
- private View mEndSpace;
- private View mBottomSpace;
- private boolean mSpacersUsed;
+ /** {@code true} while the {@link #mLapsList} is transitioning between shown and hidden. */
+ private boolean mLapsListIsTransitioning;
- private AccessibilityManager mAccessibilityManager;
+ /** The data source for {@link #mLapsList}. */
+ private LapsAdapter mLapsAdapter;
- // Used for calculating the time from the start taking into account the pause times
- long mStartTime = 0;
- long mAccumulatedTime = 0;
+ /** Draws the reference lap while the stopwatch is running. */
+ private StopwatchTimer mTime;
- // Lap information
- class Lap {
+ /** Displays the recorded lap times. */
+ private ListView mLapsList;
- Lap (long time, long total) {
- mLapTime = time;
- mTotalTime = total;
- }
- public long mLapTime;
- public long mTotalTime;
+ /** Displays the current stopwatch time. */
+ private CountingTimerView mTimeText;
- public void updateView() {
- View lapInfo = mLapsList.findViewWithTag(this);
- if (lapInfo != null) {
- mLapsAdapter.setTimeText(lapInfo, this);
- }
- }
- }
+ /** Held while the stopwatch is running and this fragment is forward to keep the screen on. */
+ private PowerManager.WakeLock mWakeLock;
- // Adapter for the ListView that shows the lap times.
- class LapsListAdapter extends BaseAdapter {
-
- private static final int VIEW_TYPE_LAP = 0;
- private static final int VIEW_TYPE_SPACE = 1;
- private static final int VIEW_TYPE_COUNT = 2;
-
- ArrayList<Lap> mLaps = new ArrayList<>();
- private final LayoutInflater mInflater;
- private final String[] mFormats;
- private final String[] mLapFormatSet;
- // Size of this array must match the size of formats
- private final long[] mThresholds = {
- 10 * DateUtils.MINUTE_IN_MILLIS, // < 10 minutes
- DateUtils.HOUR_IN_MILLIS, // < 1 hour
- 10 * DateUtils.HOUR_IN_MILLIS, // < 10 hours
- 100 * DateUtils.HOUR_IN_MILLIS, // < 100 hours
- 1000 * DateUtils.HOUR_IN_MILLIS // < 1000 hours
- };
- private int mLapIndex = 0;
- private int mTotalIndex = 0;
- private String mLapFormat;
-
- public LapsListAdapter(Context context) {
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mFormats = context.getResources().getStringArray(R.array.stopwatch_format_set);
- mLapFormatSet = context.getResources().getStringArray(R.array.sw_lap_number_set);
- updateLapFormat();
- }
+ /** The public no-arg constructor required by all fragments. */
+ public StopwatchFragment() {}
- @Override
- public boolean isEnabled(int position) {
- return false;
- }
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
+ mLapsAdapter = new LapsAdapter(getActivity());
- @Override
- public long getItemId(int position) {
- return position;
- }
+ final View v = inflater.inflate(R.layout.stopwatch_fragment, container, false);
+ mTime = (StopwatchTimer) v.findViewById(R.id.stopwatch_time);
+ mLapsList = (ListView) v.findViewById(R.id.laps_list);
+ mLapsList.setDividerHeight(0);
+ mLapsList.setAdapter(mLapsAdapter);
- @Override
- public int getItemViewType(int position) {
- return position < mLaps.size() ? VIEW_TYPE_LAP : VIEW_TYPE_SPACE;
- }
+ // Timer text serves as a virtual start/stop button.
+ mTimeText = (CountingTimerView) v.findViewById(R.id.stopwatch_time_text);
+ mTimeText.setVirtualButtonEnabled(true);
+ mTimeText.registerVirtualButtonAction(new ToggleStopwatchRunnable());
- @Override
- public int getViewTypeCount() {
- return VIEW_TYPE_COUNT;
- }
+ return v;
+ }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (getCount() == 0) {
- return null;
- }
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
- // Handle request for the Spacer at the end
- if (getItemViewType(position) == VIEW_TYPE_SPACE) {
- return convertView != null ? convertView
- : mInflater.inflate(R.layout.stopwatch_spacer, parent, false);
- }
+ mAccessibilityManager =
+ (AccessibilityManager) getActivity().getSystemService(ACCESSIBILITY_SERVICE);
+ }
- final View lapInfo = convertView != null ? convertView
- : mInflater.inflate(R.layout.lap_view, parent, false);
- Lap lap = getItem(position);
- lapInfo.setTag(lap);
+ @Override
+ public void onResume() {
+ super.onResume();
- TextView count = (TextView) lapInfo.findViewById(R.id.lap_number);
- count.setText(String.format(mLapFormat, mLaps.size() - position).toUpperCase());
- setTimeText(lapInfo, lap);
+ // Conservatively assume the data in the adapter has changed while the fragment was paused.
+ mLapsAdapter.notifyDataSetChanged();
- return lapInfo;
- }
+ // Update the state of the buttons.
+ setFabAppearance();
+ setLeftRightButtonAppearance();
- protected void setTimeText(View lapInfo, Lap lap) {
- TextView lapTime = (TextView)lapInfo.findViewById(R.id.lap_time);
- TextView totalTime = (TextView)lapInfo.findViewById(R.id.lap_total);
- lapTime.setText(Stopwatches.formatTimeText(lap.mLapTime, mFormats[mLapIndex]));
- totalTime.setText(Stopwatches.formatTimeText(lap.mTotalTime, mFormats[mTotalIndex]));
- }
+ // Draw the current stopwatch and lap times.
+ updateTime();
- @Override
- public int getCount() {
- // Add 1 for the spacer if list is not empty
- return mLaps.isEmpty() ? 0 : mLaps.size() + 1;
+ // Start updates if the stopwatch is running; blink text if it is paused.
+ switch (getStopwatch().getState()) {
+ case RUNNING:
+ acquireWakeLock();
+ mTime.startAnimation();
+ startUpdatingTime();
+ break;
+ case PAUSED:
+ mTimeText.blinkTimeStr(true);
+ break;
}
- @Override
- public Lap getItem(int position) {
- if (position >= mLaps.size()) {
- return null;
- }
- return mLaps.get(position);
- }
+ // Adjust the visibility of the list of laps.
+ showOrHideLaps(false);
- private void updateLapFormat() {
- // Note Stopwatches.MAX_LAPS < 100
- mLapFormat = mLapFormatSet[mLaps.size() < 10 ? 0 : 1];
- }
+ // Start watching for page changes away from this fragment.
+ getDeskClock().registerPageChangedListener(this);
- private void resetTimeFormats() {
- mLapIndex = mTotalIndex = 0;
+ // View is hidden in onPause, make sure it is visible now.
+ final View view = getView();
+ if (view != null) {
+ view.setVisibility(VISIBLE);
}
+ }
- /**
- * A lap is printed into two columns: the total time and the lap time. To make this print
- * as pretty as possible, multiple formats were created which minimize the width of the
- * print. As the total or lap time exceed the limit of that format, this code updates
- * the format used for the total and/or lap times.
- *
- * @param lap to measure
- * @return true if this lap exceeded either threshold and a format was updated.
- */
- public boolean updateTimeFormats(Lap lap) {
- boolean formatChanged = false;
- while (mLapIndex + 1 < mThresholds.length && lap.mLapTime >= mThresholds[mLapIndex]) {
- mLapIndex++;
- formatChanged = true;
- }
- while (mTotalIndex + 1 < mThresholds.length &&
- lap.mTotalTime >= mThresholds[mTotalIndex]) {
- mTotalIndex++;
- formatChanged = true;
- }
- return formatChanged;
- }
+ @Override
+ public void onPause() {
+ super.onPause();
- public void addLap(Lap l) {
- mLaps.add(0, l);
- // for efficiency caller also calls notifyDataSetChanged()
+ final View view = getView();
+ if (view != null) {
+ // Make the view invisible because when the lock screen is activated, the window stays
+ // active under it. Later, when unlocking the screen, we see the old stopwatch time for
+ // a fraction of a second.
+ getView().setVisibility(INVISIBLE);
}
- public void clearLaps() {
- mLaps.clear();
- updateLapFormat();
- resetTimeFormats();
- notifyDataSetChanged();
- }
+ // Stop all updates while the fragment is not visible.
+ mTime.stopAnimation();
+ stopUpdatingTime();
+ mTimeText.blinkTimeStr(false);
- // Helper function used to get the lap data to be stored in the activity's bundle
- public long [] getLapTimes() {
- int size = mLaps.size();
- if (size == 0) {
- return null;
- }
- long [] laps = new long[size];
- for (int i = 0; i < size; i ++) {
- laps[i] = mLaps.get(i).mTotalTime;
- }
- return laps;
- }
+ // Stop watching for page changes away from this fragment.
+ getDeskClock().unregisterPageChangedListener(this);
- // Helper function to restore adapter's data from the activity's bundle
- public void setLapTimes(long [] laps) {
- if (laps == null || laps.length == 0) {
- return;
- }
+ // Release the wake lock if it is currently held.
+ releaseWakeLock();
+ }
- int size = laps.length;
- mLaps.clear();
- for (long lap : laps) {
- mLaps.add(new Lap(lap, 0));
- }
- long totalTime = 0;
- for (int i = size -1; i >= 0; i --) {
- totalTime += laps[i];
- mLaps.get(i).mTotalTime = totalTime;
- updateTimeFormats(mLaps.get(i));
- }
- updateLapFormat();
- showLaps();
- notifyDataSetChanged();
+ @Override
+ public void onPageChanged(int page) {
+ if (page == DeskClock.STOPWATCH_TAB_INDEX && getStopwatch().isRunning()) {
+ acquireWakeLock();
+ } else {
+ releaseWakeLock();
}
}
- LapsListAdapter mLapsAdapter;
-
- public StopwatchFragment() {
+ @Override
+ public void onFabClick(View view) {
+ toggleStopwatchState();
}
- private void toggleStopwatchState() {
- long time = Utils.getTimeNow();
- Context context = getActivity().getApplicationContext();
- Intent intent = new Intent(context, StopwatchService.class);
- intent.putExtra(Stopwatches.MESSAGE_TIME, time);
- intent.putExtra(Stopwatches.SHOW_NOTIF, false);
- switch (mState) {
- case Stopwatches.STOPWATCH_RUNNING:
- // do stop
- long curTime = Utils.getTimeNow();
- mAccumulatedTime += (curTime - mStartTime);
- doStop();
- Events.sendStopwatchEvent(R.string.action_stop, R.string.label_deskclock);
-
- intent.setAction(HandleDeskClockApiCalls.ACTION_STOP_STOPWATCH);
- context.startService(intent);
- releaseWakeLock();
- break;
- case Stopwatches.STOPWATCH_RESET:
- case Stopwatches.STOPWATCH_STOPPED:
- // do start
- doStart(time);
- Events.sendStopwatchEvent(R.string.action_start, R.string.label_deskclock);
-
- intent.setAction(HandleDeskClockApiCalls.ACTION_START_STOPWATCH);
- context.startService(intent);
- acquireWakeLock();
+ @Override
+ public void onLeftButtonClick(View view) {
+ switch (getStopwatch().getState()) {
+ case RUNNING:
+ doAddLap();
break;
- default:
- LogUtils.wtf("Illegal state " + mState
- + " while pressing the right stopwatch button");
+ case PAUSED:
+ doReset();
break;
}
}
@Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // Inflate the layout for this fragment
- ViewGroup v = (ViewGroup)inflater.inflate(R.layout.stopwatch_fragment, container, false);
-
- mTime = (CircleTimerView)v.findViewById(R.id.stopwatch_time);
- mTimeText = (CountingTimerView)v.findViewById(R.id.stopwatch_time_text);
- mLapsList = (ListView)v.findViewById(R.id.laps_list);
- mLapsList.setDividerHeight(0);
- mLapsAdapter = new LapsListAdapter(getActivity());
- mLapsList.setAdapter(mLapsAdapter);
-
- // Timer text serves as a virtual start/stop button.
- mTimeText.registerVirtualButtonAction(new Runnable() {
- @Override
- public void run() {
- toggleStopwatchState();
- }
- });
- mTimeText.setVirtualButtonEnabled(true);
-
- mCircleLayout = (CircleButtonsLayout)v.findViewById(R.id.stopwatch_circle);
- mCircleLayout.setCircleTimerViewIds(R.id.stopwatch_time, 0 /* stopwatchId */ ,
- 0 /* labelId */);
-
- // Animation setup
- mLayoutTransition = new LayoutTransition();
- mCircleLayoutTransition = new LayoutTransition();
-
- // The CircleButtonsLayout only needs to undertake location changes
- mCircleLayoutTransition.enableTransitionType(LayoutTransition.CHANGING);
- mCircleLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
- mCircleLayoutTransition.disableTransitionType(LayoutTransition.DISAPPEARING);
- mCircleLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
- mCircleLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
- mCircleLayoutTransition.setAnimateParentHierarchy(false);
-
- // These spacers assist in keeping the size of CircleButtonsLayout constant
- mStartSpace = v.findViewById(R.id.start_space);
- mEndSpace = v.findViewById(R.id.end_space);
- mSpacersUsed = mStartSpace != null || mEndSpace != null;
-
- // Only applicable on portrait, only visible when there is no lap
- mBottomSpace = v.findViewById(R.id.bottom_space);
-
- // Listener to invoke extra animation within the laps-list
- mLayoutTransition.addTransitionListener(new LayoutTransition.TransitionListener() {
- @Override
- public void startTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {
- if (view == mLapsList) {
- if (transitionType == LayoutTransition.DISAPPEARING) {
- if (DEBUG) LogUtils.v("StopwatchFragment.start laps-list disappearing");
- boolean shiftX = view.getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE;
- int first = mLapsList.getFirstVisiblePosition();
- int last = mLapsList.getLastVisiblePosition();
- // Ensure index range will not cause a divide by zero
- if (last < first) {
- last = first;
- }
- long duration = transition.getDuration(LayoutTransition.DISAPPEARING);
- long offset = duration / (last - first + 1) / 5;
- for (int visibleIndex = first; visibleIndex <= last; visibleIndex++) {
- View lapView = mLapsList.getChildAt(visibleIndex - first);
- if (lapView != null) {
- float toXValue = shiftX ? 1.0f * (visibleIndex - first + 1) : 0;
- float toYValue = shiftX ? 0 : 4.0f * (visibleIndex - first + 1);
- TranslateAnimation animation = new TranslateAnimation(
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, toXValue,
- Animation.RELATIVE_TO_SELF, 0,
- Animation.RELATIVE_TO_SELF, toYValue);
- animation.setStartOffset((last - visibleIndex) * offset);
- animation.setDuration(duration);
- lapView.startAnimation(animation);
- }
- }
- }
- }
- }
+ public void onRightButtonClick(View view) {
+ doShare();
+ }
- @Override
- public void endTransition(LayoutTransition transition, ViewGroup container,
- View view, int transitionType) {
- if (transitionType == LayoutTransition.DISAPPEARING) {
- if (DEBUG) LogUtils.v("StopwatchFragment.end laps-list disappearing");
- int last = mLapsList.getLastVisiblePosition();
- for (int visibleIndex = mLapsList.getFirstVisiblePosition();
- visibleIndex <= last; visibleIndex++) {
- View lapView = mLapsList.getChildAt(visibleIndex);
- if (lapView != null) {
- Animation animation = lapView.getAnimation();
- if (animation != null) {
- animation.cancel();
- }
- }
- }
- }
- }
- });
+ @Override
+ public void setFabAppearance() {
+ if (mFab == null || getSelectedTab() != DeskClock.STOPWATCH_TAB_INDEX) {
+ return;
+ }
- return v;
+ if (getStopwatch().isRunning()) {
+ mFab.setImageResource(R.drawable.ic_pause_white_24dp);
+ mFab.setContentDescription(getString(R.string.sw_pause_button));
+ } else {
+ mFab.setImageResource(R.drawable.ic_start_white_24dp);
+ mFab.setContentDescription(getString(R.string.sw_start_button));
+ }
+ mFab.setVisibility(VISIBLE);
}
- /**
- * Make the final display setup.
- *
- * If the fragment is starting with an existing list of laps, shows the laps list and if the
- * spacers around the clock exist, hide them. If there are not laps at the start, hide the laps
- * list and show the clock spacers if they exist.
- */
@Override
- public void onStart() {
- super.onStart();
+ public void setLeftRightButtonAppearance() {
+ if (mLeftButton == null || mRightButton == null ||
+ getSelectedTab() != DeskClock.STOPWATCH_TAB_INDEX) {
+ return;
+ }
- boolean lapsVisible = mLapsAdapter.getCount() > 0;
+ mRightButton.setImageResource(R.drawable.ic_share);
+ mRightButton.setContentDescription(getString(R.string.sw_share_button));
- mLapsList.setVisibility(lapsVisible ? View.VISIBLE : View.GONE);
- if (mSpacersUsed) {
- showSpacerVisibility(lapsVisible);
+ switch (getStopwatch().getState()) {
+ case RESET:
+ mLeftButton.setEnabled(false);
+ mLeftButton.setVisibility(INVISIBLE);
+ mRightButton.setVisibility(INVISIBLE);
+ break;
+ case RUNNING:
+ mLeftButton.setImageResource(R.drawable.ic_lap);
+ mLeftButton.setContentDescription(getString(R.string.sw_lap_button));
+ mLeftButton.setEnabled(canRecordMoreLaps());
+ mLeftButton.setVisibility(canRecordMoreLaps() ? VISIBLE : INVISIBLE);
+ mRightButton.setVisibility(INVISIBLE);
+ break;
+ case PAUSED:
+ mLeftButton.setEnabled(true);
+ mLeftButton.setImageResource(R.drawable.ic_reset);
+ mLeftButton.setContentDescription(getString(R.string.sw_reset_button));
+ mLeftButton.setVisibility(VISIBLE);
+ mRightButton.setVisibility(VISIBLE);
+ break;
}
- showBottomSpacerVisibility(lapsVisible);
+ }
- ((ViewGroup)getView()).setLayoutTransition(mLayoutTransition);
- mCircleLayout.setLayoutTransition(mCircleLayoutTransition);
+ /**
+ * Start the stopwatch.
+ */
+ private void doStart() {
+ Events.sendStopwatchEvent(R.string.action_start, R.string.label_deskclock);
- mAccessibilityManager = (AccessibilityManager) getActivity().getSystemService(
- Context.ACCESSIBILITY_SERVICE);
- }
+ // Update the stopwatch state.
+ DataModel.getDataModel().startStopwatch();
- @Override
- public void onResume() {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
- prefs.registerOnSharedPreferenceChangeListener(this);
- readFromSharedPref(prefs);
- mTime.readFromSharedPref(prefs, "sw");
- mTime.postInvalidate();
+ // Start UI updates.
+ startUpdatingTime();
+ mTime.startAnimation();
+ mTimeText.blinkTimeStr(false);
+ // Update button states.
setFabAppearance();
setLeftRightButtonAppearance();
- mTimeText.setTime(mAccumulatedTime, true, true);
- if (mState == Stopwatches.STOPWATCH_RUNNING) {
- acquireWakeLock();
- startUpdateThread();
- } else if (mState == Stopwatches.STOPWATCH_STOPPED && mAccumulatedTime != 0) {
- mTimeText.blinkTimeStr(true);
- }
- showLaps();
- ((DeskClock)getActivity()).registerPageChangedListener(this);
- // View was hidden in onPause, make sure it is visible now.
- View v = getView();
- if (v != null) {
- v.setVisibility(View.VISIBLE);
- }
- super.onResume();
+
+ // Acquire the wake lock.
+ acquireWakeLock();
}
- @Override
- public void onPause() {
- if (mState == Stopwatches.STOPWATCH_RUNNING) {
- stopUpdateThread();
+ /**
+ * Pause the stopwatch.
+ */
+ private void doPause() {
+ Events.sendStopwatchEvent(R.string.action_pause, R.string.label_deskclock);
- // This is called because the lock screen was activated, the window stay
- // active under it and when we unlock the screen, we see the old time for
- // a fraction of a second.
- View v = getView();
- if (v != null) {
- v.setVisibility(View.INVISIBLE);
- }
- }
- // The stopwatch must keep running even if the user closes the app so save stopwatch state
- // in shared prefs
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
- prefs.unregisterOnSharedPreferenceChangeListener(this);
- writeToSharedPref(prefs);
- mTime.writeToSharedPref(prefs, "sw");
- mTimeText.blinkTimeStr(false);
- ((DeskClock)getActivity()).unregisterPageChangedListener(this);
- releaseWakeLock();
- super.onPause();
- }
+ // Update the stopwatch state
+ DataModel.getDataModel().pauseStopwatch();
- @Override
- public void onPageChanged(int page) {
- if (page == DeskClock.STOPWATCH_TAB_INDEX && mState == Stopwatches.STOPWATCH_RUNNING) {
- acquireWakeLock();
- } else {
- releaseWakeLock();
- }
- }
+ // Redraw the paused stopwatch time.
+ updateTime();
- private void doStop() {
- if (DEBUG) LogUtils.v("StopwatchFragment.doStop");
- stopUpdateThread();
- mTime.pauseIntervalAnimation();
- mTimeText.setTime(mAccumulatedTime, true, true);
+ // Stop UI updates.
+ stopUpdatingTime();
+ mTime.stopAnimation();
mTimeText.blinkTimeStr(true);
- updateCurrentLap(mAccumulatedTime);
- mState = Stopwatches.STOPWATCH_STOPPED;
- setFabAppearance();
- setLeftRightButtonAppearance();
- }
- private void doStart(long time) {
- if (DEBUG) LogUtils.v("StopwatchFragment.doStart");
- mStartTime = time;
- startUpdateThread();
- mTimeText.blinkTimeStr(false);
- if (mTime.isAnimating()) {
- mTime.startIntervalAnimation();
- }
- mState = Stopwatches.STOPWATCH_RUNNING;
+ // Update button states.
setFabAppearance();
setLeftRightButtonAppearance();
- }
- private void doLap() {
- if (DEBUG) LogUtils.v("StopwatchFragment.doLap");
- showLaps();
- setFabAppearance();
- setLeftRightButtonAppearance();
+ // Release the wake lock.
+ releaseWakeLock();
}
+ /**
+ * Reset the stopwatch.
+ */
private void doReset() {
- if (DEBUG) LogUtils.v("StopwatchFragment.doReset");
- SharedPreferences prefs =
- PreferenceManager.getDefaultSharedPreferences(getActivity());
- Utils.clearSwSharedPref(prefs);
- mTime.clearSharedPref(prefs, "sw");
- mAccumulatedTime = 0;
- mLapsAdapter.clearLaps();
- showLaps();
- mTime.stopIntervalAnimation();
- mTime.reset();
- mTimeText.setTime(mAccumulatedTime, true, true);
+ Events.sendStopwatchEvent(R.string.action_reset, R.string.label_deskclock);
+
+ // Update the stopwatch state.
+ DataModel.getDataModel().resetStopwatch();
+
+ // Clear the laps.
+ showOrHideLaps(true);
+
+ // Clear the times.
+ mTime.postInvalidateOnAnimation();
+ mTimeText.setTime(0, true, true);
mTimeText.blinkTimeStr(false);
- mState = Stopwatches.STOPWATCH_RESET;
+
+ // Update button states.
setFabAppearance();
setLeftRightButtonAppearance();
+
+ // Release the wake lock.
+ releaseWakeLock();
}
- private void shareResults() {
+ /**
+ * Send stopwatch time and lap times to an external sharing application.
+ */
+ private void doShare() {
+ final String[] subjects = getResources().getStringArray(R.array.sw_share_strings);
+ final String subject = subjects[(int)(Math.random() * subjects.length)];
+ final String text = mLapsAdapter.getShareText();
+
+ final Intent shareIntent = new Intent(Intent.ACTION_SEND)
+ .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
+ .putExtra(Intent.EXTRA_SUBJECT, subject)
+ .putExtra(Intent.EXTRA_TEXT, text)
+ .setType("text/plain");
+
final Context context = getActivity();
- final Intent shareIntent = new Intent(android.content.Intent.ACTION_SEND);
- shareIntent.setType("text/plain");
- shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
- shareIntent.putExtra(Intent.EXTRA_SUBJECT,
- Stopwatches.getShareTitle(context.getApplicationContext()));
- shareIntent.putExtra(Intent.EXTRA_TEXT, Stopwatches.buildShareResults(
- getActivity().getApplicationContext(), mTimeText.getTimeString(),
- getLapShareTimes(mLapsAdapter.getLapTimes())));
-
- final Intent launchIntent = Intent.createChooser(shareIntent,
- context.getString(R.string.sw_share_button));
+ final String title = context.getString(R.string.sw_share_button);
+ final Intent shareChooserIntent = Intent.createChooser(shareIntent, title);
try {
- context.startActivity(launchIntent);
- } catch (ActivityNotFoundException e) {
+ context.startActivity(shareChooserIntent);
+ } catch (ActivityNotFoundException anfe) {
LogUtils.e("No compatible receiver is found");
}
}
- /** Turn laps as they would be saved in prefs into format for sharing. **/
- private long[] getLapShareTimes(long[] input) {
- if (input == null) {
- return null;
- }
+ /**
+ * Record and add a new lap ending now.
+ */
+ private void doAddLap() {
+ Events.sendStopwatchEvent(R.string.action_lap, R.string.label_deskclock);
- int numLaps = input.length;
- long[] output = new long[numLaps];
- long prevLapElapsedTime = 0;
- for (int lap_i = numLaps - 1; lap_i >= 0; lap_i--) {
- long lap = input[lap_i];
- LogUtils.v("lap " + lap_i + ": " + lap);
- output[lap_i] = lap - prevLapElapsedTime;
- prevLapElapsedTime = lap;
+ // Record a new lap.
+ final Lap lap = mLapsAdapter.addLap();
+ if (lap == null) {
+ return;
}
- return output;
- }
- private boolean reachedMaxLaps() {
- return mLapsAdapter.getCount() >= Stopwatches.MAX_LAPS;
- }
+ // Update button states.
+ setLeftRightButtonAppearance();
- /***
- * Handle action when user presses the lap button
- * @param time - in hundredth of a second
- */
- private void addLapTime(long time) {
- // The total elapsed time
- final long curTime = time - mStartTime + mAccumulatedTime;
- int size = mLapsAdapter.getCount();
- if (size == 0) {
- // Create and add the first lap
- Lap firstLap = new Lap(curTime, curTime);
- mLapsAdapter.addLap(firstLap);
- // Create the first active lap
- mLapsAdapter.addLap(new Lap(0, curTime));
- // Update the interval on the clock and check the lap and total time formatting
- mTime.setIntervalTime(curTime);
- mLapsAdapter.updateTimeFormats(firstLap);
- } else {
- // Finish active lap
- final long lapTime = curTime - mLapsAdapter.getItem(1).mTotalTime;
- mLapsAdapter.getItem(0).mLapTime = lapTime;
- mLapsAdapter.getItem(0).mTotalTime = curTime;
- // Create a new active lap
- mLapsAdapter.addLap(new Lap(0, curTime));
- // Update marker on clock and check that formatting for the lap number
- mTime.setMarkerTime(lapTime);
- mLapsAdapter.updateLapFormat();
- }
- // Repaint the laps list
- mLapsAdapter.notifyDataSetChanged();
+ if (lap.getLapNumber() == 1) {
+ // Child views from prior lap sets hang around and blit to the screen when adding the
+ // first lap of the subsequent lap set. Remove those superfluous children here manually
+ // to ensure they aren't seen as the first lap is drawn.
+ mLapsList.removeAllViewsInLayout();
- // Start lap animation starting from the second lap
- mTime.stopIntervalAnimation();
- if (!reachedMaxLaps()) {
- mTime.startIntervalAnimation();
- }
- }
+ // Start animating the reference lap.
+ mTime.startAnimation();
+
+ // Recording the first lap transitions the UI to display the laps list.
+ showOrHideLaps(false);
- private void updateCurrentLap(long totalTime) {
- // There are either 0, 2 or more Laps in the list See {@link #addLapTime}
- if (mLapsAdapter.getCount() > 0) {
- Lap curLap = mLapsAdapter.getItem(0);
- curLap.mLapTime = totalTime - mLapsAdapter.getItem(1).mTotalTime;
- curLap.mTotalTime = totalTime;
- // If this lap has caused a change in the format for total and/or lap time, all of
- // the rows need a fresh print. The simplest way to refresh all of the rows is
- // calling notifyDataSetChanged.
- if (mLapsAdapter.updateTimeFormats(curLap)) {
- mLapsAdapter.notifyDataSetChanged();
+ } else {
+ if (mLapsList.getFirstVisiblePosition() > 0) {
+ // Ensure the newly added lap is visible on screen.
+ mLapsList.smoothScrollToPosition(0);
} else {
- curLap.updateView();
+ // Avoid nasty bugs in the Transition framework prior to L MR1.
+ // Without the transition, the new lap just appears on screen instantaneously.
+ if (Utils.isLMR1OrLater()) {
+ // Ignore updates to the current lap while adding the new recorded lap.
+ final Transition transition = new AutoTransition();
+ transition.excludeChildren(R.id.lap_view, true);
+
+ final ViewGroup sceneRoot = (ViewGroup) getView();
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
+ }
}
}
}
/**
- * Show or hide the laps-list
+ * Show or hide the list of laps.
*/
- private void showLaps() {
- if (DEBUG) LogUtils.v(String.format("StopwatchFragment.showLaps: count=%d",
- mLapsAdapter.getCount()));
+ private void showOrHideLaps(boolean clearLaps) {
+ final Transition transition = new AutoTransition()
+ .addListener(new Transition.TransitionListener() {
+ @Override
+ public void onTransitionStart(Transition transition) {
+ mLapsListIsTransitioning = true;
+ }
- boolean lapsVisible = mLapsAdapter.getCount() > 0;
+ @Override
+ public void onTransitionEnd(Transition transition) {
+ mLapsListIsTransitioning = false;
+ }
- // Layout change animations will start upon the first add/hide view. Temporarily disable
- // the layout transition animation for the spacers, make the changes, then re-enable
- // the animation for the add/hide laps-list
- if (mSpacersUsed) {
- ViewGroup rootView = (ViewGroup) getView();
- if (rootView != null) {
- rootView.setLayoutTransition(null);
+ @Override
+ public void onTransitionCancel(Transition transition) {
+ }
- showSpacerVisibility(lapsVisible);
+ @Override
+ public void onTransitionPause(Transition transition) {
+ }
- rootView.setLayoutTransition(mLayoutTransition);
- }
- }
+ @Override
+ public void onTransitionResume(Transition transition) {
+ }
+ });
- showBottomSpacerVisibility(lapsVisible);
+ final ViewGroup sceneRoot = (ViewGroup) getView();
+ TransitionManager.beginDelayedTransition(sceneRoot, transition);
- if (lapsVisible) {
- // There are laps - show the laps-list
- // No delay for the CircleButtonsLayout changes - start immediately so that the
- // circle has shifted before the laps-list starts appearing.
- mCircleLayoutTransition.setStartDelay(LayoutTransition.CHANGING, 0);
+ if (clearLaps) {
+ mLapsAdapter.clearLaps();
+ }
- mLapsList.setVisibility(View.VISIBLE);
- } else {
- // There are no laps - hide the laps list
+ final boolean lapsVisible = mLapsAdapter.getCount() > 0;
+ mLapsList.setVisibility(lapsVisible ? VISIBLE : GONE);
+ }
- // Delay the CircleButtonsLayout animation until after the laps-list disappears
- long startDelay = mLayoutTransition.getStartDelay(LayoutTransition.DISAPPEARING) +
- mLayoutTransition.getDuration(LayoutTransition.DISAPPEARING);
- mCircleLayoutTransition.setStartDelay(LayoutTransition.CHANGING, startDelay);
- mLapsList.setVisibility(View.GONE);
+ private void acquireWakeLock() {
+ if (mWakeLock == null) {
+ final PowerManager pm = (PowerManager) getActivity().getSystemService(POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(SCREEN_BRIGHT_WAKE_LOCK | ON_AFTER_RELEASE, TAG);
+ mWakeLock.setReferenceCounted(false);
}
+ mWakeLock.acquire();
}
- private void showSpacerVisibility(boolean lapsVisible) {
- final int spacersVisibility = lapsVisible ? View.GONE : View.VISIBLE;
- if (mStartSpace != null) {
- mStartSpace.setVisibility(spacersVisibility);
- }
- if (mEndSpace != null) {
- mEndSpace.setVisibility(spacersVisibility);
+ private void releaseWakeLock() {
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release();
}
}
- private void showBottomSpacerVisibility(boolean lapsVisible) {
- if (mBottomSpace != null) {
- mBottomSpace.setVisibility(lapsVisible ? View.GONE : View.VISIBLE);
+ /**
+ * Either pause or start the stopwatch based on its current state.
+ */
+ private void toggleStopwatchState() {
+ if (getStopwatch().isRunning()) {
+ doPause();
+ } else {
+ doStart();
}
}
- private void startUpdateThread() {
- mTime.post(mTimeUpdateThread);
+ private Stopwatch getStopwatch() {
+ return DataModel.getDataModel().getStopwatch();
}
- private void stopUpdateThread() {
- mTime.removeCallbacks(mTimeUpdateThread);
+ private boolean canRecordMoreLaps() {
+ return DataModel.getDataModel().canAddMoreLaps();
}
- Runnable mTimeUpdateThread = new Runnable() {
- @Override
- public void run() {
- long curTime = Utils.getTimeNow();
- long totalTime = mAccumulatedTime + (curTime - mStartTime);
- if (mTime != null) {
- mTimeText.setTime(totalTime, true, true);
- }
- updateCurrentLap(totalTime);
- mTime.postDelayed(mTimeUpdateThread, mAccessibilityManager != null &&
- mAccessibilityManager.isTouchExplorationEnabled()
- ? STOPWATCH_ACCESSIBILTY_REFRESH_INTERVAL_MILLIS
- : STOPWATCH_REFRESH_INTERVAL_MILLIS);
- }
- };
-
- private void writeToSharedPref(SharedPreferences prefs) {
- SharedPreferences.Editor editor = prefs.edit();
- editor.putLong (Stopwatches.PREF_START_TIME, mStartTime);
- editor.putLong (Stopwatches.PREF_ACCUM_TIME, mAccumulatedTime);
- editor.putInt (Stopwatches.PREF_STATE, mState);
- if (mLapsAdapter != null) {
- long [] laps = mLapsAdapter.getLapTimes();
- if (laps != null) {
- editor.putInt (Stopwatches.PREF_LAP_NUM, laps.length);
- for (int i = 0; i < laps.length; i++) {
- String key = Stopwatches.PREF_LAP_TIME + Integer.toString(laps.length - i);
- editor.putLong (key, laps[i]);
- }
- }
- }
- if (mState == Stopwatches.STOPWATCH_RUNNING) {
- editor.putLong(Stopwatches.NOTIF_CLOCK_BASE, mStartTime-mAccumulatedTime);
- editor.putLong(Stopwatches.NOTIF_CLOCK_ELAPSED, -1);
- editor.putBoolean(Stopwatches.NOTIF_CLOCK_RUNNING, true);
- } else if (mState == Stopwatches.STOPWATCH_STOPPED) {
- editor.putLong(Stopwatches.NOTIF_CLOCK_ELAPSED, mAccumulatedTime);
- editor.putLong(Stopwatches.NOTIF_CLOCK_BASE, -1);
- editor.putBoolean(Stopwatches.NOTIF_CLOCK_RUNNING, false);
- } else if (mState == Stopwatches.STOPWATCH_RESET) {
- editor.remove(Stopwatches.NOTIF_CLOCK_BASE);
- editor.remove(Stopwatches.NOTIF_CLOCK_RUNNING);
- editor.remove(Stopwatches.NOTIF_CLOCK_ELAPSED);
- }
- editor.putBoolean(Stopwatches.PREF_UPDATE_CIRCLE, false);
- editor.apply();
+ /**
+ * Post the first runnable to update times within the UI. It will reschedule itself as needed.
+ */
+ private void startUpdatingTime() {
+ mTime.post(mTimeUpdateRunnable);
}
- private void readFromSharedPref(SharedPreferences prefs) {
- mStartTime = prefs.getLong(Stopwatches.PREF_START_TIME, 0);
- mAccumulatedTime = prefs.getLong(Stopwatches.PREF_ACCUM_TIME, 0);
- mState = prefs.getInt(Stopwatches.PREF_STATE, Stopwatches.STOPWATCH_RESET);
- int numLaps = prefs.getInt(Stopwatches.PREF_LAP_NUM, Stopwatches.STOPWATCH_RESET);
- if (mLapsAdapter != null) {
- long[] oldLaps = mLapsAdapter.getLapTimes();
- if (oldLaps == null || oldLaps.length < numLaps) {
- long[] laps = new long[numLaps];
- long prevLapElapsedTime = 0;
- for (int lap_i = 0; lap_i < numLaps; lap_i++) {
- String key = Stopwatches.PREF_LAP_TIME + Integer.toString(lap_i + 1);
- long lap = prefs.getLong(key, 0);
- laps[numLaps - lap_i - 1] = lap - prevLapElapsedTime;
- prevLapElapsedTime = lap;
- }
- mLapsAdapter.setLapTimes(laps);
- }
- }
- if (prefs.getBoolean(Stopwatches.PREF_UPDATE_CIRCLE, true)) {
- if (mState == Stopwatches.STOPWATCH_STOPPED) {
- doStop();
- } else if (mState == Stopwatches.STOPWATCH_RUNNING) {
- doStart(mStartTime);
- } else if (mState == Stopwatches.STOPWATCH_RESET) {
- doReset();
- }
- }
+ /**
+ * Remove the runnable that updates times within the UI.
+ */
+ private void stopUpdatingTime() {
+ mTime.removeCallbacks(mTimeUpdateRunnable);
}
- @Override
- public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- if (prefs.equals(PreferenceManager.getDefaultSharedPreferences(getActivity()))) {
- if (! (key.equals(Stopwatches.PREF_LAP_NUM) ||
- key.startsWith(Stopwatches.PREF_LAP_TIME))) {
- readFromSharedPref(prefs);
- if (prefs.getBoolean(Stopwatches.PREF_UPDATE_CIRCLE, true)) {
- mTime.readFromSharedPref(prefs, "sw");
- }
+ /**
+ * Update all time displays based on a single snapshot of the stopwatch progress. This includes
+ * the stopwatch time drawn in the circle, the current lap time and the total elapsed time in
+ * the list of laps.
+ */
+ private void updateTime() {
+ // Compute the total time of the stopwatch.
+ final long totalTime = getStopwatch().getTotalTime();
+
+ // Update the total time display.
+ mTimeText.setTime(totalTime, true, true);
+
+ // Update the current lap if one exists and is visible on the screen.
+ final boolean lapsExist = mLapsAdapter.getCount() > 0;
+ final boolean currentLapIsVisible = mLapsList.getFirstVisiblePosition() == 0;
+ if (!mLapsListIsTransitioning && lapsExist && currentLapIsVisible) {
+ final View currentLapView = mLapsList.getChildAt(0);
+ if (currentLapView != null) {
+ // Compute the lap time using the total time.
+ final long lapTime = DataModel.getDataModel().getCurrentLapTime(totalTime);
+
+ // Update the current lap.
+ mLapsAdapter.updateCurrentLap(currentLapView, lapTime, totalTime);
}
}
}
- // Used to keeps screen on when stopwatch is running.
-
- private void acquireWakeLock() {
- if (mWakeLock == null) {
- final PowerManager pm =
- (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);
- mWakeLock = pm.newWakeLock(
- PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG);
- mWakeLock.setReferenceCounted(false);
- }
- mWakeLock.acquire();
- }
-
- private void releaseWakeLock() {
- if (mWakeLock != null && mWakeLock.isHeld()) {
- mWakeLock.release();
- }
- }
+ /**
+ * This runnable periodically updates times throughout the UI. It stops these updates when the
+ * stopwatch is no longer running.
+ */
+ private final class TimeUpdateRunnable implements Runnable {
+ @Override
+ public void run() {
+ final long startTime = SystemClock.elapsedRealtime();
- @Override
- public void onFabClick(View view){
- toggleStopwatchState();
- }
+ updateTime();
- @Override
- public void onLeftButtonClick(View view) {
- final long time = Utils.getTimeNow();
- final Context context = getActivity().getApplicationContext();
- final Intent intent = new Intent(context, StopwatchService.class);
- intent.putExtra(Stopwatches.MESSAGE_TIME, time);
- intent.putExtra(Stopwatches.SHOW_NOTIF, false);
- switch (mState) {
- case Stopwatches.STOPWATCH_RUNNING:
- // Save lap time
- addLapTime(time);
- doLap();
- Events.sendStopwatchEvent(R.string.action_lap, R.string.label_deskclock);
-
- intent.setAction(HandleDeskClockApiCalls.ACTION_LAP_STOPWATCH);
- context.startService(intent);
- break;
- case Stopwatches.STOPWATCH_STOPPED:
- // do reset
- doReset();
- Events.sendStopwatchEvent(R.string.action_reset, R.string.label_deskclock);
+ if (getStopwatch().isRunning()) {
+ // The stopwatch is still running so execute this runnable again after a delay.
+ final boolean talkBackOn = mAccessibilityManager.isTouchExplorationEnabled();
- intent.setAction(HandleDeskClockApiCalls.ACTION_RESET_STOPWATCH);
- context.startService(intent);
- releaseWakeLock();
- break;
- default:
- // Happens in monkey tests
- LogUtils.i("Illegal state " + mState + " while pressing the left stopwatch button");
- break;
- }
- }
+ // Grant longer time between redraws when talk-back is on to let it catch up.
+ final int period = talkBackOn ? 500 : 25;
- @Override
- public void onRightButtonClick(View view) {
- shareResults();
- }
+ // Try to maintain a consistent period of time between redraws.
+ final long endTime = SystemClock.elapsedRealtime();
+ final long delay = Math.max(0, startTime + period - endTime);
- @Override
- public void setFabAppearance() {
- final DeskClock activity = (DeskClock) getActivity();
- if (mFab == null || activity.getSelectedTab() != DeskClock.STOPWATCH_TAB_INDEX) {
- return;
- }
- if (mState == Stopwatches.STOPWATCH_RUNNING) {
- mFab.setImageResource(R.drawable.ic_pause_white_24dp);
- mFab.setContentDescription(getString(R.string.sw_stop_button));
- } else {
- mFab.setImageResource(R.drawable.ic_start_white_24dp);
- mFab.setContentDescription(getString(R.string.sw_start_button));
+ mTime.postDelayed(this, delay);
+ }
}
- mFab.setVisibility(View.VISIBLE);
}
- @Override
- public void setLeftRightButtonAppearance() {
- final DeskClock activity = (DeskClock) getActivity();
- if (mLeftButton == null || mRightButton == null ||
- activity.getSelectedTab() != DeskClock.STOPWATCH_TAB_INDEX) {
- return;
- }
- mRightButton.setImageResource(R.drawable.ic_share);
- mRightButton.setContentDescription(getString(R.string.sw_share_button));
-
- switch (mState) {
- case Stopwatches.STOPWATCH_RESET:
- mLeftButton.setImageResource(R.drawable.ic_lap);
- mLeftButton.setContentDescription(getString(R.string.sw_lap_button));
- mLeftButton.setEnabled(false);
- mLeftButton.setVisibility(View.INVISIBLE);
- mRightButton.setVisibility(View.INVISIBLE);
- break;
- case Stopwatches.STOPWATCH_RUNNING:
- mLeftButton.setImageResource(R.drawable.ic_lap);
- mLeftButton.setContentDescription(getString(R.string.sw_lap_button));
- mLeftButton.setEnabled(!reachedMaxLaps());
- mLeftButton.setVisibility(View.VISIBLE);
- mRightButton.setVisibility(View.INVISIBLE);
- break;
- case Stopwatches.STOPWATCH_STOPPED:
- mLeftButton.setImageResource(R.drawable.ic_reset);
- mLeftButton.setContentDescription(getString(R.string.sw_reset_button));
- mLeftButton.setEnabled(true);
- mLeftButton.setVisibility(View.VISIBLE);
- mRightButton.setVisibility(View.VISIBLE);
- break;
+ /**
+ * Tapping the stopwatch text also toggles the stopwatch state, just like the fab.
+ */
+ private final class ToggleStopwatchRunnable implements Runnable {
+ @Override
+ public void run() {
+ toggleStopwatchState();
}
}
-}
+} \ No newline at end of file