Skip to content

Commit

Permalink
[MaterialTimePicker] Fix NumberFormatException
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 482810027
  • Loading branch information
paulfthomas committed Oct 21, 2022
1 parent 8510596 commit 0c204b8
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 13 deletions.
Expand Up @@ -18,6 +18,7 @@

import com.google.android.material.R;

import static android.text.TextUtils.isEmpty;
import static com.google.android.material.timepicker.TimePickerView.GENERIC_VIEW_ACCESSIBILITY_CLASS_NAME;

import android.content.Context;
Expand All @@ -27,7 +28,6 @@
import android.os.LocaleList;
import android.text.Editable;
import android.text.InputFilter;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.LayoutInflater;
Expand All @@ -37,6 +37,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.view.AccessibilityDelegateCompat;
import androidx.core.view.ViewCompat;
import com.google.android.material.chip.Chip;
Expand Down Expand Up @@ -117,13 +118,18 @@ public void toggle() {
public void setText(CharSequence text) {
String formattedText = formatText(text);
chip.setText(formattedText);
if (!TextUtils.isEmpty(formattedText)) {
if (!isEmpty(formattedText)) {
editText.removeTextChangedListener(watcher);
editText.setText(formattedText);
editText.addTextChangedListener(watcher);
}
}

@VisibleForTesting
CharSequence getChipText() {
return chip.getText();
}

private String formatText(CharSequence text) {
return TimeModel.formatText(getResources(), text);
}
Expand Down Expand Up @@ -167,12 +173,12 @@ private class TextFormatter extends TextWatcherAdapter {

@Override
public void afterTextChanged(Editable editable) {
if (TextUtils.isEmpty(editable)) {
if (isEmpty(editable)) {
chip.setText(formatText(DEFAULT_TEXT));
return;
}

chip.setText(formatText(editable));
String formattedText = formatText(editable);
chip.setText(isEmpty(formattedText) ? formatText(DEFAULT_TEXT) : formattedText);
}
}

Expand Down
13 changes: 9 additions & 4 deletions lib/java/com/google/android/material/timepicker/TimeModel.java
Expand Up @@ -28,6 +28,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.IntRange;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import com.google.android.material.timepicker.TimePickerControls.ActiveSelection;
import com.google.android.material.timepicker.TimePickerControls.ClockPeriod;
Expand Down Expand Up @@ -186,14 +187,18 @@ public void setPeriod(@ClockPeriod int period) {
}
}

@Nullable
public static String formatText(Resources resources, CharSequence text) {
return formatText(resources, text, ZERO_LEADING_NUMBER_FORMAT);
}

@Nullable
public static String formatText(Resources resources, CharSequence text, String format) {
return String.format(
resources.getConfiguration().locale,
format,
Integer.parseInt(String.valueOf(text)));
try {
return String.format(
resources.getConfiguration().locale, format, Integer.parseInt(String.valueOf(text)));
} catch (NumberFormatException e) {
return null;
}
}
}
Expand Up @@ -22,6 +22,7 @@
import static com.google.android.material.timepicker.TimeFormat.CLOCK_24H;
import static com.google.common.truth.Truth.assertThat;

import androidx.test.core.app.ApplicationProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
Expand All @@ -31,33 +32,49 @@
public class TimeModelTest {

@Test
public void timeModel_with12HFormat_hasCorrectValidators() {
public void with12HFormat_hasCorrectValidators() {
TimeModel timeModel = new TimeModel(CLOCK_12H);

assertThat(timeModel.getHourInputValidator().getMax()).isEqualTo(12);
assertThat(timeModel.getMinuteInputValidator().getMax()).isEqualTo(59);
}

@Test
public void timeModel_with24HFormat_hasCorrectValidators() {
public void with24HFormat_hasCorrectValidators() {
TimeModel timeModel = new TimeModel(CLOCK_24H);

assertThat(timeModel.getHourInputValidator().getMax()).isEqualTo(24);
assertThat(timeModel.getMinuteInputValidator().getMax()).isEqualTo(59);
}

@Test
public void timeModel_with12HFormat_returnsCorrectHourContentDescription() {
public void with12HFormat_returnsCorrectHourContentDescription() {
TimeModel timeModel = new TimeModel(CLOCK_12H);

assertThat(timeModel.getHourContentDescriptionResId()).isEqualTo(R.string.material_hour_suffix);
}

@Test
public void timeModel_with24HFormat_returnsCorrectHourContentDescription() {
public void with24HFormat_returnsCorrectHourContentDescription() {
TimeModel timeModel = new TimeModel(CLOCK_24H);

assertThat(timeModel.getHourContentDescriptionResId())
.isEqualTo(R.string.material_hour_24h_suffix);
}

@Test
public void formatText_validInput_returnsFormattedText() {
String formattedText =
TimeModel.formatText(ApplicationProvider.getApplicationContext().getResources(), "1");

assertThat(formattedText).isEqualTo("01");
}

@Test
public void formatText_invalidInput_returnsNull() {
String formattedText =
TimeModel.formatText(ApplicationProvider.getApplicationContext().getResources(), "+");

assertThat(formattedText).isNull();
}
}
Expand Up @@ -121,6 +121,44 @@ public void controller_clearPrefilledText_shouldNotClearWhenPartialText() {
assertThat(editText.getText().length()).isEqualTo(1);
}

@Test
public void afterTextChanged_validHourInput_formatsText() {
EditText editText = hourInput.getTextInput().getEditText();
editText.setText("1");
shadowOf(getMainLooper()).idle();

assertThat(hourInput.getChipText().toString()).isEqualTo("01");
}

@Test
public void afterTextChanged_invalidHourInput_resetsToDefault() {
EditText editText = hourInput.getTextInput().getEditText();
editText.setText("1");
editText.setText("+");
shadowOf(getMainLooper()).idle();

assertThat(hourInput.getChipText().toString()).isEqualTo("00");
}

@Test
public void afterTextChanged_validMinuteInput_formatsText() {
EditText editText = minuteInput.getTextInput().getEditText();
editText.setText("1");
shadowOf(getMainLooper()).idle();

assertThat(minuteInput.getChipText().toString()).isEqualTo("01");
}

@Test
public void afterTextChanged_invalidMinuteInput_resetsToDefault() {
EditText editText = minuteInput.getTextInput().getEditText();
editText.setText("1");
editText.setText("+");
shadowOf(getMainLooper()).idle();

assertThat(minuteInput.getChipText().toString()).isEqualTo("00");
}

private static void pressKeys(EditText editText, int... keycodes) {
for (int key : keycodes) {
editText.dispatchKeyEvent(new KeyEvent(0, 0, KeyEvent.ACTION_DOWN, key, 0));
Expand Down

0 comments on commit 0c204b8

Please sign in to comment.