1 /* 2 * Copyright (C) 2016 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 android.widget.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertFalse; 21 import static org.junit.Assert.assertTrue; 22 import static org.junit.Assert.fail; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.times; 25 import static org.mockito.Mockito.verify; 26 import static org.mockito.Mockito.verifyNoMoreInteractions; 27 import static org.mockito.Mockito.verifyZeroInteractions; 28 29 import android.app.Instrumentation; 30 import android.graphics.Rect; 31 import android.view.ViewGroup; 32 import android.widget.CalendarView; 33 import android.widget.ScrollView; 34 import android.widget.cts.util.TestUtils; 35 36 import androidx.annotation.ColorInt; 37 import androidx.test.InstrumentationRegistry; 38 import androidx.test.annotation.UiThreadTest; 39 import androidx.test.filters.MediumTest; 40 import androidx.test.rule.ActivityTestRule; 41 import androidx.test.runner.AndroidJUnit4; 42 43 import com.android.compatibility.common.util.CtsTouchUtils; 44 import com.android.compatibility.common.util.WidgetTestUtils; 45 46 import org.junit.Before; 47 import org.junit.Rule; 48 import org.junit.Test; 49 import org.junit.runner.RunWith; 50 51 import java.util.Calendar; 52 import java.util.GregorianCalendar; 53 54 @MediumTest 55 @RunWith(AndroidJUnit4.class) 56 public class CalendarViewTest { 57 private Instrumentation mInstrumentation; 58 private CalendarViewCtsActivity mActivity; 59 private CalendarView mCalendarViewMaterial; 60 private CalendarView mCalendarViewHolo; 61 62 @Rule 63 public ActivityTestRule<CalendarViewCtsActivity> mActivityRule = 64 new ActivityTestRule<>(CalendarViewCtsActivity.class); 65 66 @UiThreadTest 67 @Before 68 public void setup() { 69 mInstrumentation = InstrumentationRegistry.getInstrumentation(); 70 mActivity = mActivityRule.getActivity(); 71 mCalendarViewMaterial = (CalendarView) mActivity.findViewById(R.id.calendar_view_material); 72 mCalendarViewHolo = (CalendarView) mActivity.findViewById(R.id.calendar_view_holoyolo); 73 74 // Initialize both calendar views to the current date 75 final long currentDate = new GregorianCalendar().getTime().getTime(); 76 mCalendarViewMaterial.setDate(currentDate); 77 mCalendarViewHolo.setDate(currentDate); 78 } 79 80 @UiThreadTest 81 @Test 82 public void testConstructor() { 83 new CalendarView(mActivity); 84 85 new CalendarView(mActivity, null); 86 87 new CalendarView(mActivity, null, android.R.attr.calendarViewStyle); 88 89 new CalendarView(mActivity, null, 0, android.R.style.Widget_DeviceDefault_CalendarView); 90 91 new CalendarView(mActivity, null, 0, 92 android.R.style.Widget_DeviceDefault_Light_CalendarView); 93 94 new CalendarView(mActivity, null, 0, android.R.style.Widget_Material_CalendarView); 95 96 new CalendarView(mActivity, null, 0, android.R.style.Widget_Material_Light_CalendarView); 97 } 98 99 @UiThreadTest 100 @Test 101 public void testAccessDate() { 102 // Go back one year 103 final Calendar newCalendar = new GregorianCalendar(); 104 newCalendar.set(Calendar.YEAR, newCalendar.get(Calendar.YEAR) - 1); 105 final long yearAgoDate = newCalendar.getTime().getTime(); 106 107 mCalendarViewMaterial.setDate(yearAgoDate); 108 assertEquals(yearAgoDate, mCalendarViewMaterial.getDate()); 109 110 // Go forward two years (one year from current date in aggregate) 111 newCalendar.set(Calendar.YEAR, newCalendar.get(Calendar.YEAR) + 2); 112 final long yearHenceDate = newCalendar.getTime().getTime(); 113 114 mCalendarViewMaterial.setDate(yearHenceDate, true, false); 115 assertEquals(yearHenceDate, mCalendarViewMaterial.getDate()); 116 } 117 118 @UiThreadTest 119 @Test 120 public void testAccessMinMaxDate() { 121 // Use a range of minus/plus one year as min/max dates 122 final Calendar minCalendar = new GregorianCalendar(); 123 minCalendar.set(Calendar.YEAR, minCalendar.get(Calendar.YEAR) - 1); 124 final Calendar maxCalendar = new GregorianCalendar(); 125 maxCalendar.set(Calendar.YEAR, maxCalendar.get(Calendar.YEAR) + 1); 126 127 final long minDate = minCalendar.getTime().getTime(); 128 final long maxDate = maxCalendar.getTime().getTime(); 129 130 mCalendarViewMaterial.setMinDate(minDate); 131 mCalendarViewMaterial.setMaxDate(maxDate); 132 133 assertEquals(mCalendarViewMaterial.getMinDate(), minDate); 134 assertEquals(mCalendarViewMaterial.getMaxDate(), maxDate); 135 } 136 137 @UiThreadTest 138 @Test 139 public void testMinMaxRangeRestrictionsHolo() { 140 // Use a range of minus/plus one year as min/max dates. 141 final Calendar minCalendar = new GregorianCalendar(); 142 minCalendar.set(Calendar.YEAR, minCalendar.get(Calendar.YEAR) - 1); 143 final Calendar maxCalendar = new GregorianCalendar(); 144 maxCalendar.set(Calendar.YEAR, maxCalendar.get(Calendar.YEAR) + 1); 145 final long minDate = minCalendar.getTime().getTime(); 146 final long maxDate = maxCalendar.getTime().getTime(); 147 148 mCalendarViewHolo.setMinDate(minDate); 149 mCalendarViewHolo.setMaxDate(maxDate); 150 151 try { 152 mCalendarViewHolo.setDate(minDate - 1); 153 fail("Should throw IllegalArgumentException, date is before minDate"); 154 } catch (IllegalArgumentException e) { 155 } 156 157 try { 158 mCalendarViewHolo.setDate(maxDate + 1); 159 fail("Should throw IllegalArgumentException, date is after maxDate"); 160 } catch (IllegalArgumentException e) { 161 } 162 } 163 164 @UiThreadTest 165 @Test 166 public void testMinMaxRangeClampingMaterial() { 167 // Use a range of minus/plus one year as min/max dates. 168 final Calendar minCalendar = new GregorianCalendar(); 169 minCalendar.set(Calendar.YEAR, minCalendar.get(Calendar.YEAR) - 1); 170 final Calendar maxCalendar = new GregorianCalendar(); 171 maxCalendar.set(Calendar.YEAR, maxCalendar.get(Calendar.YEAR) + 1); 172 final long minDate = minCalendar.getTime().getTime(); 173 final long maxDate = maxCalendar.getTime().getTime(); 174 175 mCalendarViewMaterial.setMinDate(minDate); 176 mCalendarViewMaterial.setMaxDate(maxDate); 177 mCalendarViewMaterial.setDate(minDate + 1); 178 179 // Expecting to get back the same values because minDate < date < maxDate. 180 assertEquals(minDate, mCalendarViewMaterial.getMinDate()); 181 assertEquals(maxDate, mCalendarViewMaterial.getMaxDate()); 182 assertEquals(minDate + 1, mCalendarViewMaterial.getDate()); 183 184 // Set a new minDate after the current date. 185 mCalendarViewMaterial.setMinDate(minDate + 20); 186 assertEquals(minDate + 20, mCalendarViewMaterial.getMinDate()); 187 assertEquals(maxDate, mCalendarViewMaterial.getMaxDate()); 188 // Verify date clamping when calling setMinDate(). 189 assertEquals(minDate + 20, mCalendarViewMaterial.getDate()); 190 191 mCalendarViewMaterial.setMinDate(minDate); 192 // Set a maxDate before the current date. 193 mCalendarViewMaterial.setMaxDate(minDate + 10); 194 assertEquals(minDate, mCalendarViewMaterial.getMinDate()); 195 assertEquals(minDate + 10, mCalendarViewMaterial.getMaxDate()); 196 // Verify date clamping when calling setMaxDate(). 197 assertEquals(minDate + 10, mCalendarViewMaterial.getDate()); 198 199 // Set a date before minDate. 200 mCalendarViewMaterial.setDate(mCalendarViewMaterial.getMinDate() - 1); 201 // Expect date clamped to minDate. 202 assertEquals(mCalendarViewMaterial.getMinDate(), mCalendarViewMaterial.getDate()); 203 204 // Set a date after maxDate. 205 mCalendarViewMaterial.setDate(mCalendarViewMaterial.getMaxDate() + 1); 206 // Expect date clamped to maxDate. 207 assertEquals(mCalendarViewMaterial.getMaxDate(), mCalendarViewMaterial.getDate()); 208 } 209 210 private void verifyOnDateChangeListener(CalendarView calendarView, 211 boolean onlyAllowOneChangeEvent) throws Throwable { 212 final CalendarView.OnDateChangeListener mockDateChangeListener = 213 mock(CalendarView.OnDateChangeListener.class); 214 calendarView.setOnDateChangeListener(mockDateChangeListener); 215 216 // Go back to September 2008 217 final Calendar calendar = new GregorianCalendar(); 218 calendar.set(Calendar.YEAR, 2008); 219 calendar.set(Calendar.MONTH, Calendar.SEPTEMBER); 220 calendar.set(Calendar.DAY_OF_MONTH, 16); 221 WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, calendarView, 222 () -> calendarView.setDate(calendar.getTime().getTime(), false, true)); 223 224 // Get bounds of 09/07/2008 225 calendar.set(Calendar.DAY_OF_MONTH, 7); 226 final Rect dayBounds = new Rect(); 227 final boolean getDayBoundsSuccess = calendarView.getBoundsForDate( 228 calendar.getTime().getTime(), dayBounds); 229 assertTrue(getDayBoundsSuccess); 230 231 if (onlyAllowOneChangeEvent) { 232 verifyZeroInteractions(mockDateChangeListener); 233 } 234 235 // Use instrumentation to emulate a tap on 09/07/2008 236 CtsTouchUtils.emulateTapOnView(mInstrumentation, mActivityRule, calendarView, 237 dayBounds.left + dayBounds.width() / 2, 238 dayBounds.top + dayBounds.height() / 2); 239 240 verify(mockDateChangeListener, times(1)).onSelectedDayChange(calendarView, 241 2008, Calendar.SEPTEMBER, 7); 242 if (onlyAllowOneChangeEvent) { 243 verifyNoMoreInteractions(mockDateChangeListener); 244 } 245 } 246 247 @Test 248 public void testOnDateChangeListenerHolo() throws Throwable { 249 // Scroll the Holo calendar view all the way up so it's fully visible 250 final ScrollView scroller = (ScrollView) mActivity.findViewById(R.id.scroller); 251 final ViewGroup container = (ViewGroup) scroller.findViewById(R.id.container); 252 253 mActivityRule.runOnUiThread(() -> scroller.scrollTo(0, container.getHeight())); 254 // Note that in pre-Material world we are "allowing" the CalendarView to notify 255 // the date change listener on multiple occasions. This is the old behavior of the widget. 256 verifyOnDateChangeListener(mCalendarViewHolo, false); 257 } 258 259 @Test 260 public void testOnDateChangeListenerMaterial() throws Throwable { 261 // Note that in Material world only "real" date change events are allowed to be reported 262 // to our listener. This is the new behavior of the widget. 263 verifyOnDateChangeListener(mCalendarViewMaterial, true); 264 } 265 266 @UiThreadTest 267 @Test 268 public void testAppearanceMaterial() { 269 // The logic in this method is performed on a Material-styled CalendarView and 270 // non-deprecated attributes / visual appearance APIs 271 272 // Test the initial appearance defined in the layout XML 273 assertEquals(2, mCalendarViewMaterial.getFirstDayOfWeek()); 274 assertEquals(R.style.TextAppearance_WithColor, 275 mCalendarViewMaterial.getDateTextAppearance()); 276 assertEquals(R.style.TextAppearance_WithColorGreen, 277 mCalendarViewMaterial.getWeekDayTextAppearance()); 278 279 // Change the visual appearance of the widget 280 mCalendarViewMaterial.setFirstDayOfWeek(Calendar.TUESDAY); 281 mCalendarViewMaterial.setDateTextAppearance(R.style.TextAppearance_WithColorBlue); 282 mCalendarViewMaterial.setWeekDayTextAppearance(R.style.TextAppearance_WithColorMagenta); 283 284 assertEquals(Calendar.TUESDAY, mCalendarViewMaterial.getFirstDayOfWeek()); 285 assertEquals(R.style.TextAppearance_WithColorBlue, 286 mCalendarViewMaterial.getDateTextAppearance()); 287 assertEquals(R.style.TextAppearance_WithColorMagenta, 288 mCalendarViewMaterial.getWeekDayTextAppearance()); 289 } 290 291 @UiThreadTest 292 @Test 293 public void testAppearanceHolo() { 294 // All the logic in this method is performed on a Holo-styled CalendarView, as 295 // under Material design we are ignoring most of these decorative attributes 296 297 // Test the initial appearance defined in the layout XML 298 assertEquals(3, mCalendarViewHolo.getFirstDayOfWeek()); 299 assertEquals(5, mCalendarViewHolo.getShownWeekCount()); 300 assertFalse(mCalendarViewHolo.getShowWeekNumber()); 301 assertEquals(R.style.TextAppearance_WithColor, 302 mCalendarViewHolo.getDateTextAppearance()); 303 assertEquals(R.style.TextAppearance_WithColorGreen, 304 mCalendarViewHolo.getWeekDayTextAppearance()); 305 assertEquals(mActivity.getColor(R.color.calendarview_week_background), 306 mCalendarViewHolo.getSelectedWeekBackgroundColor()); 307 assertEquals(mActivity.getColor(R.color.calendarview_focusedmonthdate), 308 mCalendarViewHolo.getFocusedMonthDateColor()); 309 assertEquals(mActivity.getColor(R.color.calendarview_unfocusedmonthdate), 310 mCalendarViewHolo.getUnfocusedMonthDateColor()); 311 TestUtils.assertAllPixelsOfColor("Selected date vertical bar blue", 312 mCalendarViewHolo.getSelectedDateVerticalBar(), 40, 40, true, 0xFF0000FF, 1, true); 313 314 // Change the visual appearance of the widget 315 final @ColorInt int newSelectedWeekBackgroundColor = 316 mActivity.getColor(R.color.calendarview_week_background_new); 317 final @ColorInt int newFocusedMonthDateColor = 318 mActivity.getColor(R.color.calendarview_focusedmonthdate_new); 319 final @ColorInt int newUnfocusedMonthDataColor = 320 mActivity.getColor(R.color.calendarview_unfocusedmonthdate_new); 321 final @ColorInt int newWeekNumberColor = 322 mActivity.getColor(R.color.calendarview_week_number_new); 323 final @ColorInt int newWeekSeparatorLineColor = 324 mActivity.getColor(R.color.calendarview_week_separatorline_new); 325 326 mCalendarViewHolo.setFirstDayOfWeek(Calendar.SUNDAY); 327 mCalendarViewHolo.setShownWeekCount(4); 328 mCalendarViewHolo.setShowWeekNumber(true); 329 mCalendarViewHolo.setDateTextAppearance(R.style.TextAppearance_WithColorBlue); 330 mCalendarViewHolo.setWeekDayTextAppearance(R.style.TextAppearance_WithColorMagenta); 331 mCalendarViewHolo.setSelectedWeekBackgroundColor(newSelectedWeekBackgroundColor); 332 mCalendarViewHolo.setFocusedMonthDateColor(newFocusedMonthDateColor); 333 mCalendarViewHolo.setUnfocusedMonthDateColor(newUnfocusedMonthDataColor); 334 mCalendarViewHolo.setWeekNumberColor(newWeekNumberColor); 335 mCalendarViewHolo.setWeekSeparatorLineColor(newWeekSeparatorLineColor); 336 337 assertEquals(Calendar.SUNDAY, mCalendarViewHolo.getFirstDayOfWeek()); 338 assertEquals(4, mCalendarViewHolo.getShownWeekCount()); 339 assertTrue(mCalendarViewHolo.getShowWeekNumber()); 340 assertEquals(R.style.TextAppearance_WithColorBlue, 341 mCalendarViewHolo.getDateTextAppearance()); 342 assertEquals(R.style.TextAppearance_WithColorMagenta, 343 mCalendarViewHolo.getWeekDayTextAppearance()); 344 assertEquals(newSelectedWeekBackgroundColor, 345 mCalendarViewHolo.getSelectedWeekBackgroundColor()); 346 assertEquals(newFocusedMonthDateColor, 347 mCalendarViewHolo.getFocusedMonthDateColor()); 348 assertEquals(newUnfocusedMonthDataColor, 349 mCalendarViewHolo.getUnfocusedMonthDateColor()); 350 assertEquals(newWeekNumberColor, 351 mCalendarViewHolo.getWeekNumberColor()); 352 assertEquals(newWeekSeparatorLineColor, 353 mCalendarViewHolo.getWeekSeparatorLineColor()); 354 355 mCalendarViewHolo.setSelectedDateVerticalBar(R.drawable.yellow_fill); 356 TestUtils.assertAllPixelsOfColor("Selected date vertical bar yellow", 357 mCalendarViewHolo.getSelectedDateVerticalBar(), 40, 40, true, 0xFFFFFF00, 1, true); 358 359 mCalendarViewHolo.setSelectedDateVerticalBar( 360 mActivity.getDrawable(R.drawable.magenta_fill)); 361 TestUtils.assertAllPixelsOfColor("Selected date vertical bar magenta", 362 mCalendarViewHolo.getSelectedDateVerticalBar(), 40, 40, true, 0xFFFF00FF, 1, true); 363 } 364 } 365