• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.deskclock;
18 
19 import android.animation.ArgbEvaluator;
20 import android.animation.ObjectAnimator;
21 import android.app.ActionBar;
22 import android.app.ActionBar.Tab;
23 import android.app.Activity;
24 import android.app.Fragment;
25 import android.app.FragmentManager;
26 import android.app.FragmentTransaction;
27 import android.content.ActivityNotFoundException;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.SharedPreferences;
31 import android.content.res.Configuration;
32 import android.graphics.Outline;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.preference.PreferenceManager;
36 import android.support.v13.app.FragmentPagerAdapter;
37 import android.support.v4.view.ViewPager;
38 import android.text.TextUtils;
39 import android.text.format.DateUtils;
40 import android.util.Log;
41 import android.view.Menu;
42 import android.view.MenuItem;
43 import android.view.MotionEvent;
44 import android.view.View;
45 import android.view.View.OnClickListener;
46 import android.view.View.OnTouchListener;
47 import android.view.ViewOutlineProvider;
48 import android.widget.ImageButton;
49 import android.widget.TextView;
50 
51 import com.android.deskclock.alarms.AlarmStateManager;
52 import com.android.deskclock.provider.Alarm;
53 import com.android.deskclock.stopwatch.StopwatchFragment;
54 import com.android.deskclock.stopwatch.StopwatchService;
55 import com.android.deskclock.stopwatch.Stopwatches;
56 import com.android.deskclock.timer.TimerFragment;
57 import com.android.deskclock.timer.TimerObj;
58 import com.android.deskclock.timer.Timers;
59 
60 import java.util.ArrayList;
61 import java.util.HashSet;
62 import java.util.Locale;
63 import java.util.TimeZone;
64 
65 /**
66  * DeskClock clock view for desk docks.
67  */
68 public 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
onStart()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
onStop()130     protected void onStop() {
131         super.onStop();
132         mHander.removeCallbacks(mBackgroundColorChanger);
133     }
134 
135     @Override
onNewIntent(Intent newIntent)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 
initViews()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 
getSelectedFragment()197     private DeskClockFragment getSelectedFragment() {
198         return (DeskClockFragment) mTabsAdapter.getItem(getRtlPosition(mSelectedTab));
199     }
200 
createTabs(int selectedIndex)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
onCreate(Bundle icicle)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
onResume()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
onPause()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
onSaveInstanceState(Bundle outState)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
onCreateOptionsMenu(Menu menu)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
onPrepareOptionsMenu(Menu menu)331     public boolean onPrepareOptionsMenu(Menu menu) {
332         updateMenu(menu);
333         return true;
334     }
335 
updateMenu(Menu menu)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
onOptionsItemSelected(MenuItem item)353     public boolean onOptionsItemSelected(MenuItem item) {
354         if (processMenuClick(item)) {
355             return true;
356         }
357 
358         return super.onOptionsItemSelected(item);
359     }
360 
processMenuClick(MenuItem item)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      */
setHomeTimeZone()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 
registerPageChangedListener(DeskClockFragment frag)400     public void registerPageChangedListener(DeskClockFragment frag) {
401         if (mTabsAdapter != null) {
402             mTabsAdapter.registerPageChangedListener(frag);
403         }
404     }
405 
unregisterPageChangedListener(DeskClockFragment frag)406     public void unregisterPageChangedListener(DeskClockFragment frag) {
407         if (mTabsAdapter != null) {
408             mTabsAdapter.unregisterPageChangedListener(frag);
409         }
410     }
411 
setBackgroundColor()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 
TabInfo(Class<?> _class, int position)443             TabInfo(Class<?> _class, int position) {
444                 clss = _class;
445                 args = new Bundle();
446                 args.putInt(KEY_TAB_POSITION, position);
447             }
448 
getPosition()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 
TabsAdapter(Activity activity, ViewPager pager)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
getItem(int position)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          */
makeFragmentName(int viewId, int index)492         private String makeFragmentName(int viewId, int index) {
493             return "android:switcher:" + viewId + ":" + index;
494         }
495 
496         @Override
getCount()497         public int getCount() {
498             return mTabs.size();
499         }
500 
addTab(ActionBar.Tab tab, Class<?> clss, int position)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
onPageScrolled(int position, float positionOffset, int positionOffsetPixels)511         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
512             // Do nothing
513         }
514 
515         @Override
onPageSelected(int position)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
onPageScrollStateChanged(int state)534         public void onPageScrollStateChanged(int state) {
535             // Do nothing
536         }
537 
538         @Override
onTabReselected(Tab arg0, FragmentTransaction arg1)539         public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
540             // Do nothing
541         }
542 
543         @Override
onTabSelected(Tab tab, FragmentTransaction ft)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
onTabUnselected(Tab arg0, FragmentTransaction arg1)566         public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {
567             // Do nothing
568         }
569 
isClockTab(int rtlSafePosition)570         private boolean isClockTab(int rtlSafePosition) {
571             final int clockTabIndex = isRtl() ? RTL_CLOCK_TAB_INDEX : CLOCK_TAB_INDEX;
572             return rtlSafePosition == clockTabIndex;
573         }
574 
notifySelectedPage(int page)575         public void notifySelectedPage(int page) {
576             notifyPageChanged(page);
577         }
578 
notifyPageChanged(int newPage)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 
registerPageChangedListener(DeskClockFragment frag)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 
unregisterPageChangedListener(DeskClockFragment frag)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 
OnTapListener(Activity activity, TextView makePressedView)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
onTouch(View v, MotionEvent e)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 
resetValues()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 
processClick(View v)670         protected abstract void processClick(View v);
671     }
672 
673     /**
674      * Called by the LabelDialogFormat class after the dialog is finished. *
675      */
676     @Override
onDialogLabelSet(TimerObj timer, String label, String tag)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
onDialogLabelSet(Alarm alarm, String label, String tag)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 
getSelectedTab()695     public int getSelectedTab() {
696         return mSelectedTab;
697     }
698 
isRtl()699     private boolean isRtl() {
700         return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
701                 View.LAYOUT_DIRECTION_RTL;
702     }
703 
getRtlPosition(int position)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 
getFab()722     public ImageButton getFab() {
723         return mFab;
724     }
725 
getLeftButton()726     public ImageButton getLeftButton() {
727         return mLeftButton;
728     }
729 
getRightButton()730     public ImageButton getRightButton() {
731         return mRightButton;
732     }
733 }
734