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