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