Skip to content

Commit b21ddce

Browse files
iorgamgabrielArchaeopteryx
authored andcommitted
Bug 1992407 - Migrate the DatePicker to the material design and fix the colours. r=android-reviewers,aaronmt,calu
Differential Revision: https://phabricator.services.mozilla.com/D268528
1 parent 2b99e2c commit b21ddce

File tree

8 files changed

+204
-327
lines changed

8 files changed

+204
-327
lines changed

mobile/android/android-components/components/feature/prompts/src/main/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragment.kt

Lines changed: 147 additions & 146 deletions
Large diffs are not rendered by default.

mobile/android/android-components/components/feature/prompts/src/test/java/mozilla/components/feature/prompts/dialog/TimePickerDialogFragmentTest.kt

Lines changed: 2 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@
44

55
package mozilla.components.feature.prompts.dialog
66

7-
import android.app.AlertDialog
8-
import android.app.DatePickerDialog
9-
import android.app.TimePickerDialog
107
import android.content.DialogInterface.BUTTON_NEUTRAL
118
import android.content.DialogInterface.BUTTON_POSITIVE
129
import android.os.Looper.getMainLooper
1310
import android.widget.DatePicker
1411
import android.widget.NumberPicker
1512
import android.widget.TimePicker
13+
import androidx.appcompat.app.AlertDialog
1614
import androidx.test.ext.junit.runners.AndroidJUnit4
1715
import mozilla.components.feature.prompts.R
1816
import mozilla.components.feature.prompts.dialog.TimePickerDialogFragment.Companion.SELECTION_TYPE_DATE_AND_TIME
@@ -44,74 +42,12 @@ import java.util.Date
4442
@RunWith(AndroidJUnit4::class)
4543
class TimePickerDialogFragmentTest {
4644

47-
@Mock private lateinit var mockFeature: Prompter
48-
4945
@Before
5046
fun setup() {
47+
testContext.setTheme(com.google.android.material.R.style.Theme_MaterialComponents_Light)
5148
openMocks(this)
5249
}
5350

54-
@Test
55-
fun `build dialog`() {
56-
val initialDate = "2019-11-29".toDate("yyyy-MM-dd")
57-
val minDate = "2019-11-28".toDate("yyyy-MM-dd")
58-
val maxDate = "2019-11-30".toDate("yyyy-MM-dd")
59-
val fragment = spy(
60-
TimePickerDialogFragment.newInstance("sessionId", "uid", true, initialDate, minDate, maxDate),
61-
)
62-
63-
doReturn(appCompatContext).`when`(fragment).requireContext()
64-
65-
val dialog = fragment.onCreateDialog(null)
66-
dialog.show()
67-
68-
val datePicker = (dialog as DatePickerDialog).datePicker
69-
assertEquals("sessionId", fragment.sessionId)
70-
assertEquals("uid", fragment.promptRequestUID)
71-
assertEquals(2019, datePicker.year)
72-
assertEquals(11, datePicker.month + 1)
73-
assertEquals(29, datePicker.dayOfMonth)
74-
assertEquals(minDate, Date(datePicker.minDate))
75-
assertEquals(maxDate, Date(datePicker.maxDate))
76-
}
77-
78-
@Test
79-
fun `Clicking on positive, neutral and negative button notifies the feature`() {
80-
val initialDate = "2019-11-29".toDate("yyyy-MM-dd")
81-
val fragment = spy(
82-
TimePickerDialogFragment.newInstance("sessionId", "uid", false, initialDate, null, null),
83-
)
84-
fragment.feature = mockFeature
85-
86-
doReturn(appCompatContext).`when`(fragment).requireContext()
87-
88-
val dialog = fragment.onCreateDialog(null)
89-
dialog.show()
90-
91-
val positiveButton = (dialog as AlertDialog).getButton(BUTTON_POSITIVE)
92-
positiveButton.performClick()
93-
shadowOf(getMainLooper()).idle()
94-
95-
verify(mockFeature).onConfirm(eq("sessionId"), eq("uid"), any())
96-
97-
val neutralButton = dialog.getButton(BUTTON_NEUTRAL)
98-
neutralButton.performClick()
99-
shadowOf(getMainLooper()).idle()
100-
101-
verify(mockFeature).onClear("sessionId", "uid")
102-
}
103-
104-
@Test
105-
fun `touching outside of the dialog must notify the feature onCancel`() {
106-
val fragment = spy(
107-
TimePickerDialogFragment.newInstance("sessionId", "uid", true, Date(), null, null),
108-
)
109-
fragment.feature = mockFeature
110-
doReturn(testContext).`when`(fragment).requireContext()
111-
fragment.onCancel(mock())
112-
verify(mockFeature).onCancel("sessionId", "uid")
113-
}
114-
11551
@Test
11652
fun `onTimeChanged must update the selectedDate`() {
11753
val dialogPicker = TimePickerDialogFragment.newInstance("sessionId", "uid", false, Date(), null, null)
@@ -124,43 +60,6 @@ class TimePickerDialogFragmentTest {
12460
assertEquals(calendar.minutes, 12)
12561
}
12662

127-
@Test
128-
fun `building a date and time picker`() {
129-
val initialDate = "2018-06-12T19:30".toDate("yyyy-MM-dd'T'HH:mm")
130-
val minDate = "2018-06-07T00:00".toDate("yyyy-MM-dd'T'HH:mm")
131-
val maxDate = "2018-06-14T00:00".toDate("yyyy-MM-dd'T'HH:mm")
132-
val fragment = spy(
133-
TimePickerDialogFragment.newInstance(
134-
"sessionId",
135-
"uid",
136-
true,
137-
initialDate,
138-
minDate,
139-
maxDate,
140-
SELECTION_TYPE_DATE_AND_TIME,
141-
),
142-
)
143-
144-
doReturn(appCompatContext).`when`(fragment).requireContext()
145-
146-
val dialog = fragment.onCreateDialog(null)
147-
dialog.show()
148-
149-
val datePicker = dialog.findViewById<DatePicker>(R.id.date_picker)
150-
151-
assertEquals(2018, datePicker.year)
152-
assertEquals(6, datePicker.month + 1)
153-
assertEquals(12, datePicker.dayOfMonth)
154-
155-
assertEquals(minDate, Date(datePicker.minDate))
156-
assertEquals(maxDate, Date(datePicker.maxDate))
157-
158-
val timePicker = dialog.findViewById<TimePicker>(R.id.datetime_picker)
159-
160-
assertEquals(19, timePicker.hour)
161-
assertEquals(30, timePicker.minute)
162-
}
163-
16463
@Test
16564
fun `building a month picker`() {
16665
val initialDate = "2018-06".toDate("yyyy-MM")
@@ -207,30 +106,6 @@ class TimePickerDialogFragmentTest {
207106
assertEquals(7, selectedDate.month)
208107
}
209108

210-
@Test
211-
fun `building a time picker`() {
212-
val initialDate = "2018-06-12T19:30".toDate("yyyy-MM-dd'T'HH:mm")
213-
val minDate = "2018-06-07T00:00".toDate("yyyy-MM-dd'T'HH:mm")
214-
val maxDate = "2018-06-14T00:00".toDate("yyyy-MM-dd'T'HH:mm")
215-
val fragment = spy(
216-
TimePickerDialogFragment.newInstance(
217-
"sessionId",
218-
"uid",
219-
true,
220-
initialDate,
221-
minDate,
222-
maxDate,
223-
SELECTION_TYPE_TIME,
224-
),
225-
)
226-
227-
doReturn(appCompatContext).`when`(fragment).requireContext()
228-
229-
val dialog = fragment.onCreateDialog(null)
230-
dialog.show()
231-
assertTrue(dialog is TimePickerDialog)
232-
}
233-
234109
@Test(expected = IllegalArgumentException::class)
235110
fun `creating a TimePickerDialogFragment with an invalid type selection will throw an exception`() {
236111
val initialDate = "2018-06-12T19:30".toDate("yyyy-MM-dd'T'HH:mm")

mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/WebControlsTest.kt

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,10 @@ class WebControlsTest : TestSetup() {
5959
clickPageObject(itemWithResId("submitDate"))
6060
verifyNoDateIsSelected()
6161
clickPageObject(itemWithResId("calendar"))
62-
clickPageObject(itemWithDescription("$currentDay $currentMonth $currentYear"))
63-
clickPageObject(itemContainingText("OK"))
62+
clickPageObject(itemWithDescription("$currentMonth $currentDay"))
63+
clickPageObject(itemContainingText("Set"))
6464
clickPageObject(itemWithResId("submitDate"))
6565
verifySelectedDate()
66-
clickPageObject(itemWithResId("calendar"))
67-
clickPageObject(itemContainingText("CLEAR"))
68-
clickPageObject(itemWithResId("submitDate"))
69-
verifyNoDateIsSelected()
7066
}
7167
}
7268

@@ -78,18 +74,14 @@ class WebControlsTest : TestSetup() {
7874
navigationToolbar {
7975
}.enterURLAndEnterToBrowser(htmlControlsPage.url) {
8076
clickPageObject(itemWithResId("clock"))
81-
clickPageObject(itemContainingText("CANCEL"))
77+
clickPageObject(itemContainingText("Cancel"))
8278
clickPageObject(itemWithResId("submitTime"))
8379
verifyNoTimeIsSelected(hour, minute)
8480
clickPageObject(itemWithResId("clock"))
8581
selectTime(hour, minute)
8682
clickPageObject(itemContainingText("OK"))
8783
clickPageObject(itemWithResId("submitTime"))
8884
verifySelectedTime(hour, minute)
89-
clickPageObject(itemWithResId("clock"))
90-
clickPageObject(itemContainingText("CLEAR"))
91-
clickPageObject(itemWithResId("submitTime"))
92-
verifyNoTimeIsSelected(hour, minute)
9385
}
9486
}
9587

mobile/android/fenix/app/src/androidTest/java/org/mozilla/fenix/ui/robots/BrowserRobot.kt

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import android.content.Context
1010
import android.net.Uri
1111
import android.os.SystemClock
1212
import android.util.Log
13-
import android.widget.TimePicker
1413
import androidx.compose.ui.test.ExperimentalTestApi
1514
import androidx.compose.ui.test.assertAny
1615
import androidx.compose.ui.test.assertIsDisplayed
@@ -32,10 +31,8 @@ import androidx.test.espresso.Espresso.onView
3231
import androidx.test.espresso.action.ViewActions.click
3332
import androidx.test.espresso.action.ViewActions.longClick
3433
import androidx.test.espresso.assertion.ViewAssertions.matches
35-
import androidx.test.espresso.contrib.PickerActions
3634
import androidx.test.espresso.matcher.RootMatchers.isDialog
3735
import androidx.test.espresso.matcher.ViewMatchers.Visibility
38-
import androidx.test.espresso.matcher.ViewMatchers.isAssignableFrom
3936
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
4037
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
4138
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
@@ -770,11 +767,9 @@ class BrowserRobot {
770767

771768
fun selectTime(hour: Int, minute: Int) {
772769
Log.i(TAG, "selectTime: Trying to select time picker hour: $hour and minute: $minute")
773-
onView(
774-
isAssignableFrom(TimePicker::class.java),
775-
).inRoot(
776-
isDialog(),
777-
).perform(PickerActions.setTime(hour, minute))
770+
itemWithDescription("$hour o'clock").click()
771+
waitForAppWindowToBeUpdated()
772+
itemWithDescription("$minute minutes").click()
778773
Log.i(TAG, "selectTime: Selected time picker hour: $hour and minute: $minute")
779774
}
780775

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!-- This Source Code Form is subject to the terms of the Mozilla Public
3+
- License, v. 2.0. If a copy of the MPL was not distributed with this
4+
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
5+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
6+
<item android:color="?attr/colorSecondaryContainer" android:state_checked="true"/>
7+
</selector>

mobile/android/fenix/app/src/main/res/values/styles.xml

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616

1717
<style name="NormalThemeBase" parent="Theme.Material3.DayNight.NoActionBar">
1818
<item name="preferenceTheme">@style/PreferenceTheme</item>
19-
19+
<item name="materialTimePickerTheme">@style/Normal.MaterialTimePicker</item>
20+
<item name="materialCalendarTheme">@style/MaterialCalendar</item>
2021
<!-- Android system styling -->
2122
<item name="searchViewStyle">@style/SearchViewStyle</item>
2223
<item name="autoCompleteTextViewStyle">@style/AutoCompleteTextViewStyle</item>
23-
<item name="android:textAlignment">viewStart</item>
2424
<item name="android:windowContentTransitions">true</item>
25-
<item name="android:datePickerDialogTheme">@style/DatePickerDialogStyle</item>
2625
<item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
2726
<item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item>
2827
<item name="android:statusBarColor">@android:color/transparent</item>
@@ -200,32 +199,6 @@
200199
<item name="tabCounterTintColor">?attr/textPrimary</item>
201200
</style>
202201

203-
<!-- A theme to fix DatePickerDialogs year picker text alignment
204-
https://bugzilla.mozilla.org/show_bug.cgi?id=1986252 -->
205-
<style name="DatePickerDialogStyle" parent="ThemeOverlay.MaterialComponents.Dialog">
206-
<item name="android:textAlignment">gravity</item>
207-
<item name="android:windowBackground">?attr/layer1</item>
208-
<item name="android:colorEdgeEffect">@color/accent_normal_theme</item>
209-
<item name="android:colorAccent">@color/fx_mobile_text_color_primary</item>
210-
<item name="android:textColorPrimary">@color/state_list_text_color</item>
211-
<item name="android:textColorSecondary">@color/secondary_state_list_text_color</item>
212-
<item name="android:colorForeground">@color/toggle_off_track_normal_theme</item>
213-
</style>
214-
215-
<style name="PrivateDatePickerDialogStyle" parent="ThemeOverlay.MaterialComponents.Dialog">
216-
<item name="android:textAlignment">gravity</item>
217-
<!-- For some reason in private mode it was trying to load @drawable/?
218-
as the window background. This resolved to #424242. Hard coding
219-
this until we know what color we want private mode dialogs to be.
220-
see: https://bugzilla.mozilla.org/show_bug.cgi?id=1986252-->
221-
<item name="android:windowBackground">#424242</item>
222-
<item name="android:colorEdgeEffect">@color/accent_private_theme</item>
223-
<item name="android:colorAccent">@color/fx_mobile_private_text_color_primary</item>
224-
<item name="android:textColorPrimary">@color/state_list_text_color</item>
225-
<item name="android:textColorSecondary">@color/secondary_state_list_text_color</item>
226-
<item name="android:colorForeground">@color/toggle_off_track_dark_theme</item>
227-
</style>
228-
229202
<!-- A theme derived from the normal activity theme, but to look and behave like a dialog -->
230203
<style name="DialogActivityTheme" parent="NormalTheme">
231204
<item name="android:windowElevation">16dp</item>
@@ -261,6 +234,25 @@
261234
<item name="materialAlertDialogBodyTextStyle">@style/MaterialAlertDialog.App.Body.Text.Private</item>
262235
</style>
263236

237+
<style name="Normal.MaterialTimePicker" parent="ThemeOverlay.Material3.MaterialTimePicker">
238+
<item name="materialButtonOutlinedStyle">@style/My.Widget.MaterialComponents.TimePicker.Button</item>
239+
</style>
240+
241+
<style name="Private.MaterialTimePicker" parent="ThemeOverlay.Material3.MaterialTimePicker">
242+
<item name="materialClockStyle">@style/Private.MaterialTimePickerClock</item>
243+
<item name="materialButtonOutlinedStyle">@style/My.Widget.MaterialComponents.TimePicker.Button</item>
244+
</style>
245+
246+
<style name="My.Widget.MaterialComponents.TimePicker.Button" parent="Widget.Material3.MaterialTimePicker.Button">
247+
<item name="backgroundTint">@color/time_picker_button_background_tint</item>
248+
</style>
249+
250+
<style name="Private.MaterialTimePickerClock" parent="Widget.MaterialComponents.TimePicker.Clock">
251+
<item name="clockFaceBackgroundColor">?attr/colorSurfaceContainerHighest</item>
252+
<item name="clockHandColor">?attr/colorPrimary</item>
253+
<item name="clockNumberTextColor">?attr/colorOnSurface</item>
254+
</style>
255+
264256
<style name="MaterialAlertDialog.App.Body.Text.Private" parent="MaterialAlertDialog.App.Body.Text">
265257
<item name="android:textColor">@color/fx_mobile_private_text_color_primary</item>
266258
</style>
@@ -295,6 +287,23 @@
295287
<item name="android:gravity">center</item>
296288
</style>
297289

290+
<style name="MaterialCalendar" parent="ThemeOverlay.Material3.MaterialCalendar">
291+
<item name="materialCalendarHeaderDivider">@style/MaterialCalendar.HeaderDivider</item>
292+
<item name="materialCalendarHeaderTitle">@style/MaterialCalendar.Title.Text</item>
293+
<item name="buttonBarPositiveButtonStyle">@style/DialogButtonStyle</item>
294+
<item name="buttonBarNegativeButtonStyle">@style/DialogButtonStyle</item>
295+
</style>
296+
297+
<style name="MaterialCalendar.Title.Text" parent="@style/Widget.Material3.MaterialCalendar.HeaderTitle">
298+
<item name="android:textColor">?attr/colorOnSurface</item>
299+
<item name="android:titleTextStyle">?attr/textAppearanceHeadlineLarge</item>
300+
</style>
301+
302+
<style name="MaterialCalendar.HeaderDivider" parent="Widget.Material3.MaterialCalendar.HeaderDivider">
303+
<item name="android:visibility">visible</item>
304+
<item name="android:background">?attr/colorOutlineVariant</item>
305+
</style>
306+
298307
<style name="MaterialAlertDialogShapeAppearance" parent="">
299308
<item name="cornerFamily">rounded</item>
300309
<item name="cornerSize">@dimen/material_dialog_corner_radius</item>
@@ -308,13 +317,13 @@
308317

309318
<style name="PrivateThemeBase" parent="Theme.Material3.DayNight.NoActionBar">
310319
<!-- Android system styling -->
320+
<item name="materialTimePickerTheme">@style/Private.MaterialTimePicker</item>
321+
<item name="materialCalendarTheme">@style/MaterialCalendar</item>
311322
<item name="searchViewStyle">@style/SearchViewStyle</item>
312323
<item name="android:textColorLink">@color/fx_mobile_private_text_color_accent</item>
313324
<item name="preferenceTheme">@style/PreferenceTheme</item>
314325
<item name="checkboxStyle">@style/App.Widget.CompoundButton.CheckBox</item>
315326
<item name="autoCompleteTextViewStyle">@style/AutoCompleteTextViewStyle</item>
316-
<item name="android:textAlignment">viewStart</item>
317-
<item name="android:datePickerDialogTheme">@style/PrivateDatePickerDialogStyle</item>
318327
<item name="android:windowContentTransitions">true</item>
319328
<item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
320329
<item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item>

mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/WebControlsTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ class WebControlsTest : TestSetup() {
169169
progressBar.waitUntilGone(waitingTime)
170170
clickCalendarForm()
171171
selectDate()
172-
clickButtonWithText("OK")
172+
clickButtonWithText("Set")
173173
clickSubmitDateButton()
174174
verifySelectedDate()
175175
}

mobile/android/focus-android/app/src/androidTest/java/org/mozilla/focus/activity/robots/BrowserRobot.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,12 +291,10 @@ class BrowserRobot {
291291
fun clickCalendarForm() = clickPageObject(webPageItemWithResourceId("calendar"))
292292

293293
fun selectDate() {
294-
mDevice.findObject(UiSelector().resourceId("android:id/month_view")).waitForExists(waitingTime)
295-
296294
mDevice.findObject(
297295
UiSelector()
298296
.textContains("$currentDay")
299-
.descriptionContains("$currentDay $currentMonth $currentYear"),
297+
.descriptionContains("$currentMonth $currentDay"),
300298
).click()
301299
}
302300

0 commit comments

Comments
 (0)