1/* 2 * Copyright (C) 2017 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 androidx.leanback.widget.picker; 18 19import static org.hamcrest.CoreMatchers.is; 20import static org.hamcrest.MatcherAssert.assertThat; 21import static org.junit.Assert.assertEquals; 22 23import android.content.Context; 24import android.content.Intent; 25import android.support.test.InstrumentationRegistry; 26import android.support.test.filters.LargeTest; 27import android.support.test.rule.ActivityTestRule; 28import android.support.test.runner.AndroidJUnit4; 29import android.view.KeyEvent; 30import android.view.View; 31import android.view.ViewGroup; 32 33import androidx.leanback.test.R; 34 35import org.junit.Before; 36import org.junit.Rule; 37import org.junit.Test; 38import org.junit.runner.RunWith; 39 40import java.util.Arrays; 41import java.util.List; 42 43@LargeTest 44@RunWith(AndroidJUnit4.class) 45public class TimePickerTest { 46 47 private static final String TAG = "TimePickerTest"; 48 private static final long TRANSITION_LENGTH = 1000; 49 private static final long UPDATE_LENGTH = 1000; 50 51 View mViewAbove; 52 TimePicker mTimePicker12HourView; 53 TimePicker mTimePicker24HourView; 54 View mViewBelow; 55 56 @Rule 57 public ActivityTestRule<TimePickerActivity> mActivityTestRule = 58 new ActivityTestRule<>(TimePickerActivity.class, false, false); 59 private TimePickerActivity mActivity; 60 private Context mContext; 61 62 @Before 63 public void setUp() throws Exception { 64 mContext = InstrumentationRegistry.getTargetContext(); 65 } 66 67 public void initActivity(Intent intent) throws Throwable { 68 mActivity = mActivityTestRule.launchActivity(intent); 69 mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 70 mTimePicker12HourView = (TimePicker) mActivity.findViewById(R.id.time_picker12); 71 mTimePicker12HourView.setActivatedVisibleItemCount(3); 72 mTimePicker12HourView.setOnClickListener(new View.OnClickListener() { 73 @Override 74 public void onClick(View v) { 75 mTimePicker12HourView.setActivated(!mTimePicker12HourView.isActivated()); 76 } 77 }); 78 79 if (intent.getIntExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 80 R.layout.timepicker_with_other_widgets) == R.layout.timepicker_with_other_widgets) { 81 mViewAbove = mActivity.findViewById(R.id.above_picker); 82 mViewBelow = mActivity.findViewById(R.id.below_picker); 83 mTimePicker24HourView = (TimePicker) mActivity.findViewById(R.id.time_picker24); 84 mTimePicker24HourView.setActivatedVisibleItemCount(3); 85 mTimePicker24HourView.setOnClickListener(new View.OnClickListener() { 86 @Override 87 public void onClick(View v) { 88 mTimePicker24HourView.setActivated(!mTimePicker24HourView.isActivated()); 89 } 90 }); 91 } else if (intent.getIntExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 92 R.layout.timepicker_with_other_widgets) == R.layout.timepicker_alone) { 93 // A layout with only a TimePicker widget that is initially activated. 94 mActivityTestRule.runOnUiThread(new Runnable() { 95 @Override 96 public void run() { 97 mTimePicker12HourView.setActivated(true); 98 } 99 }); 100 Thread.sleep(500); 101 } 102 } 103 104 @Test 105 public void testSetHourIn24hFormat() throws Throwable { 106 Intent intent = new Intent(); 107 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 108 R.layout.timepicker_with_other_widgets); 109 initActivity(intent); 110 111 mActivityTestRule.runOnUiThread(new Runnable() { 112 @Override 113 public void run() { 114 mTimePicker24HourView.setHour(0); 115 } 116 }); 117 Thread.sleep(UPDATE_LENGTH); 118 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 119 mTimePicker24HourView.getHour(), is(0)); 120 121 mActivityTestRule.runOnUiThread(new Runnable() { 122 @Override 123 public void run() { 124 mTimePicker24HourView.setHour(11); 125 } 126 }); 127 Thread.sleep(UPDATE_LENGTH); 128 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 129 mTimePicker24HourView.getHour(), is(11)); 130 131 mActivityTestRule.runOnUiThread(new Runnable() { 132 @Override 133 public void run() { 134 mTimePicker24HourView.setHour(12); 135 } 136 }); 137 Thread.sleep(UPDATE_LENGTH); 138 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 139 mTimePicker24HourView.getHour(), is(12)); 140 141 mActivityTestRule.runOnUiThread(new Runnable() { 142 @Override 143 public void run() { 144 mTimePicker24HourView.setHour(13); 145 } 146 }); 147 Thread.sleep(UPDATE_LENGTH); 148 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 149 mTimePicker24HourView.getHour(), is(13)); 150 151 mActivityTestRule.runOnUiThread(new Runnable() { 152 @Override 153 public void run() { 154 mTimePicker24HourView.setHour(23); 155 } 156 }); 157 Thread.sleep(UPDATE_LENGTH); 158 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 159 mTimePicker24HourView.getHour(), is(23)); 160 } 161 162 @Test 163 public void testSetHourIn12hFormat() throws Throwable { 164 Intent intent = new Intent(); 165 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 166 R.layout.timepicker_with_other_widgets); 167 initActivity(intent); 168 mActivityTestRule.runOnUiThread(new Runnable() { 169 @Override 170 public void run() { 171 mTimePicker12HourView.setHour(0); 172 } 173 }); 174 Thread.sleep(UPDATE_LENGTH); 175 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 176 mTimePicker12HourView.getHour(), is(0)); 177 178 mActivityTestRule.runOnUiThread(new Runnable() { 179 @Override 180 public void run() { 181 mTimePicker12HourView.setHour(11); 182 } 183 }); 184 Thread.sleep(UPDATE_LENGTH); 185 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 186 mTimePicker12HourView.getHour(), is(11)); 187 188 mActivityTestRule.runOnUiThread(new Runnable() { 189 @Override 190 public void run() { 191 mTimePicker12HourView.setHour(12); 192 } 193 }); 194 Thread.sleep(UPDATE_LENGTH); 195 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 196 mTimePicker12HourView.getHour(), is(12)); 197 198 mActivityTestRule.runOnUiThread(new Runnable() { 199 @Override 200 public void run() { 201 mTimePicker12HourView.setHour(13); 202 } 203 }); 204 Thread.sleep(UPDATE_LENGTH); 205 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 206 mTimePicker12HourView.getHour(), is(13)); 207 208 mActivityTestRule.runOnUiThread(new Runnable() { 209 @Override 210 public void run() { 211 mTimePicker12HourView.setHour(23); 212 } 213 }); 214 Thread.sleep(UPDATE_LENGTH); 215 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 216 mTimePicker12HourView.getHour(), is(23)); 217 } 218 219 @Test 220 public void testSetMinuteIn24hFormat() throws Throwable { 221 Intent intent = new Intent(); 222 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 223 R.layout.timepicker_with_other_widgets); 224 initActivity(intent); 225 mActivityTestRule.runOnUiThread(new Runnable() { 226 @Override 227 public void run() { 228 mTimePicker24HourView.setMinute(0); 229 } 230 }); 231 Thread.sleep(UPDATE_LENGTH); 232 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 233 mTimePicker24HourView.getMinute(), is(0)); 234 235 mActivityTestRule.runOnUiThread(new Runnable() { 236 @Override 237 public void run() { 238 mTimePicker24HourView.setMinute(11); 239 } 240 }); 241 Thread.sleep(UPDATE_LENGTH); 242 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 243 mTimePicker24HourView.getMinute(), is(11)); 244 245 mActivityTestRule.runOnUiThread(new Runnable() { 246 @Override 247 public void run() { 248 mTimePicker24HourView.setMinute(59); 249 } 250 }); 251 Thread.sleep(UPDATE_LENGTH); 252 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 253 mTimePicker24HourView.getMinute(), is(59)); 254 } 255 256 @Test 257 public void testSetMinuteIn12hFormat() throws Throwable { 258 Intent intent = new Intent(); 259 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 260 R.layout.timepicker_with_other_widgets); 261 initActivity(intent); 262 mActivityTestRule.runOnUiThread(new Runnable() { 263 @Override 264 public void run() { 265 mTimePicker12HourView.setMinute(0); 266 } 267 }); 268 Thread.sleep(UPDATE_LENGTH); 269 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 270 mTimePicker12HourView.getMinute(), is(0)); 271 272 mActivityTestRule.runOnUiThread(new Runnable() { 273 @Override 274 public void run() { 275 mTimePicker12HourView.setMinute(11); 276 } 277 }); 278 Thread.sleep(UPDATE_LENGTH); 279 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 280 mTimePicker12HourView.getMinute(), is(11)); 281 282 mActivityTestRule.runOnUiThread(new Runnable() { 283 @Override 284 public void run() { 285 mTimePicker12HourView.setMinute(59); 286 } 287 }); 288 Thread.sleep(UPDATE_LENGTH); 289 assertThat("TimePicker in 24-hour mode returns a different hour in getHour()", 290 mTimePicker12HourView.getMinute(), is(59)); 291 292 } 293 294 @Test 295 public void testAmToPmTransition() throws Throwable { 296 Intent intent = new Intent(); 297 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 298 R.layout.timepicker_with_other_widgets); 299 initActivity(intent); 300 mActivityTestRule.runOnUiThread(new Runnable() { 301 @Override 302 public void run() { 303 mTimePicker12HourView.setHour(0); 304 mTimePicker12HourView.setMinute(47); 305 } 306 }); 307 Thread.sleep(UPDATE_LENGTH); 308 assertThat("TimePicker in 12-hour mode returns a different hour in getHour()", 309 mTimePicker12HourView.getHour(), is(0)); 310 assertThat("TimePicker in 12-hour mode returns a different hour in getMinute()", 311 mTimePicker12HourView.getMinute(), is(47)); 312 313 // traverse to the AM/PM column of 12 hour TimePicker widget 314 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 315 Thread.sleep(TRANSITION_LENGTH); 316 // Click once to activate 317 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 318 Thread.sleep(TRANSITION_LENGTH); 319 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 320 Thread.sleep(TRANSITION_LENGTH); 321 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 322 Thread.sleep(TRANSITION_LENGTH); 323 // scroll down to PM value 324 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 325 Thread.sleep(TRANSITION_LENGTH); 326 // Click now to deactivate 327 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 328 Thread.sleep(TRANSITION_LENGTH); 329 330 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 331 mTimePicker12HourView.getHour(), is(12)); 332 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 333 mTimePicker12HourView.getMinute(), is(47)); 334 } 335 336 @Test 337 public void testPmToAmTransition() throws Throwable { 338 Intent intent = new Intent(); 339 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 340 R.layout.timepicker_with_other_widgets); 341 initActivity(intent); 342 mActivityTestRule.runOnUiThread(new Runnable() { 343 @Override 344 public void run() { 345 mTimePicker12HourView.setHour(12); 346 mTimePicker12HourView.setMinute(47); 347 } 348 }); 349 Thread.sleep(UPDATE_LENGTH); 350 assertThat("TimePicker in 12-hour mode returns a different hour in getHour()", 351 mTimePicker12HourView.getHour(), is(12)); 352 assertThat("TimePicker in 12-hour mode returns a different hour in getMinute()", 353 mTimePicker12HourView.getMinute(), is(47)); 354 355 // traverse to the AM/PM column of 12 hour TimePicker widget 356 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 357 Thread.sleep(TRANSITION_LENGTH); 358 // Click once to activate 359 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 360 Thread.sleep(TRANSITION_LENGTH); 361 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 362 Thread.sleep(TRANSITION_LENGTH); 363 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 364 Thread.sleep(TRANSITION_LENGTH); 365 // scroll down to PM value 366 sendKeys(KeyEvent.KEYCODE_DPAD_UP); 367 Thread.sleep(TRANSITION_LENGTH); 368 // Click now to deactivate 369 sendKeys(KeyEvent.KEYCODE_DPAD_CENTER); 370 Thread.sleep(TRANSITION_LENGTH); 371 372 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 373 mTimePicker12HourView.getHour(), is(0)); 374 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 375 mTimePicker12HourView.getMinute(), is(47)); 376 } 377 378 @Test 379 public void test12To24HourFormatTransition() throws Throwable { 380 Intent intent = new Intent(); 381 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 382 R.layout.timepicker_with_other_widgets); 383 initActivity(intent); 384 mActivityTestRule.runOnUiThread(new Runnable() { 385 @Override 386 public void run() { 387 mTimePicker12HourView.setHour(14); 388 mTimePicker12HourView.setMinute(47); 389 } 390 }); 391 Thread.sleep(UPDATE_LENGTH); 392 assertThat("TimePicker should be in 12-hour format.", mTimePicker12HourView.is24Hour(), 393 is(false)); 394 mActivityTestRule.runOnUiThread(new Runnable() { 395 @Override 396 public void run() { 397 mTimePicker12HourView.setIs24Hour(true); 398 } 399 }); 400 Thread.sleep(UPDATE_LENGTH); 401 assertThat("TimePicker should now be in 24-hour format.", mTimePicker12HourView.is24Hour(), 402 is(true)); 403 // The hour and minute should not be changed. 404 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 405 mTimePicker12HourView.getHour(), is(14)); 406 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 407 mTimePicker12HourView.getMinute(), is(47)); 408 } 409 410 @Test 411 public void test24To12HourFormatTransition() throws Throwable { 412 Intent intent = new Intent(); 413 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 414 R.layout.timepicker_with_other_widgets); 415 initActivity(intent); 416 mActivityTestRule.runOnUiThread(new Runnable() { 417 @Override 418 public void run() { 419 mTimePicker24HourView.setHour(14); 420 mTimePicker24HourView.setMinute(47); 421 } 422 }); 423 Thread.sleep(UPDATE_LENGTH); 424 assertThat("TimePicker should be in 12-hour format.", mTimePicker24HourView.is24Hour(), 425 is(true)); 426 mActivityTestRule.runOnUiThread(new Runnable() { 427 @Override 428 public void run() { 429 mTimePicker24HourView.setIs24Hour(false); 430 } 431 }); 432 Thread.sleep(UPDATE_LENGTH); 433 assertThat("TimePicker should now be in 24-hour format.", mTimePicker24HourView.is24Hour(), 434 is(false)); 435 // The hour and minute should not be changed. 436 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 437 mTimePicker24HourView.getHour(), is(14)); 438 assertThat("TimePicker in 24-hour mode returns a different hour in getHour() returns", 439 mTimePicker24HourView.getMinute(), is(47)); 440 } 441 442 @Test 443 public void testInitiallyActiveTimePicker() 444 throws Throwable { 445 Intent intent = new Intent(); 446 intent.putExtra(TimePickerActivity.EXTRA_LAYOUT_RESOURCE_ID, 447 R.layout.timepicker_alone); 448 initActivity(intent); 449 450 mActivityTestRule.runOnUiThread(new Runnable() { 451 @Override 452 public void run() { 453 mTimePicker12HourView.setHour(14); 454 mTimePicker12HourView.setMinute(47); 455 } 456 }); 457 Thread.sleep(UPDATE_LENGTH); 458 459 ViewGroup mTimePickerInnerView = (ViewGroup) mTimePicker12HourView.findViewById( 460 R.id.picker); 461 462 assertThat("The first column of TimePicker should initially hold focus", 463 mTimePickerInnerView.getChildAt(0).hasFocus(), is(true)); 464 465 // focus on first column 466 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 467 Thread.sleep(TRANSITION_LENGTH); 468 assertThat("The first column of TimePicker should still hold focus after scrolling down", 469 mTimePickerInnerView.getChildAt(0).hasFocus(), is(true)); 470 471 // focus on second column 472 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 473 Thread.sleep(TRANSITION_LENGTH); 474 assertThat("The second column of TimePicker should hold focus after scrolling right", 475 mTimePickerInnerView.getChildAt(2).hasFocus(), is(true)); 476 477 sendKeys(KeyEvent.KEYCODE_DPAD_DOWN); 478 Thread.sleep(TRANSITION_LENGTH); 479 assertThat("The second column of TimePicker should still hold focus after scrolling down", 480 mTimePickerInnerView.getChildAt(2).hasFocus(), is(true)); 481 482 // focus on third column 483 sendKeys(KeyEvent.KEYCODE_DPAD_RIGHT); 484 Thread.sleep(TRANSITION_LENGTH); 485 assertThat("The third column of TimePicker should hold focus after scrolling right", 486 mTimePickerInnerView.getChildAt(3).hasFocus(), is(true)); 487 488 sendKeys(KeyEvent.KEYCODE_DPAD_UP); 489 Thread.sleep(TRANSITION_LENGTH); 490 assertThat("The third column of TimePicker should still hold focus after scrolling down", 491 mTimePickerInnerView.getChildAt(3).hasFocus(), is(true)); 492 } 493 494 @Test 495 public void testExtractSeparatorsForDifferentLocales() throws Throwable { 496 // A typical time pattern for different locales in 12-hour format 497 TimePicker timePicker = new TimePicker(mContext, null) { 498 @Override 499 String getBestHourMinutePattern() { 500 return "h:mm a"; 501 } 502 }; 503 List<CharSequence> actualSeparators = timePicker.extractSeparators(); 504 List<String> expectedSeparators = Arrays.asList(new String[] {"", ":", "", ""}); 505 assertEquals(expectedSeparators, actualSeparators); 506 507 // time pattern for ja_JP in 12 hour format 508 timePicker = new TimePicker(mContext, null) { 509 @Override 510 String getBestHourMinutePattern() { 511 return "aK:mm"; 512 } 513 514 @Override 515 public boolean is24Hour() { 516 return false; 517 } 518 }; 519 actualSeparators = timePicker.extractSeparators(); 520 expectedSeparators = Arrays.asList(new String[] {"", "", ":", ""}); 521 assertEquals(expectedSeparators, actualSeparators); 522 523 // time pattern for fr_CA in 24 hour format 524 timePicker = new TimePicker(mContext, null) { 525 @Override 526 String getBestHourMinutePattern() { 527 return "HH 'h' mm"; 528 } 529 530 @Override 531 public boolean is24Hour() { 532 return true; 533 } 534 }; 535 actualSeparators = timePicker.extractSeparators(); 536 expectedSeparators = Arrays.asList(new String[] {"", "h", ""}); 537 assertEquals(expectedSeparators, actualSeparators); 538 539 // time pattern for hsb_DE in 24 hour format 540 timePicker = new TimePicker(mContext, null) { 541 @Override 542 String getBestHourMinutePattern() { 543 return "H:mm 'hodz'"; 544 } 545 546 @Override 547 public boolean is24Hour() { 548 return true; 549 } 550 }; 551 actualSeparators = timePicker.extractSeparators(); 552 expectedSeparators = Arrays.asList(new String[] {"", ":", "hodz"}); 553 assertEquals(expectedSeparators, actualSeparators); 554 555 // time pattern for ko_KR in 12 hour format 556 timePicker = new TimePicker(mContext, null) { 557 @Override 558 String getBestHourMinutePattern() { 559 return "a h:mm"; 560 } 561 562 @Override 563 public boolean is24Hour() { 564 return false; 565 } 566 }; 567 actualSeparators = timePicker.extractSeparators(); 568 expectedSeparators = Arrays.asList(new String[] {"", "", ":", ""}); 569 assertEquals(expectedSeparators, actualSeparators); 570 571 // time pattern for fa_IR in 24 hour format 572 timePicker = new TimePicker(mContext, null) { 573 @Override 574 String getBestHourMinutePattern() { 575 return "H:mm"; 576 } 577 578 @Override 579 public boolean is24Hour() { 580 return true; 581 } 582 }; 583 actualSeparators = timePicker.extractSeparators(); 584 expectedSeparators = Arrays.asList(new String[] {"", ":", ""}); 585 assertEquals(expectedSeparators, actualSeparators); 586 } 587 588 private void sendKeys(int ...keys) { 589 for (int i = 0; i < keys.length; i++) { 590 InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(keys[i]); 591 } 592 } 593} 594