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