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