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