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