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