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