DeskClock.java revision 22147db9e42c49b80bf3fffc61bef977af5df484
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.deskclock;
18
19import android.animation.ArgbEvaluator;
20import android.animation.ObjectAnimator;
21import android.app.ActionBar;
22import android.app.ActionBar.Tab;
23import android.app.Activity;
24import android.app.Fragment;
25import android.app.FragmentManager;
26import android.app.FragmentTransaction;
27import android.content.ActivityNotFoundException;
28import android.content.Context;
29import android.content.Intent;
30import android.content.SharedPreferences;
31import android.content.res.Configuration;
32import android.graphics.Outline;
33import android.media.AudioManager;
34import android.os.Bundle;
35import android.os.Handler;
36import android.preference.PreferenceManager;
37import android.support.v13.app.FragmentPagerAdapter;
38import android.support.v4.view.ViewPager;
39import android.text.TextUtils;
40import android.text.format.DateUtils;
41import android.util.Log;
42import android.view.Menu;
43import android.view.MenuItem;
44import android.view.MotionEvent;
45import android.view.View;
46import android.view.View.OnClickListener;
47import android.view.View.OnTouchListener;
48import android.view.ViewOutlineProvider;
49import android.widget.ImageButton;
50import android.widget.TextView;
51
52import com.android.deskclock.alarms.AlarmStateManager;
53import com.android.deskclock.provider.Alarm;
54import com.android.deskclock.stopwatch.StopwatchFragment;
55import com.android.deskclock.stopwatch.StopwatchService;
56import com.android.deskclock.stopwatch.Stopwatches;
57import com.android.deskclock.timer.TimerFragment;
58import com.android.deskclock.timer.TimerObj;
59import com.android.deskclock.timer.Timers;
60
61import java.util.ArrayList;
62import java.util.HashSet;
63import java.util.Locale;
64import java.util.TimeZone;
65
66/**
67 * DeskClock clock view for desk docks.
68 */
69public class DeskClock extends Activity implements LabelDialogFragment.TimerLabelDialogHandler,
70        LabelDialogFragment.AlarmLabelDialogHandler {
71    private static final boolean DEBUG = false;
72    private static final String LOG_TAG = "DeskClock";
73    // Alarm action for midnight (so we can update the date display).
74    private static final String KEY_SELECTED_TAB = "selected_tab";
75    private static final String KEY_LAST_HOUR_COLOR = "last_hour_color";
76    // Check whether to change background every minute
77    private static final long BACKGROUND_COLOR_CHECK_DELAY_MILLIS = DateUtils.MINUTE_IN_MILLIS;
78    private static final int BACKGROUND_COLOR_INITIAL_ANIMATION_DURATION_MILLIS = 3000;
79    private static final int UNKNOWN_COLOR_ID = 0;
80
81    private boolean mIsFirstLaunch = true;
82    private ActionBar mActionBar;
83    private Tab mAlarmTab;
84    private Tab mClockTab;
85    private Tab mTimerTab;
86    private Tab mStopwatchTab;
87    private Menu mMenu;
88    private ViewPager mViewPager;
89    private TabsAdapter mTabsAdapter;
90    private Handler mHander;
91    private ImageButton mFab;
92    private ImageButton mLeftButton;
93    private ImageButton mRightButton;
94    private int mSelectedTab;
95    private int mLastHourColor = UNKNOWN_COLOR_ID;
96    private final Runnable mBackgroundColorChanger = new Runnable() {
97        @Override
98        public void run() {
99            setBackgroundColor();
100            mHander.postDelayed(this, BACKGROUND_COLOR_CHECK_DELAY_MILLIS);
101        }
102    };
103
104    public static final int ALARM_TAB_INDEX = 0;
105    public static final int CLOCK_TAB_INDEX = 1;
106    public static final int TIMER_TAB_INDEX = 2;
107    public static final int STOPWATCH_TAB_INDEX = 3;
108    // Tabs indices are switched for right-to-left since there is no
109    // native support for RTL in the ViewPager.
110    public static final int RTL_ALARM_TAB_INDEX = 3;
111    public static final int RTL_CLOCK_TAB_INDEX = 2;
112    public static final int RTL_TIMER_TAB_INDEX = 1;
113    public static final int RTL_STOPWATCH_TAB_INDEX = 0;
114    public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab";
115
116    // TODO(rachelzhang): adding a broadcast receiver to adjust color when the timezone/time
117    // changes in the background.
118
119    @Override
120    protected void onStart() {
121        super.onStart();
122        if (mHander == null) {
123            mHander = new Handler();
124        }
125        mHander.postDelayed(mBackgroundColorChanger, BACKGROUND_COLOR_CHECK_DELAY_MILLIS);
126    }
127
128    @Override
129    protected void onStop() {
130        super.onStop();
131        mHander.removeCallbacks(mBackgroundColorChanger);
132    }
133
134    @Override
135    public void onNewIntent(Intent newIntent) {
136        super.onNewIntent(newIntent);
137        if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent);
138
139        // update our intent so that we can consult it to determine whether or
140        // not the most recent launch was via a dock event
141        setIntent(newIntent);
142
143        // Timer receiver may ask to go to the timers fragment if a timer expired.
144        int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
145        if (tab != -1) {
146            if (mActionBar != null) {
147                mActionBar.setSelectedNavigationItem(tab);
148            }
149        }
150    }
151
152    private static final ViewOutlineProvider OVAL_OUTLINE_PROVIDER = new ViewOutlineProvider() {
153        @Override
154        public void getOutline(View view, Outline outline) {
155            outline.setOval(0, 0, view.getWidth(), view.getHeight());
156        }
157    };
158
159    private void initViews() {
160        setContentView(R.layout.desk_clock);
161        mFab = (ImageButton) findViewById(R.id.fab);
162        mFab.setOutlineProvider(OVAL_OUTLINE_PROVIDER);
163        mLeftButton = (ImageButton) findViewById(R.id.left_button);
164        mRightButton = (ImageButton) findViewById(R.id.right_button);
165        if (mTabsAdapter == null) {
166            mViewPager = (ViewPager) findViewById(R.id.desk_clock_pager);
167            // Keep all four tabs to minimize jank.
168            mViewPager.setOffscreenPageLimit(3);
169            mTabsAdapter = new TabsAdapter(this, mViewPager);
170            createTabs(mSelectedTab);
171        }
172
173        mFab.setOnClickListener(new OnClickListener() {
174            @Override
175            public void onClick(View view) {
176                getSelectedFragment().onFabClick(view);
177            }
178        });
179        mLeftButton.setOnClickListener(new OnClickListener() {
180            @Override
181            public void onClick(View view) {
182                getSelectedFragment().onLeftButtonClick(view);
183            }
184        });
185        mRightButton.setOnClickListener(new OnClickListener() {
186            @Override
187            public void onClick(View view) {
188                getSelectedFragment().onRightButtonClick(view);
189            }
190        });
191
192        mActionBar.setSelectedNavigationItem(mSelectedTab);
193    }
194
195    private DeskClockFragment getSelectedFragment() {
196        return (DeskClockFragment) mTabsAdapter.getItem(getRtlPosition(mSelectedTab));
197    }
198
199    private void createTabs(int selectedIndex) {
200        mActionBar = getActionBar();
201
202        if (mActionBar != null) {
203            mActionBar.setDisplayOptions(0);
204            mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
205
206            mAlarmTab = mActionBar.newTab();
207            mAlarmTab.setIcon(R.drawable.ic_alarm_animation);
208            mAlarmTab.setContentDescription(R.string.menu_alarm);
209            mTabsAdapter.addTab(mAlarmTab, AlarmClockFragment.class, ALARM_TAB_INDEX);
210
211            mClockTab = mActionBar.newTab();
212            mClockTab.setIcon(R.drawable.ic_clock_animation);
213            mClockTab.setContentDescription(R.string.menu_clock);
214            mTabsAdapter.addTab(mClockTab, ClockFragment.class, CLOCK_TAB_INDEX);
215
216            mTimerTab = mActionBar.newTab();
217            mTimerTab.setIcon(R.drawable.ic_timer_animation);
218            mTimerTab.setContentDescription(R.string.menu_timer);
219            mTabsAdapter.addTab(mTimerTab, TimerFragment.class, TIMER_TAB_INDEX);
220
221            mStopwatchTab = mActionBar.newTab();
222            mStopwatchTab.setIcon(R.drawable.ic_stopwatch_animation);
223            mStopwatchTab.setContentDescription(R.string.menu_stopwatch);
224            mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class, STOPWATCH_TAB_INDEX);
225
226            mActionBar.setSelectedNavigationItem(selectedIndex);
227            mTabsAdapter.notifySelectedPage(selectedIndex);
228        }
229    }
230
231    @Override
232    protected void onCreate(Bundle icicle) {
233        super.onCreate(icicle);
234        setVolumeControlStream(AudioManager.STREAM_ALARM);
235
236        mIsFirstLaunch = (icicle == null);
237        getWindow().setBackgroundDrawable(null);
238
239        mIsFirstLaunch = true;
240        mSelectedTab = CLOCK_TAB_INDEX;
241        if (icicle != null) {
242            mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX);
243            mLastHourColor = icicle.getInt(KEY_LAST_HOUR_COLOR, UNKNOWN_COLOR_ID);
244            if (mLastHourColor != UNKNOWN_COLOR_ID) {
245                getWindow().getDecorView().setBackgroundColor(mLastHourColor);
246            }
247        }
248
249        // Timer receiver may ask the app to go to the timer fragment if a timer expired
250        Intent i = getIntent();
251        if (i != null) {
252            int tab = i.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
253            if (tab != -1) {
254                mSelectedTab = tab;
255            }
256        }
257        initViews();
258        setHomeTimeZone();
259
260        // We need to update the system next alarm time on app startup because the
261        // user might have clear our data.
262        AlarmStateManager.updateNextAlarm(this);
263        ExtensionsFactory.init(getAssets());
264    }
265
266    @Override
267    protected void onResume() {
268        super.onResume();
269
270        setBackgroundColor();
271
272        // We only want to show notifications for stopwatch/timer when the app is closed so
273        // that we don't have to worry about keeping the notifications in perfect sync with
274        // the app.
275        Intent stopwatchIntent = new Intent(getApplicationContext(), StopwatchService.class);
276        stopwatchIntent.setAction(Stopwatches.KILL_NOTIF);
277        startService(stopwatchIntent);
278
279        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
280        SharedPreferences.Editor editor = prefs.edit();
281        editor.putBoolean(Timers.NOTIF_APP_OPEN, true);
282        editor.apply();
283        Intent timerIntent = new Intent();
284        timerIntent.setAction(Timers.NOTIF_IN_USE_CANCEL);
285        sendBroadcast(timerIntent);
286    }
287
288    @Override
289    public void onPause() {
290        Intent intent = new Intent(getApplicationContext(), StopwatchService.class);
291        intent.setAction(Stopwatches.SHOW_NOTIF);
292        startService(intent);
293
294        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
295        SharedPreferences.Editor editor = prefs.edit();
296        editor.putBoolean(Timers.NOTIF_APP_OPEN, false);
297        editor.apply();
298        Utils.showInUseNotifications(this);
299
300        super.onPause();
301    }
302
303    @Override
304    protected void onSaveInstanceState(Bundle outState) {
305        super.onSaveInstanceState(outState);
306        outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex());
307        outState.putInt(KEY_LAST_HOUR_COLOR, mLastHourColor);
308    }
309
310    @Override
311    public boolean onCreateOptionsMenu(Menu menu) {
312        // We only want to show it as a menu in landscape, and only for clock/alarm fragment.
313        mMenu = menu;
314        if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
315            if (mActionBar.getSelectedNavigationIndex() == ALARM_TAB_INDEX ||
316                    mActionBar.getSelectedNavigationIndex() == CLOCK_TAB_INDEX) {
317                // Clear the menu so that it doesn't get duplicate items in case onCreateOptionsMenu
318                // was called multiple times.
319                menu.clear();
320                getMenuInflater().inflate(R.menu.desk_clock_menu, menu);
321            }
322            // Always return true for landscape, regardless of whether we've inflated the menu, so
323            // that when we switch tabs this method will get called and we can inflate the menu.
324            return true;
325        }
326        return false;
327    }
328
329    @Override
330    public boolean onPrepareOptionsMenu(Menu menu) {
331        updateMenu(menu);
332        return true;
333    }
334
335    private void updateMenu(Menu menu) {
336        // Hide "help" if we don't have a URI for it.
337        MenuItem help = menu.findItem(R.id.menu_item_help);
338        if (help != null) {
339            Utils.prepareHelpMenuItem(this, help);
340        }
341
342        // Hide "lights out" for timer.
343        MenuItem nightMode = menu.findItem(R.id.menu_item_night_mode);
344        if (mActionBar.getSelectedNavigationIndex() == ALARM_TAB_INDEX) {
345            nightMode.setVisible(false);
346        } else if (mActionBar.getSelectedNavigationIndex() == CLOCK_TAB_INDEX) {
347            nightMode.setVisible(true);
348        }
349    }
350
351    @Override
352    public boolean onOptionsItemSelected(MenuItem item) {
353        if (processMenuClick(item)) {
354            return true;
355        }
356
357        return super.onOptionsItemSelected(item);
358    }
359
360    private boolean processMenuClick(MenuItem item) {
361        switch (item.getItemId()) {
362            case R.id.menu_item_settings:
363                startActivity(new Intent(DeskClock.this, SettingsActivity.class));
364                return true;
365            case R.id.menu_item_help:
366                Intent i = item.getIntent();
367                if (i != null) {
368                    try {
369                        startActivity(i);
370                    } catch (ActivityNotFoundException e) {
371                        // No activity found to match the intent - ignore
372                    }
373                }
374                return true;
375            case R.id.menu_item_night_mode:
376                startActivity(new Intent(DeskClock.this, ScreensaverActivity.class));
377            default:
378                break;
379        }
380        return true;
381    }
382
383    /**
384     * Insert the local time zone as the Home Time Zone if one is not set
385     */
386    private void setHomeTimeZone() {
387        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
388        String homeTimeZone = prefs.getString(SettingsActivity.KEY_HOME_TZ, "");
389        if (!homeTimeZone.isEmpty()) {
390            return;
391        }
392        homeTimeZone = TimeZone.getDefault().getID();
393        SharedPreferences.Editor editor = prefs.edit();
394        editor.putString(SettingsActivity.KEY_HOME_TZ, homeTimeZone);
395        editor.apply();
396        Log.v(LOG_TAG, "Setting home time zone to " + homeTimeZone);
397    }
398
399    public void registerPageChangedListener(DeskClockFragment frag) {
400        if (mTabsAdapter != null) {
401            mTabsAdapter.registerPageChangedListener(frag);
402        }
403    }
404
405    public void unregisterPageChangedListener(DeskClockFragment frag) {
406        if (mTabsAdapter != null) {
407            mTabsAdapter.unregisterPageChangedListener(frag);
408        }
409    }
410
411    private void setBackgroundColor() {
412        final int duration;
413        if (mLastHourColor == UNKNOWN_COLOR_ID) {
414            mLastHourColor = getResources().getColor(R.color.default_background);
415            duration = BACKGROUND_COLOR_INITIAL_ANIMATION_DURATION_MILLIS;
416        } else {
417            duration = getResources().getInteger(android.R.integer.config_longAnimTime);
418        }
419        final int currHourColor = Utils.getCurrentHourColor();
420        if (mLastHourColor != currHourColor) {
421            final ObjectAnimator animator = ObjectAnimator.ofInt(getWindow().getDecorView(),
422                    "backgroundColor", mLastHourColor, currHourColor);
423            animator.setDuration(duration);
424            animator.setEvaluator(new ArgbEvaluator());
425            animator.start();
426            mLastHourColor = currHourColor;
427        }
428    }
429
430    /**
431     * Adapter for wrapping together the ActionBar's tab with the ViewPager
432     */
433    private class TabsAdapter extends FragmentPagerAdapter
434            implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
435
436        private static final String KEY_TAB_POSITION = "tab_position";
437
438        final class TabInfo {
439            private final Class<?> clss;
440            private final Bundle args;
441
442            TabInfo(Class<?> _class, int position) {
443                clss = _class;
444                args = new Bundle();
445                args.putInt(KEY_TAB_POSITION, position);
446            }
447
448            public int getPosition() {
449                return args.getInt(KEY_TAB_POSITION, 0);
450            }
451        }
452
453        private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
454        ActionBar mMainActionBar;
455        Context mContext;
456        ViewPager mPager;
457        // Used for doing callbacks to fragments.
458        HashSet<String> mFragmentTags = new HashSet<String>();
459
460        public TabsAdapter(Activity activity, ViewPager pager) {
461            super(activity.getFragmentManager());
462            mContext = activity;
463            mMainActionBar = activity.getActionBar();
464            mPager = pager;
465            mPager.setAdapter(this);
466            mPager.setOnPageChangeListener(this);
467        }
468
469        @Override
470        public Fragment getItem(int position) {
471            // Because this public method is called outside many times,
472            // check if it exits first before creating a new one.
473            final String name = makeFragmentName(R.id.desk_clock_pager, position);
474            Fragment fragment = getFragmentManager().findFragmentByTag(name);
475            if (fragment == null) {
476                TabInfo info = mTabs.get(getRtlPosition(position));
477                fragment = Fragment.instantiate(mContext, info.clss.getName(), info.args);
478                if (fragment instanceof TimerFragment) {
479                    ((TimerFragment) fragment).setFabAppearance();
480                    ((TimerFragment) fragment).setLeftRightButtonAppearance();
481                }
482            }
483            return fragment;
484        }
485
486        /**
487         * Copied from:
488         * android/frameworks/support/v13/java/android/support/v13/app/FragmentPagerAdapter.java#94
489         * Create unique name for the fragment so fragment manager knows it exist.
490         */
491        private String makeFragmentName(int viewId, int index) {
492            return "android:switcher:" + viewId + ":" + index;
493        }
494
495        @Override
496        public int getCount() {
497            return mTabs.size();
498        }
499
500        public void addTab(ActionBar.Tab tab, Class<?> clss, int position) {
501            TabInfo info = new TabInfo(clss, position);
502            tab.setTag(info);
503            tab.setTabListener(this);
504            mTabs.add(info);
505            mMainActionBar.addTab(tab);
506            notifyDataSetChanged();
507        }
508
509        @Override
510        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
511            // Do nothing
512        }
513
514        @Override
515        public void onPageSelected(int position) {
516            // Set the page before doing the menu so that onCreateOptionsMenu knows what page it is.
517            mMainActionBar.setSelectedNavigationItem(getRtlPosition(position));
518            notifyPageChanged(position);
519
520            // Only show the overflow menu for alarm and world clock.
521            if (mMenu != null) {
522                // Make sure the menu's been initialized.
523                if (position == ALARM_TAB_INDEX || position == CLOCK_TAB_INDEX) {
524                    mMenu.setGroupVisible(R.id.menu_items, true);
525                    onCreateOptionsMenu(mMenu);
526                } else {
527                    mMenu.setGroupVisible(R.id.menu_items, false);
528                }
529            }
530        }
531
532        @Override
533        public void onPageScrollStateChanged(int state) {
534            // Do nothing
535        }
536
537        @Override
538        public void onTabReselected(Tab tab, FragmentTransaction arg1) {
539            // Do nothing
540        }
541
542        @Override
543        public void onTabSelected(Tab tab, FragmentTransaction ft) {
544            final TabInfo info = (TabInfo) tab.getTag();
545            final int position = info.getPosition();
546            final int rtlSafePosition = getRtlPosition(position);
547            mSelectedTab = position;
548
549            if (mIsFirstLaunch && isClockTab(rtlSafePosition)) {
550                mLeftButton.setVisibility(View.INVISIBLE);
551                mRightButton.setVisibility(View.INVISIBLE);
552                mFab.setVisibility(View.VISIBLE);
553                mFab.setImageResource(R.drawable.ic_globe);
554                mFab.setContentDescription(getString(R.string.button_cities));
555                mIsFirstLaunch = false;
556            } else {
557                DeskClockFragment f = (DeskClockFragment) getItem(rtlSafePosition);
558                f.setFabAppearance();
559                f.setLeftRightButtonAppearance();
560            }
561            mPager.setCurrentItem(rtlSafePosition);
562        }
563
564        @Override
565        public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {
566            // Do nothing
567        }
568
569        private boolean isClockTab(int rtlSafePosition) {
570            final int clockTabIndex = isRtl() ? RTL_CLOCK_TAB_INDEX : CLOCK_TAB_INDEX;
571            return rtlSafePosition == clockTabIndex;
572        }
573
574        public void notifySelectedPage(int page) {
575            notifyPageChanged(page);
576        }
577
578        private void notifyPageChanged(int newPage) {
579            for (String tag : mFragmentTags) {
580                final FragmentManager fm = getFragmentManager();
581                DeskClockFragment f = (DeskClockFragment) fm.findFragmentByTag(tag);
582                if (f != null) {
583                    f.onPageChanged(newPage);
584                }
585            }
586        }
587
588        public void registerPageChangedListener(DeskClockFragment frag) {
589            String tag = frag.getTag();
590            if (mFragmentTags.contains(tag)) {
591                Log.wtf(LOG_TAG, "Trying to add an existing fragment " + tag);
592            } else {
593                mFragmentTags.add(frag.getTag());
594            }
595            // Since registering a listener by the fragment is done sometimes after the page
596            // was already changed, make sure the fragment gets the current page
597            frag.onPageChanged(mMainActionBar.getSelectedNavigationIndex());
598        }
599
600        public void unregisterPageChangedListener(DeskClockFragment frag) {
601            mFragmentTags.remove(frag.getTag());
602        }
603
604    }
605
606    public static abstract class OnTapListener implements OnTouchListener {
607        private float mLastTouchX;
608        private float mLastTouchY;
609        private long mLastTouchTime;
610        private final TextView mMakePressedTextView;
611        private final int mPressedColor, mGrayColor;
612        private final float MAX_MOVEMENT_ALLOWED = 20;
613        private final long MAX_TIME_ALLOWED = 500;
614
615        public OnTapListener(Activity activity, TextView makePressedView) {
616            mMakePressedTextView = makePressedView;
617            mPressedColor = activity.getResources().getColor(Utils.getPressedColorId());
618            mGrayColor = activity.getResources().getColor(Utils.getGrayColorId());
619        }
620
621        @Override
622        public boolean onTouch(View v, MotionEvent e) {
623            switch (e.getAction()) {
624                case (MotionEvent.ACTION_DOWN):
625                    mLastTouchTime = Utils.getTimeNow();
626                    mLastTouchX = e.getX();
627                    mLastTouchY = e.getY();
628                    if (mMakePressedTextView != null) {
629                        mMakePressedTextView.setTextColor(mPressedColor);
630                    }
631                    break;
632                case (MotionEvent.ACTION_UP):
633                    float xDiff = Math.abs(e.getX() - mLastTouchX);
634                    float yDiff = Math.abs(e.getY() - mLastTouchY);
635                    long timeDiff = (Utils.getTimeNow() - mLastTouchTime);
636                    if (xDiff < MAX_MOVEMENT_ALLOWED && yDiff < MAX_MOVEMENT_ALLOWED
637                            && timeDiff < MAX_TIME_ALLOWED) {
638                        if (mMakePressedTextView != null) {
639                            v = mMakePressedTextView;
640                        }
641                        processClick(v);
642                        resetValues();
643                        return true;
644                    }
645                    resetValues();
646                    break;
647                case (MotionEvent.ACTION_MOVE):
648                    xDiff = Math.abs(e.getX() - mLastTouchX);
649                    yDiff = Math.abs(e.getY() - mLastTouchY);
650                    if (xDiff >= MAX_MOVEMENT_ALLOWED || yDiff >= MAX_MOVEMENT_ALLOWED) {
651                        resetValues();
652                    }
653                    break;
654                default:
655                    resetValues();
656            }
657            return false;
658        }
659
660        private void resetValues() {
661            mLastTouchX = -1 * MAX_MOVEMENT_ALLOWED + 1;
662            mLastTouchY = -1 * MAX_MOVEMENT_ALLOWED + 1;
663            mLastTouchTime = -1 * MAX_TIME_ALLOWED + 1;
664            if (mMakePressedTextView != null) {
665                mMakePressedTextView.setTextColor(mGrayColor);
666            }
667        }
668
669        protected abstract void processClick(View v);
670    }
671
672    /**
673     * Called by the LabelDialogFormat class after the dialog is finished. *
674     */
675    @Override
676    public void onDialogLabelSet(TimerObj timer, String label, String tag) {
677        Fragment frag = getFragmentManager().findFragmentByTag(tag);
678        if (frag instanceof TimerFragment) {
679            ((TimerFragment) frag).setLabel(timer, label);
680        }
681    }
682
683    /**
684     * Called by the LabelDialogFormat class after the dialog is finished. *
685     */
686    @Override
687    public void onDialogLabelSet(Alarm alarm, String label, String tag) {
688        Fragment frag = getFragmentManager().findFragmentByTag(tag);
689        if (frag instanceof AlarmClockFragment) {
690            ((AlarmClockFragment) frag).setLabel(alarm, label);
691        }
692    }
693
694    public int getSelectedTab() {
695        return mSelectedTab;
696    }
697
698    private boolean isRtl() {
699        return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
700                View.LAYOUT_DIRECTION_RTL;
701    }
702
703    private int getRtlPosition(int position) {
704        if (isRtl()) {
705            switch (position) {
706                case TIMER_TAB_INDEX:
707                    return RTL_TIMER_TAB_INDEX;
708                case CLOCK_TAB_INDEX:
709                    return RTL_CLOCK_TAB_INDEX;
710                case STOPWATCH_TAB_INDEX:
711                    return RTL_STOPWATCH_TAB_INDEX;
712                case ALARM_TAB_INDEX:
713                    return RTL_ALARM_TAB_INDEX;
714                default:
715                    break;
716            }
717        }
718        return position;
719    }
720
721    public ImageButton getFab() {
722        return mFab;
723    }
724
725    public ImageButton getLeftButton() {
726        return mLeftButton;
727    }
728
729    public ImageButton getRightButton() {
730        return mRightButton;
731    }
732}
733