Merge pull request #1588 from theimpulson/materialDatePicker

LoyaltCardEditActivity: Migrate to materialdatepicker dialog
This commit is contained in:
Sylvia van Os
2023-11-11 11:32:13 +01:00
committed by GitHub
2 changed files with 103 additions and 108 deletions

View File

@@ -2,8 +2,6 @@ package protect.card_locker;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
@@ -29,7 +27,6 @@ import android.view.WindowManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
@@ -43,20 +40,21 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.appcompat.widget.Toolbar;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;
import androidx.exifinterface.media.ExifInterface;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.datepicker.CalendarConstraints;
import com.google.android.material.datepicker.DateValidatorPointBackward;
import com.google.android.material.datepicker.DateValidatorPointForward;
import com.google.android.material.datepicker.MaterialDatePicker;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.tabs.TabLayout;
import com.jaredrummler.android.colorpicker.ColorPickerDialog;
import com.jaredrummler.android.colorpicker.ColorPickerDialogListener;
@@ -75,7 +73,6 @@ import java.util.Calendar;
import java.util.Collections;
import java.util.Currency;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -95,6 +92,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
private final String STATE_TAB_INDEX = "savedTab";
private final String STATE_TEMP_CARD = "tempLoyaltyCard";
private final String STATE_TEMP_CARD_FIELD = "tempLoyaltyCardField";
private final String STATE_REQUESTED_IMAGE = "requestedImage";
private final String STATE_FRONT_IMAGE_UNSAVED = "frontImageUnsaved";
private final String STATE_BACK_IMAGE_UNSAVED = "backImageUnsaved";
@@ -106,6 +104,9 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
private final String STATE_ICON_REMOVED = "iconRemoved";
private final String STATE_OPEN_SET_ICON_MENU = "openSetIconMenu";
private static final String PICK_DATE_REQUEST_KEY = "pick_date_request";
private static final String NEWLY_PICKED_DATE_ARGUMENT_KEY = "newly_picked_date";
private final String TEMP_CAMERA_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_camera_image.jpg";
private final String TEMP_CROP_IMAGE_NAME = LoyaltyCardEditActivity.class.getSimpleName() + "_crop_image.png";
private final Bitmap.CompressFormat TEMP_CROP_IMAGE_FORMAT = Bitmap.CompressFormat.PNG;
@@ -183,6 +184,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
HashMap<String, String> currencySymbols = new HashMap<>();
LoyaltyCard tempLoyaltyCard;
LoyaltyCardField tempLoyaltyCardField;
ActivityResultLauncher<Uri> mPhotoTakerLauncher;
ActivityResultLauncher<Intent> mPhotoPickerLauncher;
@@ -268,6 +270,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
tabs = binding.tabs;
savedInstanceState.putInt(STATE_TAB_INDEX, tabs.getSelectedTabPosition());
savedInstanceState.putParcelable(STATE_TEMP_CARD, tempLoyaltyCard);
savedInstanceState.putSerializable(STATE_TEMP_CARD_FIELD, tempLoyaltyCardField);
savedInstanceState.putInt(STATE_REQUESTED_IMAGE, mRequestedImage);
Object cardImageFrontObj = cardImageFront.getTag();
@@ -303,6 +306,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
onRestoring = true;
tempLoyaltyCard = savedInstanceState.getParcelable(STATE_TEMP_CARD);
tempLoyaltyCardField = (LoyaltyCardField) savedInstanceState.getSerializable(STATE_TEMP_CARD_FIELD);
super.onRestoreInstanceState(savedInstanceState);
tabs = binding.tabs;
tabs.selectTab(tabs.getTabAt(savedInstanceState.getInt(STATE_TAB_INDEX)));
@@ -388,20 +392,7 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
addDateFieldTextChangedListener(expiryField, R.string.never, R.string.chooseExpiryDate, LoyaltyCardField.expiry);
DatePickerFragment.registerDatePickListener(this, (textFieldToEdit, newDate) -> {
switch (textFieldToEdit) {
case validFrom:
formatDateField(this, validFromField, newDate);
updateTempState(LoyaltyCardField.validFrom, newDate);
break;
case expiry:
formatDateField(this, expiryField, newDate);
updateTempState(LoyaltyCardField.expiry, newDate);
break;
default:
throw new AssertionError("Unexpected field: " + textFieldToEdit);
}
});
setMaterialDatePickerResultListener();
balanceField.setOnFocusChangeListener((v, hasFocus) -> {
if (!hasFocus && !onResuming && !onRestoring) {
@@ -1024,14 +1015,14 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
if (!lastValue.toString().equals(getString(chooseDateOptionStringId))) {
dateField.setText(lastValue);
}
DialogFragment datePickerFragment = DatePickerFragment.newInstance(
showDatePicker(
loyaltyCardField,
(Date) dateField.getTag(),
// if the expiry date is being set, set date picker's minDate to the 'valid from' date
loyaltyCardField == LoyaltyCardField.expiry ? (Date) validFromField.getTag() : null,
// if the 'valid from' date is being set, set date picker's maxDate to the expiry date
loyaltyCardField == LoyaltyCardField.validFrom ? (Date) expiryField.getTag() : null);
datePickerFragment.show(getSupportFragmentManager(), "datePicker");
loyaltyCardField == LoyaltyCardField.validFrom ? (Date) expiryField.getTag() : null
);
}
}
@@ -1382,103 +1373,106 @@ public class LoyaltyCardEditActivity extends CatimaAppCompatActivity implements
// Nothing to do, no change made
}
public static class DatePickerFragment extends DialogFragment
implements DatePickerDialog.OnDateSetListener {
private void showDatePicker(
LoyaltyCardField loyaltyCardField,
@Nullable Date selectedDate,
@Nullable Date minDate,
@Nullable Date maxDate
) {
// Create a new instance of MaterialDatePicker and return it
long startDate = minDate != null ? minDate.getTime() : getDefaultMinDateOfDatePicker();
long endDate = maxDate != null ? maxDate.getTime() : getDefaultMaxDateOfDatePicker();
public interface OnDatePickListener {
void onDatePicked(@NonNull LoyaltyCardField textFieldToEdit, @NonNull Date newDate);
CalendarConstraints.DateValidator dateValidator;
switch (loyaltyCardField) {
case validFrom:
dateValidator = DateValidatorPointBackward.before(endDate);
break;
case expiry:
dateValidator = DateValidatorPointForward.from(startDate);
break;
default:
throw new AssertionError("Unexpected field: " + loyaltyCardField);
}
private static final String TEXT_FIELD_TO_EDIT_ARGUMENT_KEY = "text_field_to_edit";
private static final String CURRENT_DATE_ARGUMENT_KEY = "current_date";
private static final String MIN_DATE_ARGUMENT_KEY = "min_date";
private static final String MAX_DATE_ARGUMENT_KEY = "max_date";
private static final String PICK_DATE_REQUEST_KEY = "pick_date_request";
private static final String NEWLY_PICKED_DATE_ARGUMENT_KEY = "newly_picked_date";
CalendarConstraints calendarConstraints = new CalendarConstraints.Builder()
.setValidator(dateValidator)
.setStart(startDate)
.setEnd(endDate)
.build();
LoyaltyCardField textFieldEdit;
@Nullable
Date minDate;
@Nullable
Date maxDate;
public static DatePickerFragment newInstance(@NonNull LoyaltyCardField textField, @Nullable Date currentDate, @Nullable Date minDate, @Nullable Date maxDate) {
Bundle args = new Bundle();
args.putSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY, textField);
args.putSerializable(CURRENT_DATE_ARGUMENT_KEY, currentDate);
args.putSerializable(MIN_DATE_ARGUMENT_KEY, minDate);
args.putSerializable(MAX_DATE_ARGUMENT_KEY, maxDate);
DatePickerFragment fragment = new DatePickerFragment();
fragment.setArguments(args);
return fragment;
// Use the selected date as the default date in the picker
final Calendar calendar = Calendar.getInstance();
if (selectedDate != null) {
calendar.setTime(selectedDate);
}
public static void registerDatePickListener(@NonNull AppCompatActivity activity, @NonNull OnDatePickListener listener) {
activity.getSupportFragmentManager().setFragmentResultListener(
PICK_DATE_REQUEST_KEY,
activity,
(requestKey, result) -> listener.onDatePicked(
(LoyaltyCardField) Objects.requireNonNull(result.getSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY)),
(Date) Objects.requireNonNull(result.getSerializable(NEWLY_PICKED_DATE_ARGUMENT_KEY))));
}
MaterialDatePicker<Long> materialDatePicker = MaterialDatePicker.Builder.datePicker()
.setSelection(calendar.getTimeInMillis())
.setCalendarConstraints(calendarConstraints)
.build();
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Bundle args = requireArguments();
textFieldEdit = (LoyaltyCardField) args.getSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY);
minDate = (Date) args.getSerializable(MIN_DATE_ARGUMENT_KEY);
maxDate = (Date) args.getSerializable(MAX_DATE_ARGUMENT_KEY);
// Use the current date as the default date in the picker
final Calendar c = Calendar.getInstance();
Date date = (Date) args.getSerializable(CURRENT_DATE_ARGUMENT_KEY);
if (date != null) {
c.setTime(date);
// Required to handle configuration changes
// See https://github.com/material-components/material-components-android/issues/1688
tempLoyaltyCardField = loyaltyCardField;
getSupportFragmentManager().addFragmentOnAttachListener((fragmentManager, fragment) -> {
if (fragment instanceof MaterialDatePicker && Objects.equals(fragment.getTag(), PICK_DATE_REQUEST_KEY)) {
((MaterialDatePicker<Long>) fragment).addOnPositiveButtonClickListener(selection -> {
Bundle args = new Bundle();
args.putLong(NEWLY_PICKED_DATE_ARGUMENT_KEY, selection);
getSupportFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, args);
});
}
});
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH);
int day = c.get(Calendar.DAY_OF_MONTH);
materialDatePicker.show(getSupportFragmentManager(), PICK_DATE_REQUEST_KEY);
}
// Create a new instance of DatePickerDialog and return it
DatePickerDialog datePickerDialog = new DatePickerDialog(getActivity(), this, year, month, day);
datePickerDialog.getDatePicker().setMinDate(minDate != null ? minDate.getTime() : getDefaultMinDateOfDatePicker());
datePickerDialog.getDatePicker().setMaxDate(maxDate != null ? maxDate.getTime() : getDefaultMaxDateOfDatePicker());
return datePickerDialog;
// Required to handle configuration changes
// See https://github.com/material-components/material-components-android/issues/1688
private void setMaterialDatePickerResultListener() {
MaterialDatePicker<Long> fragment = (MaterialDatePicker<Long>) getSupportFragmentManager().findFragmentByTag(PICK_DATE_REQUEST_KEY);
if (fragment != null) {
fragment.addOnPositiveButtonClickListener(selection -> {
Bundle args = new Bundle();
args.putLong(NEWLY_PICKED_DATE_ARGUMENT_KEY, selection);
getSupportFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, args);
});
}
private long getDefaultMinDateOfDatePicker() {
Calendar minDateCalendar = Calendar.getInstance();
minDateCalendar.set(1970, 0, 1);
return minDateCalendar.getTimeInMillis();
}
getSupportFragmentManager().setFragmentResultListener(
PICK_DATE_REQUEST_KEY,
this,
(requestKey, result) -> {
long selection = result.getLong(NEWLY_PICKED_DATE_ARGUMENT_KEY);
private long getDefaultMaxDateOfDatePicker() {
Calendar maxDateCalendar = Calendar.getInstance();
maxDateCalendar.set(2100, 11, 31);
return maxDateCalendar.getTimeInMillis();
}
Date newDate = new Date(selection);
switch (tempLoyaltyCardField) {
case validFrom:
formatDateField(LoyaltyCardEditActivity.this, validFromField, newDate);
updateTempState(LoyaltyCardField.validFrom, newDate);
break;
case expiry:
formatDateField(LoyaltyCardEditActivity.this, expiryField, newDate);
updateTempState(LoyaltyCardField.expiry, newDate);
break;
default:
throw new AssertionError("Unexpected field: " + tempLoyaltyCardField);
}
}
);
}
public void onDateSet(DatePicker view, int year, int month, int day) {
Calendar c = new GregorianCalendar();
c.set(Calendar.YEAR, year);
c.set(Calendar.MONTH, month);
c.set(Calendar.DAY_OF_MONTH, day);
c.set(Calendar.HOUR_OF_DAY, 0);
c.set(Calendar.MINUTE, 0);
c.set(Calendar.SECOND, 0);
c.set(Calendar.MILLISECOND, 0);
private long getDefaultMinDateOfDatePicker() {
Calendar minDateCalendar = Calendar.getInstance();
minDateCalendar.set(1970, 0, 1);
return minDateCalendar.getTimeInMillis();
}
long unixTime = c.getTimeInMillis();
Date date = new Date(unixTime);
Bundle result = new Bundle();
result.putSerializable(TEXT_FIELD_TO_EDIT_ARGUMENT_KEY, textFieldEdit);
result.putSerializable(NEWLY_PICKED_DATE_ARGUMENT_KEY, date);
getParentFragmentManager().setFragmentResult(PICK_DATE_REQUEST_KEY, result);
}
private long getDefaultMaxDateOfDatePicker() {
Calendar maxDateCalendar = Calendar.getInstance();
maxDateCalendar.set(2100, 11, 31);
return maxDateCalendar.getTimeInMillis();
}
private void doSave() {

View File

@@ -2,6 +2,7 @@ package protect.card_locker;
import android.app.Activity;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -725,9 +726,9 @@ public class LoyaltyCardViewActivityTest {
shadowOf(getMainLooper()).idle();
DatePickerDialog datePickerDialog = (DatePickerDialog) (ShadowDialog.getLatestDialog());
Dialog datePickerDialog = ShadowDialog.getLatestDialog();
assertNotNull(datePickerDialog);
datePickerDialog.getButton(DatePickerDialog.BUTTON_POSITIVE).performClick();
datePickerDialog.findViewById(com.google.android.material.R.id.confirm_button).performClick();
shadowOf(getMainLooper()).idle();