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.app.ActionBar;
     20 import android.app.ActionBar.Tab;
     21 import android.app.Activity;
     22 import android.app.Fragment;
     23 import android.app.FragmentManager;
     24 import android.app.FragmentTransaction;
     25 import android.content.ActivityNotFoundException;
     26 import android.content.Context;
     27 import android.content.Intent;
     28 import android.content.SharedPreferences;
     29 import android.os.Bundle;
     30 import android.preference.PreferenceManager;
     31 import android.support.v13.app.FragmentPagerAdapter;
     32 import android.support.v4.view.ViewPager;
     33 import android.text.TextUtils;
     34 import android.util.Log;
     35 import android.view.Menu;
     36 import android.view.MenuItem;
     37 import android.view.MotionEvent;
     38 import android.view.View;
     39 import android.view.View.OnTouchListener;
     40 import android.widget.PopupMenu;
     41 import android.widget.TextView;
     42 
     43 import com.android.deskclock.stopwatch.StopwatchFragment;
     44 import com.android.deskclock.stopwatch.StopwatchService;
     45 import com.android.deskclock.stopwatch.Stopwatches;
     46 import com.android.deskclock.timer.TimerFragment;
     47 import com.android.deskclock.timer.TimerObj;
     48 import com.android.deskclock.timer.Timers;
     49 import com.android.deskclock.worldclock.CitiesActivity;
     50 
     51 import java.util.ArrayList;
     52 import java.util.HashSet;
     53 import java.util.Locale;
     54 import java.util.TimeZone;
     55 
     56 /**
     57  * DeskClock clock view for desk docks.
     58  */
     59 public class DeskClock extends Activity implements LabelDialogFragment.TimerLabelDialogHandler {
     60     private static final boolean DEBUG = false;
     61 
     62     private static final String LOG_TAG = "DeskClock";
     63 
     64     // Alarm action for midnight (so we can update the date display).
     65     private static final String KEY_SELECTED_TAB = "selected_tab";
     66     private static final String KEY_CLOCK_STATE = "clock_state";
     67 
     68     public static final String SELECT_TAB_INTENT_EXTRA = "deskclock.select.tab";
     69 
     70     private ActionBar mActionBar;
     71     private Tab mTimerTab;
     72     private Tab mClockTab;
     73     private Tab mStopwatchTab;
     74 
     75     private ViewPager mViewPager;
     76     private TabsAdapter mTabsAdapter;
     77 
     78     public static final int TIMER_TAB_INDEX = 0;
     79     public static final int CLOCK_TAB_INDEX = 1;
     80     public static final int STOPWATCH_TAB_INDEX = 2;
     81     // Tabs indices are switched for right-to-left since there is no
     82     // native support for RTL in the ViewPager.
     83     public static final int RTL_TIMER_TAB_INDEX = 2;
     84     public static final int RTL_CLOCK_TAB_INDEX = 1;
     85     public static final int RTL_STOPWATCH_TAB_INDEX = 0;
     86 
     87     private int mSelectedTab;
     88 
     89     @Override
     90     public void onNewIntent(Intent newIntent) {
     91         super.onNewIntent(newIntent);
     92         if (DEBUG) Log.d(LOG_TAG, "onNewIntent with intent: " + newIntent);
     93 
     94         // update our intent so that we can consult it to determine whether or
     95         // not the most recent launch was via a dock event
     96         setIntent(newIntent);
     97 
     98         // Timer receiver may ask to go to the timers fragment if a timer expired.
     99         int tab = newIntent.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
    100         if (tab != -1) {
    101             if (mActionBar != null) {
    102                 mActionBar.setSelectedNavigationItem(tab);
    103             }
    104         }
    105     }
    106 
    107     private void initViews() {
    108 
    109         if (mTabsAdapter == null) {
    110             mViewPager = new ViewPager(this);
    111             mViewPager.setId(R.id.desk_clock_pager);
    112             mTabsAdapter = new TabsAdapter(this, mViewPager);
    113             createTabs(mSelectedTab);
    114         }
    115         setContentView(mViewPager);
    116         mActionBar.setSelectedNavigationItem(mSelectedTab);
    117     }
    118 
    119     private void createTabs(int selectedIndex) {
    120         mActionBar = getActionBar();
    121 
    122         mActionBar.setDisplayOptions(0);
    123         if (mActionBar != null) {
    124             mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    125             mTimerTab = mActionBar.newTab();
    126             mTimerTab.setIcon(R.drawable.timer_tab);
    127             mTimerTab.setContentDescription(R.string.menu_timer);
    128             mTabsAdapter.addTab(mTimerTab, TimerFragment.class,TIMER_TAB_INDEX);
    129 
    130             mClockTab = mActionBar.newTab();
    131             mClockTab.setIcon(R.drawable.clock_tab);
    132             mClockTab.setContentDescription(R.string.menu_clock);
    133             mTabsAdapter.addTab(mClockTab, ClockFragment.class,CLOCK_TAB_INDEX);
    134             mStopwatchTab = mActionBar.newTab();
    135             mStopwatchTab.setIcon(R.drawable.stopwatch_tab);
    136             mStopwatchTab.setContentDescription(R.string.menu_stopwatch);
    137             mTabsAdapter.addTab(mStopwatchTab, StopwatchFragment.class,STOPWATCH_TAB_INDEX);
    138             mActionBar.setSelectedNavigationItem(selectedIndex);
    139             mTabsAdapter.notifySelectedPage(selectedIndex);
    140         }
    141     }
    142 
    143     @Override
    144     protected void onCreate(Bundle icicle) {
    145         super.onCreate(icicle);
    146 
    147         mSelectedTab = CLOCK_TAB_INDEX;
    148         if (icicle != null) {
    149             mSelectedTab = icicle.getInt(KEY_SELECTED_TAB, CLOCK_TAB_INDEX);
    150         }
    151 
    152         // Timer receiver may ask the app to go to the timer fragment if a timer expired
    153         Intent i = getIntent();
    154         if (i != null) {
    155             int tab = i.getIntExtra(SELECT_TAB_INTENT_EXTRA, -1);
    156             if (tab != -1) {
    157                 mSelectedTab = tab;
    158             }
    159         }
    160         initViews();
    161         setHomeTimeZone();
    162     }
    163 
    164     @Override
    165     protected void onResume() {
    166         super.onResume();
    167 
    168         Intent stopwatchIntent = new Intent(getApplicationContext(), StopwatchService.class);
    169         stopwatchIntent.setAction(Stopwatches.KILL_NOTIF);
    170         startService(stopwatchIntent);
    171 
    172         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
    173         SharedPreferences.Editor editor = prefs.edit();
    174         editor.putBoolean(Timers.NOTIF_APP_OPEN, true);
    175         editor.apply();
    176         Intent timerIntent = new Intent();
    177         timerIntent.setAction(Timers.NOTIF_IN_USE_CANCEL);
    178         sendBroadcast(timerIntent);
    179     }
    180 
    181     @Override
    182     public void onPause() {
    183 
    184         Intent intent = new Intent(getApplicationContext(), StopwatchService.class);
    185         intent.setAction(Stopwatches.SHOW_NOTIF);
    186         startService(intent);
    187 
    188         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
    189         SharedPreferences.Editor editor = prefs.edit();
    190         editor.putBoolean(Timers.NOTIF_APP_OPEN, false);
    191         editor.apply();
    192         Utils.showInUseNotifications(this);
    193 
    194         super.onPause();
    195     }
    196 
    197     @Override
    198     protected void onSaveInstanceState(Bundle outState) {
    199         super.onSaveInstanceState(outState);
    200         outState.putInt(KEY_SELECTED_TAB, mActionBar.getSelectedNavigationIndex());
    201     }
    202 
    203     public void clockButtonsOnClick(View v) {
    204         if (v == null)
    205             return;
    206         switch (v.getId()) {
    207             case R.id.alarms_button:
    208                 startActivity(new Intent(this, AlarmClock.class));
    209                 break;
    210             case R.id.cities_button:
    211                 startActivity(new Intent(this, CitiesActivity.class));
    212                 break;
    213             case R.id.menu_button:
    214                 showMenu(v);
    215                 break;
    216             default:
    217                 break;
    218         }
    219     }
    220 
    221     private void showMenu(View v) {
    222         PopupMenu popupMenu = new PopupMenu(this, v);
    223         popupMenu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener () {
    224             @Override
    225             public boolean onMenuItemClick(MenuItem item) {
    226                 switch (item.getItemId()) {
    227                     case R.id.menu_item_settings:
    228                         startActivity(new Intent(DeskClock.this, SettingsActivity.class));
    229                         return true;
    230                     case R.id.menu_item_help:
    231                         Intent i = item.getIntent();
    232                         if (i != null) {
    233                             try {
    234                                 startActivity(i);
    235                             } catch (ActivityNotFoundException e) {
    236                                 // No activity found to match the intent - ignore
    237                             }
    238                         }
    239                         return true;
    240                     case R.id.menu_item_night_mode:
    241                         startActivity(new Intent(DeskClock.this, ScreensaverActivity.class));
    242                     default:
    243                         break;
    244                 }
    245                 return true;
    246             }
    247         });
    248         popupMenu.inflate(R.menu.desk_clock_menu);
    249 
    250         Menu menu = popupMenu.getMenu();
    251         MenuItem help = menu.findItem(R.id.menu_item_help);
    252         if (help != null) {
    253             Utils.prepareHelpMenuItem(this, help);
    254         }
    255         popupMenu.show();
    256     }
    257 
    258     /***
    259      * Insert the local time zone as the Home Time Zone if one is not set
    260      */
    261     private void setHomeTimeZone() {
    262         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
    263         String homeTimeZone = prefs.getString(SettingsActivity.KEY_HOME_TZ, "");
    264         if (!homeTimeZone.isEmpty()) {
    265         return;
    266         }
    267         homeTimeZone = TimeZone.getDefault().getID();
    268         SharedPreferences.Editor editor = prefs.edit();
    269         editor.putString(SettingsActivity.KEY_HOME_TZ, homeTimeZone);
    270         editor.apply();
    271         Log.v(LOG_TAG, "Setting home time zone to " + homeTimeZone);
    272     }
    273 
    274     public void registerPageChangedListener(DeskClockFragment frag) {
    275         if (mTabsAdapter != null) {
    276             mTabsAdapter.registerPageChangedListener(frag);
    277         }
    278     }
    279 
    280     public void unregisterPageChangedListener(DeskClockFragment frag) {
    281         if (mTabsAdapter != null) {
    282             mTabsAdapter.unregisterPageChangedListener(frag);
    283         }
    284     }
    285 
    286 
    287     /***
    288      * Adapter for wrapping together the ActionBar's tab with the ViewPager
    289      */
    290 
    291     private class TabsAdapter extends FragmentPagerAdapter
    292             implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
    293 
    294         private static final String KEY_TAB_POSITION = "tab_position";
    295 
    296         final class TabInfo {
    297             private final Class<?> clss;
    298             private final Bundle args;
    299 
    300             TabInfo(Class<?> _class, int position) {
    301                 clss = _class;
    302                 args = new Bundle();
    303                 args.putInt(KEY_TAB_POSITION, position);
    304             }
    305 
    306             public int getPosition() {
    307                 return args.getInt(KEY_TAB_POSITION, 0);
    308             }
    309         }
    310 
    311         private final ArrayList<TabInfo> mTabs = new ArrayList <TabInfo>();
    312         ActionBar mMainActionBar;
    313         Context mContext;
    314         ViewPager mPager;
    315         // Used for doing callbacks to fragments.
    316         HashSet<String> mFragmentTags = new HashSet<String>();
    317 
    318         public TabsAdapter(Activity activity, ViewPager pager) {
    319             super(activity.getFragmentManager());
    320             mContext = activity;
    321             mMainActionBar = activity.getActionBar();
    322             mPager = pager;
    323             mPager.setAdapter(this);
    324             mPager.setOnPageChangeListener(this);
    325         }
    326 
    327         @Override
    328         public Fragment getItem(int position) {
    329             TabInfo info = mTabs.get(getRtlPosition(position));
    330             DeskClockFragment f = (DeskClockFragment) Fragment.instantiate(
    331                     mContext, info.clss.getName(), info.args);
    332             return f;
    333         }
    334 
    335         @Override
    336         public int getCount() {
    337             return mTabs.size();
    338         }
    339 
    340         public void addTab(ActionBar.Tab tab, Class<?> clss, int position) {
    341             TabInfo info = new TabInfo(clss, position);
    342             tab.setTag(info);
    343             tab.setTabListener(this);
    344             mTabs.add(info);
    345             mMainActionBar.addTab(tab);
    346             notifyDataSetChanged();
    347         }
    348 
    349         @Override
    350         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
    351             // Do nothing
    352         }
    353 
    354         @Override
    355         public void onPageSelected(int position) {
    356             mMainActionBar.setSelectedNavigationItem(getRtlPosition(position));
    357             notifyPageChanged(position);
    358         }
    359 
    360         @Override
    361         public void onPageScrollStateChanged(int state) {
    362             // Do nothing
    363         }
    364 
    365         @Override
    366         public void onTabReselected(Tab arg0, FragmentTransaction arg1) {
    367             // Do nothing
    368         }
    369 
    370         @Override
    371         public void onTabSelected(Tab tab, FragmentTransaction ft) {
    372             TabInfo info = (TabInfo)tab.getTag();
    373             mPager.setCurrentItem(getRtlPosition(info.getPosition()));
    374         }
    375 
    376         @Override
    377         public void onTabUnselected(Tab arg0, FragmentTransaction arg1) {
    378             // Do nothing
    379 
    380         }
    381 
    382         public void notifySelectedPage(int page) {
    383             notifyPageChanged(page);
    384         }
    385 
    386         private void notifyPageChanged(int newPage) {
    387             for (String tag : mFragmentTags) {
    388                 final FragmentManager fm = getFragmentManager();
    389                 DeskClockFragment f = (DeskClockFragment) fm.findFragmentByTag(tag);
    390                 if (f != null) {
    391                     f.onPageChanged(newPage);
    392                 }
    393             }
    394         }
    395 
    396         public void registerPageChangedListener(DeskClockFragment frag) {
    397             String tag = frag.getTag();
    398             if (mFragmentTags.contains(tag)) {
    399                 Log.wtf(LOG_TAG, "Trying to add an existing fragment " + tag);
    400             } else {
    401                 mFragmentTags.add(frag.getTag());
    402             }
    403             // Since registering a listener by the fragment is done sometimes after the page
    404             // was already changed, make sure the fragment gets the current page
    405             frag.onPageChanged(mMainActionBar.getSelectedNavigationIndex());
    406         }
    407 
    408         public void unregisterPageChangedListener(DeskClockFragment frag) {
    409             mFragmentTags.remove(frag.getTag());
    410         }
    411 
    412         private boolean isRtl() {
    413             return TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) ==
    414                     View.LAYOUT_DIRECTION_RTL;
    415         }
    416 
    417         private int getRtlPosition(int position) {
    418              if (isRtl()) {
    419                  switch (position) {
    420                      case TIMER_TAB_INDEX:
    421                          return RTL_TIMER_TAB_INDEX;
    422                      case CLOCK_TAB_INDEX:
    423                          return RTL_CLOCK_TAB_INDEX;
    424                      case STOPWATCH_TAB_INDEX:
    425                          return RTL_STOPWATCH_TAB_INDEX;
    426                      default:
    427                          break;
    428                 }
    429             }
    430             return position;
    431         }
    432     }
    433 
    434     public static abstract class OnTapListener implements OnTouchListener {
    435         private float mLastTouchX;
    436         private float mLastTouchY;
    437         private long mLastTouchTime;
    438         private final TextView mMakePressedTextView;
    439         private final int mPressedColor, mGrayColor;
    440         private final float MAX_MOVEMENT_ALLOWED = 20;
    441         private final long MAX_TIME_ALLOWED = 500;
    442 
    443         public OnTapListener(Activity activity, TextView makePressedView) {
    444             mMakePressedTextView = makePressedView;
    445             mPressedColor = activity.getResources().getColor(Utils.getPressedColorId());
    446             mGrayColor = activity.getResources().getColor(Utils.getGrayColorId());
    447         }
    448 
    449         @Override
    450         public boolean onTouch(View v, MotionEvent e) {
    451             switch (e.getAction()) {
    452                 case (MotionEvent.ACTION_DOWN):
    453                     mLastTouchTime = Utils.getTimeNow();
    454                     mLastTouchX = e.getX();
    455                     mLastTouchY = e.getY();
    456                     if (mMakePressedTextView != null) {
    457                         mMakePressedTextView.setTextColor(mPressedColor);
    458                     }
    459                     break;
    460                 case (MotionEvent.ACTION_UP):
    461                     float xDiff = Math.abs(e.getX()-mLastTouchX);
    462                     float yDiff = Math.abs(e.getY()-mLastTouchY);
    463                     long timeDiff = (Utils.getTimeNow() - mLastTouchTime);
    464                     if (xDiff < MAX_MOVEMENT_ALLOWED && yDiff < MAX_MOVEMENT_ALLOWED
    465                             && timeDiff < MAX_TIME_ALLOWED) {
    466                         if (mMakePressedTextView != null) {
    467                             v = mMakePressedTextView;
    468                         }
    469                         processClick(v);
    470                         resetValues();
    471                         return true;
    472                     }
    473                     resetValues();
    474                     break;
    475                 case (MotionEvent.ACTION_MOVE):
    476                     xDiff = Math.abs(e.getX()-mLastTouchX);
    477                     yDiff = Math.abs(e.getY()-mLastTouchY);
    478                     if (xDiff >= MAX_MOVEMENT_ALLOWED || yDiff >= MAX_MOVEMENT_ALLOWED) {
    479                         resetValues();
    480                     }
    481                     break;
    482                 default:
    483                     resetValues();
    484             }
    485             return false;
    486         }
    487 
    488         private void resetValues() {
    489             mLastTouchX = -1*MAX_MOVEMENT_ALLOWED + 1;
    490             mLastTouchY = -1*MAX_MOVEMENT_ALLOWED + 1;
    491             mLastTouchTime = -1*MAX_TIME_ALLOWED + 1;
    492             if (mMakePressedTextView != null) {
    493                 mMakePressedTextView.setTextColor(mGrayColor);
    494             }
    495         }
    496 
    497         protected abstract void processClick(View v);
    498     }
    499 
    500     /** Called by the LabelDialogFormat class after the dialog is finished. **/
    501     @Override
    502     public void onDialogLabelSet(TimerObj timer, String label, String tag) {
    503         Fragment frag = getFragmentManager().findFragmentByTag(tag);
    504         if (frag instanceof TimerFragment) {
    505             ((TimerFragment) frag).setLabel(timer, label);
    506         }
    507     }
    508 }
    509