Home | History | Annotate | Download | only in stopwatch
      1 package com.android.deskclock.stopwatch;
      2 
      3 import android.app.Activity;
      4 import android.content.Context;
      5 import android.content.Intent;
      6 import android.content.SharedPreferences;
      7 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
      8 import android.content.pm.PackageManager;
      9 import android.content.pm.ResolveInfo;
     10 import android.graphics.drawable.Drawable;
     11 import android.os.Bundle;
     12 import android.preference.PreferenceManager;
     13 import android.view.LayoutInflater;
     14 import android.view.View;
     15 import android.view.ViewGroup;
     16 import android.widget.AdapterView;
     17 import android.widget.AdapterView.OnItemClickListener;
     18 import android.widget.ArrayAdapter;
     19 import android.widget.BaseAdapter;
     20 import android.widget.Button;
     21 import android.widget.ImageButton;
     22 import android.widget.ImageView;
     23 import android.widget.ListPopupWindow;
     24 import android.widget.ListView;
     25 import android.widget.PopupWindow.OnDismissListener;
     26 import android.widget.TextView;
     27 
     28 import com.android.deskclock.CircleButtonsLinearLayout;
     29 import com.android.deskclock.CircleTimerView;
     30 import com.android.deskclock.DeskClock;
     31 import com.android.deskclock.DeskClock.OnTapListener;
     32 import com.android.deskclock.DeskClockFragment;
     33 import com.android.deskclock.Log;
     34 import com.android.deskclock.R;
     35 import com.android.deskclock.Utils;
     36 import com.android.deskclock.timer.CountingTimerView;
     37 
     38 import java.util.ArrayList;
     39 import java.util.List;
     40 
     41 /**
     42  * TODO: Insert description here. (generated by isaackatz)
     43  */
     44 public class StopwatchFragment extends DeskClockFragment implements OnSharedPreferenceChangeListener{
     45     int mState = Stopwatches.STOPWATCH_RESET;
     46 
     47     // Stopwatch views that are accessed by the activity
     48     private ImageButton mLeftButton;
     49     private TextView mCenterButton;
     50     private CircleTimerView mTime;
     51     private CountingTimerView mTimeText;
     52     private ListView mLapsList;
     53     private ImageButton mShareButton;
     54     private ListPopupWindow mSharePopup;
     55 
     56     // Used for calculating the time from the start taking into account the pause times
     57     long mStartTime = 0;
     58     long mAccumulatedTime = 0;
     59 
     60     // Lap information
     61     class Lap {
     62         Lap () {
     63             mLapTime = 0;
     64             mTotalTime = 0;
     65         }
     66 
     67         Lap (long time, long total) {
     68             mLapTime = time;
     69             mTotalTime = total;
     70         }
     71         public long mLapTime;
     72         public long mTotalTime;
     73     }
     74 
     75     // Adapter for the ListView that shows the lap times.
     76     class LapsListAdapter extends BaseAdapter {
     77 
     78         ArrayList<Lap> mLaps = new ArrayList<Lap>();
     79         private final LayoutInflater mInflater;
     80         private final int mBackgroundColor;
     81 
     82         public LapsListAdapter(Context context) {
     83             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     84             mBackgroundColor = getResources().getColor(R.color.blackish);
     85         }
     86 
     87         @Override
     88         public long getItemId(int position) {
     89             return position;
     90         }
     91 
     92         @Override
     93         public View getView(int position, View convertView, ViewGroup parent) {
     94             if (mLaps.size() == 0 || position >= mLaps.size()) {
     95                 return null;
     96             }
     97             View lapInfo;
     98             if (convertView != null) {
     99                 lapInfo = convertView;
    100             } else {
    101                 lapInfo =  mInflater.inflate(R.layout.lap_view, parent, false);
    102             }
    103             TextView count = (TextView)lapInfo.findViewById(R.id.lap_number);
    104             TextView lapTime = (TextView)lapInfo.findViewById(R.id.lap_time);
    105             TextView toalTime = (TextView)lapInfo.findViewById(R.id.lap_total);
    106             lapTime.setText(Stopwatches.getTimeText(mLaps.get(position).mLapTime));
    107             toalTime.setText(Stopwatches.getTimeText(mLaps.get(position).mTotalTime));
    108             count.setText(getString(R.string.sw_notification_lap_number, mLaps.size() - position)
    109                     .toUpperCase());
    110 
    111             lapInfo.setBackgroundColor(mBackgroundColor);
    112             return lapInfo;
    113         }
    114 
    115         @Override
    116         public int getCount() {
    117             return mLaps.size();
    118         }
    119 
    120         @Override
    121         public Object getItem(int position) {
    122             if (mLaps.size() == 0 || position >= mLaps.size()) {
    123                 return null;
    124             }
    125             return mLaps.get(position);
    126         }
    127 
    128         public void addLap(Lap l) {
    129             mLaps.add(0, l);
    130             notifyDataSetChanged();
    131         }
    132 
    133         public void clearLaps() {
    134             mLaps.clear();
    135             notifyDataSetChanged();
    136         }
    137 
    138         // Helper function used to get the lap data to be stored in the activitys's bundle
    139         public long [] getLapTimes() {
    140             int size = mLaps.size();
    141             if (size == 0) {
    142                 return null;
    143             }
    144             long [] laps = new long[size];
    145             for (int i = 0; i < size; i ++) {
    146                 laps[i] = mLaps.get(i).mTotalTime;
    147             }
    148             return laps;
    149         }
    150 
    151         // Helper function to restore adapter's data from the activity's bundle
    152         public void setLapTimes(long [] laps) {
    153             if (laps == null || laps.length == 0) {
    154                 return;
    155             }
    156 
    157             int size = laps.length;
    158             mLaps.clear();
    159             for (int i = 0; i < size; i ++) {
    160                 mLaps.add(new Lap (laps[i], 0));
    161             }
    162             long totalTime = 0;
    163             for (int i = size -1; i >= 0; i --) {
    164                 totalTime += laps[i];
    165                 mLaps.get(i).mTotalTime = totalTime;
    166             }
    167             notifyDataSetChanged();
    168         }
    169     }
    170 
    171     // Keys for data stored in the activity's bundle
    172     private static final String START_TIME_KEY = "start_time";
    173     private static final String ACCUM_TIME_KEY = "accum_time";
    174     private static final String STATE_KEY = "state";
    175     private static final String LAPS_KEY = "laps";
    176 
    177     LapsListAdapter mLapsAdapter;
    178 
    179     public StopwatchFragment() {
    180     }
    181 
    182     private void rightButtonAction() {
    183         long time = Utils.getTimeNow();
    184         Context context = getActivity().getApplicationContext();
    185         Intent intent = new Intent(context, StopwatchService.class);
    186         intent.putExtra(Stopwatches.MESSAGE_TIME, time);
    187         intent.putExtra(Stopwatches.SHOW_NOTIF, false);
    188         buttonClicked(true);
    189         switch (mState) {
    190             case Stopwatches.STOPWATCH_RUNNING:
    191                 // do stop
    192                 long curTime = Utils.getTimeNow();
    193                 mAccumulatedTime += (curTime - mStartTime);
    194                 doStop();
    195                 intent.setAction(Stopwatches.STOP_STOPWATCH);
    196                 context.startService(intent);
    197                 break;
    198             case Stopwatches.STOPWATCH_RESET:
    199             case Stopwatches.STOPWATCH_STOPPED:
    200                 // do start
    201                 doStart(time);
    202                 intent.setAction(Stopwatches.START_STOPWATCH);
    203                 context.startService(intent);
    204                 break;
    205             default:
    206                 Log.wtf("Illegal state " + mState
    207                         + " while pressing the right stopwatch button");
    208                 break;
    209         }
    210     }
    211 
    212     @Override
    213     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    214                              Bundle savedInstanceState) {
    215         // Inflate the layout for this fragment
    216         View v = inflater.inflate(R.layout.stopwatch_fragment, container, false);
    217 
    218         mLeftButton = (ImageButton)v.findViewById(R.id.stopwatch_left_button);
    219         mLeftButton.setOnClickListener(new View.OnClickListener() {
    220             @Override
    221             public void onClick(View v) {
    222                 long time = Utils.getTimeNow();
    223                 Context context = getActivity().getApplicationContext();
    224                 Intent intent = new Intent(context, StopwatchService.class);
    225                 intent.putExtra(Stopwatches.MESSAGE_TIME, time);
    226                 intent.putExtra(Stopwatches.SHOW_NOTIF, false);
    227                 buttonClicked(true);
    228                 switch (mState) {
    229                     case Stopwatches.STOPWATCH_RUNNING:
    230                         // Save lap time
    231                         addLapTime(time);
    232                         doLap();
    233                         intent.setAction(Stopwatches.LAP_STOPWATCH);
    234                         context.startService(intent);
    235                         break;
    236                     case Stopwatches.STOPWATCH_STOPPED:
    237                         // do reset
    238                         doReset();
    239                         intent.setAction(Stopwatches.RESET_STOPWATCH);
    240                         context.startService(intent);
    241                         break;
    242                     default:
    243                         Log.wtf("Illegal state " + mState
    244                                 + " while pressing the left stopwatch button");
    245                         break;
    246                 }
    247             }
    248         });
    249 
    250 
    251         mCenterButton = (TextView)v.findViewById(R.id.stopwatch_stop);
    252         mShareButton = (ImageButton)v.findViewById(R.id.stopwatch_share_button);
    253 
    254         mShareButton.setOnClickListener(new View.OnClickListener() {
    255             @Override
    256             public void onClick(View v) {
    257                 showSharePopup();
    258             }
    259         });
    260 
    261         // Timer text serves as a virtual start/stop button.
    262         final CountingTimerView countingTimerView = (CountingTimerView)
    263                 v.findViewById(R.id.stopwatch_time_text);
    264         countingTimerView.registerVirtualButtonAction(new Runnable() {
    265             @Override
    266             public void run() {
    267                 rightButtonAction();
    268             }
    269         });
    270         countingTimerView.registerStopTextView(mCenterButton);
    271         countingTimerView.setVirtualButtonEnabled(true);
    272 
    273         mTime = (CircleTimerView)v.findViewById(R.id.stopwatch_time);
    274         mTimeText = (CountingTimerView)v.findViewById(R.id.stopwatch_time_text);
    275         mLapsList = (ListView)v.findViewById(R.id.laps_list);
    276         mLapsList.setDividerHeight(0);
    277         mLapsAdapter = new LapsListAdapter(getActivity());
    278         if (mLapsList != null) {
    279             mLapsList.setAdapter(mLapsAdapter);
    280         }
    281 
    282         CircleButtonsLinearLayout circleLayout =
    283                 (CircleButtonsLinearLayout)v.findViewById(R.id.stopwatch_circle);
    284         circleLayout.setCircleTimerViewIds(R.id.stopwatch_time, R.id.stopwatch_left_button,
    285                 R.id.stopwatch_share_button, R.id.stopwatch_stop,
    286                 R.dimen.plusone_reset_button_padding, R.dimen.share_button_padding,
    287                 0, 0); /** No label for a stopwatch**/
    288 
    289         return v;
    290     }
    291 
    292     @Override
    293     public void onResume() {
    294         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
    295         prefs.registerOnSharedPreferenceChangeListener(this);
    296         readFromSharedPref(prefs);
    297         mTime.readFromSharedPref(prefs, "sw");
    298         mTime.postInvalidate();
    299 
    300         setButtons(mState);
    301         mTimeText.setTime(mAccumulatedTime, true, true);
    302         if (mState == Stopwatches.STOPWATCH_RUNNING) {
    303             startUpdateThread();
    304         } else if (mState == Stopwatches.STOPWATCH_STOPPED && mAccumulatedTime != 0) {
    305             mTimeText.blinkTimeStr(true);
    306         }
    307         showLaps();
    308 
    309         super.onResume();
    310     }
    311 
    312     @Override
    313     public void onPause() {
    314         if (mState == Stopwatches.STOPWATCH_RUNNING) {
    315             stopUpdateThread();
    316         }
    317         // The stopwatch must keep running even if the user closes the app so save stopwatch state
    318         // in shared prefs
    319         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
    320         prefs.unregisterOnSharedPreferenceChangeListener(this);
    321         writeToSharedPref(prefs);
    322         mTime.writeToSharedPref(prefs, "sw");
    323         mTimeText.blinkTimeStr(false);
    324         if (mSharePopup != null) {
    325             mSharePopup.dismiss();
    326             mSharePopup = null;
    327         }
    328 
    329         super.onPause();
    330     }
    331 
    332     private void doStop() {
    333         stopUpdateThread();
    334         mTime.pauseIntervalAnimation();
    335         mTimeText.setTime(mAccumulatedTime, true, true);
    336         mTimeText.blinkTimeStr(true);
    337         updateCurrentLap(mAccumulatedTime);
    338         setButtons(Stopwatches.STOPWATCH_STOPPED);
    339         mState = Stopwatches.STOPWATCH_STOPPED;
    340     }
    341 
    342     private void doStart(long time) {
    343         mStartTime = time;
    344         startUpdateThread();
    345         mTimeText.blinkTimeStr(false);
    346         if (mTime.isAnimating()) {
    347             mTime.startIntervalAnimation();
    348         }
    349         setButtons(Stopwatches.STOPWATCH_RUNNING);
    350         mState = Stopwatches.STOPWATCH_RUNNING;
    351     }
    352 
    353     private void doLap() {
    354         showLaps();
    355         setButtons(Stopwatches.STOPWATCH_RUNNING);
    356     }
    357 
    358     private void doReset() {
    359         SharedPreferences prefs =
    360                 PreferenceManager.getDefaultSharedPreferences(getActivity());
    361         Utils.clearSwSharedPref(prefs);
    362         mTime.clearSharedPref(prefs, "sw");
    363         mAccumulatedTime = 0;
    364         mLapsAdapter.clearLaps();
    365         showLaps();
    366         mTime.stopIntervalAnimation();
    367         mTime.reset();
    368         mTimeText.setTime(mAccumulatedTime, true, true);
    369         mTimeText.blinkTimeStr(false);
    370         setButtons(Stopwatches.STOPWATCH_RESET);
    371         mState = Stopwatches.STOPWATCH_RESET;
    372     }
    373 
    374     private void showShareButton(boolean show) {
    375         if (mShareButton != null) {
    376             mShareButton.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
    377             mShareButton.setEnabled(show);
    378         }
    379     }
    380 
    381     private void showSharePopup() {
    382         Intent intent = getShareIntent();
    383 
    384         Activity parent = getActivity();
    385         PackageManager packageManager = parent.getPackageManager();
    386 
    387         // Get a list of sharable options.
    388         List<ResolveInfo> shareOptions = packageManager
    389                 .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
    390 
    391         if (shareOptions.size() == 0) {
    392             return;
    393         }
    394         ArrayList<CharSequence> shareOptionTitles = new ArrayList<CharSequence>();
    395         ArrayList<Drawable> shareOptionIcons = new ArrayList<Drawable>();
    396         ArrayList<CharSequence> shareOptionThreeTitles = new ArrayList<CharSequence>();
    397         ArrayList<Drawable> shareOptionThreeIcons = new ArrayList<Drawable>();
    398         ArrayList<String> shareOptionPackageNames = new ArrayList<String>();
    399         ArrayList<String> shareOptionClassNames = new ArrayList<String>();
    400 
    401         for (int option_i = 0; option_i < shareOptions.size(); option_i++) {
    402             ResolveInfo option = shareOptions.get(option_i);
    403             CharSequence label = option.loadLabel(packageManager);
    404             Drawable icon = option.loadIcon(packageManager);
    405             shareOptionTitles.add(label);
    406             shareOptionIcons.add(icon);
    407             if (shareOptions.size() > 4 && option_i < 3) {
    408                 shareOptionThreeTitles.add(label);
    409                 shareOptionThreeIcons.add(icon);
    410             }
    411             shareOptionPackageNames.add(option.activityInfo.packageName);
    412             shareOptionClassNames.add(option.activityInfo.name);
    413         }
    414         if (shareOptionTitles.size() > 4) {
    415             shareOptionThreeTitles.add(getResources().getString(R.string.see_all));
    416             shareOptionThreeIcons.add(getResources().getDrawable(android.R.color.transparent));
    417         }
    418 
    419         if (mSharePopup != null) {
    420             mSharePopup.dismiss();
    421             mSharePopup = null;
    422         }
    423         mSharePopup = new ListPopupWindow(parent);
    424         mSharePopup.setAnchorView(mShareButton);
    425         mSharePopup.setModal(true);
    426         // This adapter to show the rest will be used to quickly repopulate if "See all..." is hit.
    427         ImageLabelAdapter showAllAdapter = new ImageLabelAdapter(parent,
    428                 R.layout.popup_window_item, shareOptionTitles, shareOptionIcons,
    429                 shareOptionPackageNames, shareOptionClassNames);
    430         if (shareOptionTitles.size() > 4) {
    431             mSharePopup.setAdapter(new ImageLabelAdapter(parent, R.layout.popup_window_item,
    432                     shareOptionThreeTitles, shareOptionThreeIcons, shareOptionPackageNames,
    433                     shareOptionClassNames, showAllAdapter));
    434         } else {
    435             mSharePopup.setAdapter(showAllAdapter);
    436         }
    437 
    438         mSharePopup.setOnItemClickListener(new OnItemClickListener() {
    439             @Override
    440             public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    441                 CharSequence label = ((TextView) view.findViewById(R.id.title)).getText();
    442                 if (label.equals(getResources().getString(R.string.see_all))) {
    443                     mSharePopup.setAdapter(
    444                             ((ImageLabelAdapter) parent.getAdapter()).getShowAllAdapter());
    445                     mSharePopup.show();
    446                     return;
    447                 }
    448 
    449                 Intent intent = getShareIntent();
    450                 ImageLabelAdapter adapter = (ImageLabelAdapter) parent.getAdapter();
    451                 String packageName = adapter.getPackageName(position);
    452                 String className = adapter.getClassName(position);
    453                 intent.setClassName(packageName, className);
    454                 startActivity(intent);
    455             }
    456         });
    457         mSharePopup.setOnDismissListener(new OnDismissListener() {
    458             @Override
    459             public void onDismiss() {
    460                 mSharePopup = null;
    461             }
    462         });
    463         mSharePopup.setWidth((int) getResources().getDimension(R.dimen.popup_window_width));
    464         mSharePopup.show();
    465     }
    466 
    467     private Intent getShareIntent() {
    468         Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    469         intent.setType("text/plain");
    470         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
    471         intent.putExtra(Intent.EXTRA_SUBJECT,
    472                 Stopwatches.getShareTitle(getActivity().getApplicationContext()));
    473         intent.putExtra(Intent.EXTRA_TEXT, Stopwatches.buildShareResults(
    474                 getActivity().getApplicationContext(), mTimeText.getTimeString(),
    475                 getLapShareTimes(mLapsAdapter.getLapTimes())));
    476         return intent;
    477     }
    478 
    479     /** Turn laps as they would be saved in prefs into format for sharing. **/
    480     private long[] getLapShareTimes(long[] input) {
    481         if (input == null) {
    482             return null;
    483         }
    484 
    485         int numLaps = input.length;
    486         long[] output = new long[numLaps];
    487         long prevLapElapsedTime = 0;
    488         for (int lap_i = numLaps - 1; lap_i >= 0; lap_i--) {
    489             long lap = input[lap_i];
    490             Log.v("lap "+lap_i+": "+lap);
    491             output[lap_i] = lap - prevLapElapsedTime;
    492             prevLapElapsedTime = lap;
    493         }
    494         return output;
    495     }
    496 
    497     /***
    498      * Update the buttons on the stopwatch according to the watch's state
    499      */
    500     private void setButtons(int state) {
    501         switch (state) {
    502             case Stopwatches.STOPWATCH_RESET:
    503                 setButton(mLeftButton, R.string.sw_lap_button, R.drawable.ic_lap, false,
    504                         View.INVISIBLE);
    505                 setStartStopText(mCenterButton, R.string.sw_start_button);
    506                 showShareButton(false);
    507                 break;
    508             case Stopwatches.STOPWATCH_RUNNING:
    509                 setButton(mLeftButton, R.string.sw_lap_button, R.drawable.ic_lap,
    510                         !reachedMaxLaps(), View.VISIBLE);
    511                 setStartStopText(mCenterButton, R.string.sw_stop_button);
    512                 showShareButton(false);
    513                 break;
    514             case Stopwatches.STOPWATCH_STOPPED:
    515                 setButton(mLeftButton, R.string.sw_reset_button, R.drawable.ic_reset, true,
    516                         View.VISIBLE);
    517                 setStartStopText(mCenterButton, R.string.sw_start_button);
    518                 showShareButton(true);
    519                 break;
    520             default:
    521                 break;
    522         }
    523     }
    524     private boolean reachedMaxLaps() {
    525         return mLapsAdapter.getCount() >= Stopwatches.MAX_LAPS;
    526     }
    527 
    528     /***
    529      * Set a single button with the string and states provided.
    530      * @param b - Button view to update
    531      * @param text - Text in button
    532      * @param enabled - enable/disables the button
    533      * @param visibility - Show/hide the button
    534      */
    535     private void setButton(
    536             ImageButton b, int text, int drawableId, boolean enabled, int visibility) {
    537         b.setContentDescription(getActivity().getResources().getString(text));
    538         b.setImageResource(drawableId);
    539         b.setVisibility(visibility);
    540         b.setEnabled(enabled);
    541     }
    542 
    543     private void setStartStopText(TextView v, int text) {
    544         String textStr = getActivity().getResources().getString(text);
    545         v.setText(textStr);
    546         v.setContentDescription(textStr);
    547     }
    548 
    549     /***
    550      *
    551      * @param time - in hundredths of a second
    552      */
    553     private void addLapTime(long time) {
    554         int size = mLapsAdapter.getCount();
    555         long curTime = time - mStartTime + mAccumulatedTime;
    556         if (size == 0) {
    557             // Always show the ending lap and a new one
    558             mLapsAdapter.addLap(new Lap(curTime, curTime));
    559             mLapsAdapter.addLap(new Lap(0, curTime));
    560             mTime.setIntervalTime(curTime);
    561         } else {
    562             long lapTime = curTime - ((Lap) mLapsAdapter.getItem(1)).mTotalTime;
    563             ((Lap)mLapsAdapter.getItem(0)).mLapTime = lapTime;
    564             ((Lap)mLapsAdapter.getItem(0)).mTotalTime = curTime;
    565             mLapsAdapter.addLap(new Lap(0, 0));
    566             mTime.setMarkerTime(lapTime);
    567         //    mTime.setIntervalTime(lapTime * 10);
    568         }
    569         mLapsAdapter.notifyDataSetChanged();
    570         // Start lap animation starting from the second lap
    571          mTime.stopIntervalAnimation();
    572          if (!reachedMaxLaps()) {
    573              mTime.startIntervalAnimation();
    574          }
    575     }
    576 
    577     private void updateCurrentLap(long totalTime) {
    578         if (mLapsAdapter.getCount() > 0) {
    579             Lap curLap = (Lap)mLapsAdapter.getItem(0);
    580             curLap.mLapTime = totalTime - ((Lap)mLapsAdapter.getItem(1)).mTotalTime;
    581             curLap.mTotalTime = totalTime;
    582             mLapsAdapter.notifyDataSetChanged();
    583         }
    584     }
    585 
    586     private void showLaps() {
    587         if (mLapsAdapter.getCount() > 0) {
    588             mLapsList.setVisibility(View.VISIBLE);
    589         } else {
    590             mLapsList.setVisibility(View.INVISIBLE);
    591         }
    592     }
    593 
    594     private void startUpdateThread() {
    595         mTime.post(mTimeUpdateThread);
    596     }
    597 
    598     private void stopUpdateThread() {
    599         mTime.removeCallbacks(mTimeUpdateThread);
    600     }
    601 
    602     Runnable mTimeUpdateThread = new Runnable() {
    603         @Override
    604         public void run() {
    605             long curTime = Utils.getTimeNow();
    606             long totalTime = mAccumulatedTime + (curTime - mStartTime);
    607             if (mTime != null) {
    608                 mTimeText.setTime(totalTime, true, true);
    609             }
    610             if (mLapsAdapter.getCount() > 0) {
    611                 updateCurrentLap(totalTime);
    612             }
    613             mTime.postDelayed(mTimeUpdateThread, 10);
    614         }
    615     };
    616 
    617     private void writeToSharedPref(SharedPreferences prefs) {
    618         SharedPreferences.Editor editor = prefs.edit();
    619         editor.putLong (Stopwatches.PREF_START_TIME, mStartTime);
    620         editor.putLong (Stopwatches.PREF_ACCUM_TIME, mAccumulatedTime);
    621         editor.putInt (Stopwatches.PREF_STATE, mState);
    622         if (mLapsAdapter != null) {
    623             long [] laps = mLapsAdapter.getLapTimes();
    624             if (laps != null) {
    625                 editor.putInt (Stopwatches.PREF_LAP_NUM, laps.length);
    626                 for (int i = 0; i < laps.length; i++) {
    627                     String key = Stopwatches.PREF_LAP_TIME + Integer.toString(laps.length - i);
    628                     editor.putLong (key, laps[i]);
    629                 }
    630             }
    631         }
    632         if (mState == Stopwatches.STOPWATCH_RUNNING) {
    633             editor.putLong(Stopwatches.NOTIF_CLOCK_BASE, mStartTime-mAccumulatedTime);
    634             editor.putLong(Stopwatches.NOTIF_CLOCK_ELAPSED, -1);
    635             editor.putBoolean(Stopwatches.NOTIF_CLOCK_RUNNING, true);
    636         } else if (mState == Stopwatches.STOPWATCH_STOPPED) {
    637             editor.putLong(Stopwatches.NOTIF_CLOCK_ELAPSED, mAccumulatedTime);
    638             editor.putLong(Stopwatches.NOTIF_CLOCK_BASE, -1);
    639             editor.putBoolean(Stopwatches.NOTIF_CLOCK_RUNNING, false);
    640         } else if (mState == Stopwatches.STOPWATCH_RESET) {
    641             editor.remove(Stopwatches.NOTIF_CLOCK_BASE);
    642             editor.remove(Stopwatches.NOTIF_CLOCK_RUNNING);
    643             editor.remove(Stopwatches.NOTIF_CLOCK_ELAPSED);
    644         }
    645         editor.putBoolean(Stopwatches.PREF_UPDATE_CIRCLE, false);
    646         editor.apply();
    647     }
    648 
    649     private void readFromSharedPref(SharedPreferences prefs) {
    650         mStartTime = prefs.getLong(Stopwatches.PREF_START_TIME, 0);
    651         mAccumulatedTime = prefs.getLong(Stopwatches.PREF_ACCUM_TIME, 0);
    652         mState = prefs.getInt(Stopwatches.PREF_STATE, Stopwatches.STOPWATCH_RESET);
    653         int numLaps = prefs.getInt(Stopwatches.PREF_LAP_NUM, Stopwatches.STOPWATCH_RESET);
    654         if (mLapsAdapter != null) {
    655             long[] oldLaps = mLapsAdapter.getLapTimes();
    656             if (oldLaps == null || oldLaps.length < numLaps) {
    657                 long[] laps = new long[numLaps];
    658                 long prevLapElapsedTime = 0;
    659                 for (int lap_i = 0; lap_i < numLaps; lap_i++) {
    660                     String key = Stopwatches.PREF_LAP_TIME + Integer.toString(lap_i + 1);
    661                     long lap = prefs.getLong(key, 0);
    662                     laps[numLaps - lap_i - 1] = lap - prevLapElapsedTime;
    663                     prevLapElapsedTime = lap;
    664                 }
    665                 mLapsAdapter.setLapTimes(laps);
    666             }
    667         }
    668         if (prefs.getBoolean(Stopwatches.PREF_UPDATE_CIRCLE, true)) {
    669             if (mState == Stopwatches.STOPWATCH_STOPPED) {
    670                 doStop();
    671             } else if (mState == Stopwatches.STOPWATCH_RUNNING) {
    672                 doStart(mStartTime);
    673             } else if (mState == Stopwatches.STOPWATCH_RESET) {
    674                 doReset();
    675             }
    676         }
    677     }
    678 
    679     public class ImageLabelAdapter extends ArrayAdapter<CharSequence> {
    680         private final ArrayList<CharSequence> mStrings;
    681         private final ArrayList<Drawable> mDrawables;
    682         private final ArrayList<String> mPackageNames;
    683         private final ArrayList<String> mClassNames;
    684         private ImageLabelAdapter mShowAllAdapter;
    685 
    686         public ImageLabelAdapter(Context context, int textViewResourceId,
    687                 ArrayList<CharSequence> strings, ArrayList<Drawable> drawables,
    688                 ArrayList<String> packageNames, ArrayList<String> classNames) {
    689             super(context, textViewResourceId, strings);
    690             mStrings = strings;
    691             mDrawables = drawables;
    692             mPackageNames = packageNames;
    693             mClassNames = classNames;
    694         }
    695 
    696         // Use this constructor if showing a "see all" option, to pass in the adapter
    697         // that will be needed to quickly show all the remaining options.
    698         public ImageLabelAdapter(Context context, int textViewResourceId,
    699                 ArrayList<CharSequence> strings, ArrayList<Drawable> drawables,
    700                 ArrayList<String> packageNames, ArrayList<String> classNames,
    701                 ImageLabelAdapter showAllAdapter) {
    702             super(context, textViewResourceId, strings);
    703             mStrings = strings;
    704             mDrawables = drawables;
    705             mPackageNames = packageNames;
    706             mClassNames = classNames;
    707             mShowAllAdapter = showAllAdapter;
    708         }
    709 
    710         @Override
    711         public View getView(int position, View convertView, ViewGroup parent) {
    712             LayoutInflater li = getActivity().getLayoutInflater();
    713             View row = li.inflate(R.layout.popup_window_item, parent, false);
    714             ((TextView) row.findViewById(R.id.title)).setText(
    715                     mStrings.get(position));
    716             ((ImageView) row.findViewById(R.id.icon)).setBackground(
    717                     mDrawables.get(position));
    718             return row;
    719         }
    720 
    721         public String getPackageName(int position) {
    722             return mPackageNames.get(position);
    723         }
    724 
    725         public String getClassName(int position) {
    726             return mClassNames.get(position);
    727         }
    728 
    729         public ImageLabelAdapter getShowAllAdapter() {
    730             return mShowAllAdapter;
    731         }
    732     }
    733 
    734     @Override
    735     public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
    736         if (prefs.equals(PreferenceManager.getDefaultSharedPreferences(getActivity()))) {
    737             if (! (key.equals(Stopwatches.PREF_LAP_NUM) ||
    738                     key.startsWith(Stopwatches.PREF_LAP_TIME))) {
    739                 readFromSharedPref(prefs);
    740                 if (prefs.getBoolean(Stopwatches.PREF_UPDATE_CIRCLE, true)) {
    741                     mTime.readFromSharedPref(prefs, "sw");
    742                 }
    743             }
    744         }
    745     }
    746 }
    747