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