forked from getodk/collect
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add abstractions for non-Gregorian calendars such as Ethiopian (getod…
…k#1505) Calendars other than the Gregorian calendar can now be added more easily. These are introduced as appearances on the existing date types meaning that the underlying date is always Gregorian but the user sees pickers that follow the rules of the desired calendar. The Ethiopian calendar is introduced through the ethiopian appearance. The implementation uses the Ethiopic joda Chronology. This appearance can be combined with others such as month-year or year.
- Loading branch information
1 parent
d73d536
commit 3cae266
Showing
23 changed files
with
1,288 additions
and
263 deletions.
There are no files selected for viewing
248 changes: 248 additions & 0 deletions
248
collect_app/src/androidTest/java/org/odk/collect/android/utilities/DateTimeUtilsTest.java
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
231 changes: 231 additions & 0 deletions
231
...t_app/src/main/java/org/odk/collect/android/fragments/dialogs/CustomDatePickerDialog.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
/* | ||
* Copyright 2017 Nafundi | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.odk.collect.android.fragments.dialogs; | ||
|
||
import android.app.Dialog; | ||
import android.content.Context; | ||
import android.content.DialogInterface; | ||
import android.os.Bundle; | ||
import android.support.v4.app.DialogFragment; | ||
import android.support.v7.app.AlertDialog; | ||
import android.view.View; | ||
import android.widget.NumberPicker; | ||
import android.widget.TextView; | ||
|
||
import org.javarosa.core.model.FormIndex; | ||
import org.joda.time.LocalDateTime; | ||
import org.joda.time.chrono.GregorianChronology; | ||
import org.odk.collect.android.R; | ||
import org.odk.collect.android.application.Collect; | ||
import org.odk.collect.android.logic.DatePickerDetails; | ||
import org.odk.collect.android.logic.FormController; | ||
import org.odk.collect.android.utilities.DateTimeUtils; | ||
|
||
/** | ||
* @author Grzegorz Orczykowski ([email protected]) | ||
*/ | ||
public abstract class CustomDatePickerDialog extends DialogFragment { | ||
public static final String DATE_PICKER_DIALOG = "datePickerDialog"; | ||
|
||
private static final String FORM_INDEX = "formIndex"; | ||
private static final String DATE = "date"; | ||
private static final String DATE_PICKER_DETAILS = "datePickerDetails"; | ||
|
||
private NumberPicker dayPicker; | ||
private NumberPicker monthPicker; | ||
private NumberPicker yearPicker; | ||
|
||
private LocalDateTime date; | ||
|
||
private TextView gregorianDateText; | ||
|
||
private FormIndex formIndex; | ||
|
||
private DatePickerDetails datePickerDetails; | ||
|
||
private CustomDatePickerDialogListener listener; | ||
|
||
public interface CustomDatePickerDialogListener { | ||
void onDateChanged(LocalDateTime date); | ||
} | ||
|
||
@Override | ||
public void onAttach(Context context) { | ||
super.onAttach(context); | ||
if (context instanceof CustomDatePickerDialogListener) { | ||
listener = (CustomDatePickerDialogListener) context; | ||
} | ||
} | ||
|
||
@Override | ||
public void onCreate(Bundle savedInstanceState) { | ||
super.onCreate(savedInstanceState); | ||
|
||
Bundle savedInstanceStateToRead = savedInstanceState; | ||
if (savedInstanceStateToRead == null) { | ||
savedInstanceStateToRead = getArguments(); | ||
} | ||
|
||
formIndex = (FormIndex) savedInstanceStateToRead.getSerializable(FORM_INDEX); | ||
date = (LocalDateTime) savedInstanceStateToRead.getSerializable(DATE); | ||
datePickerDetails = (DatePickerDetails) savedInstanceStateToRead.getSerializable(DATE_PICKER_DETAILS); | ||
} | ||
|
||
@Override | ||
public Dialog onCreateDialog(Bundle savedInstanceState) { | ||
return new AlertDialog.Builder(getActivity()) | ||
.setTitle(R.string.select_date) | ||
.setView(R.layout.custom_date_picker_dialog) | ||
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { | ||
@Override | ||
public void onClick(DialogInterface dialog, int id) { | ||
FormController formController = Collect.getInstance().getFormController(); | ||
if (formController != null) { | ||
formController.setIndexWaitingForData(formIndex); | ||
} | ||
listener.onDateChanged(getDateAsGregorian(getOriginalDate())); | ||
dismiss(); | ||
} | ||
}) | ||
.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { | ||
@Override | ||
public void onClick(DialogInterface dialog, int id) { | ||
dismiss(); | ||
} | ||
}) | ||
.create(); | ||
} | ||
|
||
@Override | ||
public void onResume() { | ||
super.onResume(); | ||
gregorianDateText = (TextView) getDialog().findViewById(R.id.date_gregorian); | ||
setUpPickers(); | ||
} | ||
|
||
@Override | ||
public void onSaveInstanceState(Bundle outState) { | ||
outState.putSerializable(FORM_INDEX, formIndex); | ||
outState.putSerializable(DATE, getDateAsGregorian(getOriginalDate())); | ||
outState.putSerializable(DATE_PICKER_DETAILS, datePickerDetails); | ||
|
||
super.onSaveInstanceState(outState); | ||
} | ||
|
||
private void setUpPickers() { | ||
dayPicker = (NumberPicker) getDialog().findViewById(R.id.day_picker); | ||
dayPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { | ||
@Override | ||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { | ||
updateGregorianDateLabel(); | ||
} | ||
}); | ||
monthPicker = (NumberPicker) getDialog().findViewById(R.id.month_picker); | ||
monthPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { | ||
@Override | ||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { | ||
updateDays(); | ||
updateGregorianDateLabel(); | ||
} | ||
}); | ||
yearPicker = (NumberPicker) getDialog().findViewById(R.id.year_picker); | ||
yearPicker.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() { | ||
@Override | ||
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { | ||
updateDays(); | ||
updateGregorianDateLabel(); | ||
} | ||
}); | ||
|
||
hidePickersIfNeeded(); | ||
} | ||
|
||
private void hidePickersIfNeeded() { | ||
if (datePickerDetails.isMonthYearMode()) { | ||
dayPicker.setVisibility(View.GONE); | ||
dayPicker.setValue(1); | ||
} else if (datePickerDetails.isYearMode()) { | ||
dayPicker.setVisibility(View.GONE); | ||
monthPicker.setVisibility(View.GONE); | ||
dayPicker.setValue(1); | ||
yearPicker.setValue(1); | ||
} | ||
} | ||
|
||
private LocalDateTime getDateAsGregorian(LocalDateTime date) { | ||
return DateTimeUtils.skipDaylightSavingGapIfExists(date) | ||
.toDateTime() | ||
.withChronology(GregorianChronology.getInstance()) | ||
.toLocalDateTime(); | ||
} | ||
|
||
protected static Bundle getArgs(FormIndex formIndex, LocalDateTime date, DatePickerDetails datePickerDetails) { | ||
Bundle args = new Bundle(); | ||
args.putSerializable(FORM_INDEX, formIndex); | ||
args.putSerializable(DATE, date); | ||
args.putSerializable(DATE_PICKER_DETAILS, datePickerDetails); | ||
|
||
return args; | ||
} | ||
|
||
protected void updateGregorianDateLabel() { | ||
String label = DateTimeUtils.getDateTimeLabel(getDateAsGregorian(getOriginalDate()).toDate(), datePickerDetails, false, getContext()); | ||
gregorianDateText.setText(label); | ||
} | ||
|
||
protected void setUpDayPicker(LocalDateTime ethiopianDate) { | ||
dayPicker.setMinValue(1); | ||
dayPicker.setMaxValue(ethiopianDate.dayOfMonth().getMaximumValue()); | ||
if (datePickerDetails.isSpinnerMode()) { | ||
dayPicker.setValue(ethiopianDate.getDayOfMonth()); | ||
} | ||
} | ||
|
||
protected void setUpMonthPicker(LocalDateTime ethiopianDate, String[] monthsArray) { | ||
monthPicker.setMaxValue(monthsArray.length - 1); | ||
monthPicker.setDisplayedValues(monthsArray); | ||
if (!datePickerDetails.isYearMode()) { | ||
monthPicker.setValue(ethiopianDate.getMonthOfYear() - 1); | ||
} | ||
} | ||
|
||
protected void setUpYearPicker(LocalDateTime ethiopianDate, int minSupportedYear, int maxSupportedYear) { | ||
yearPicker.setMinValue(minSupportedYear); | ||
yearPicker.setMaxValue(maxSupportedYear); | ||
yearPicker.setValue(ethiopianDate.getYear()); | ||
} | ||
|
||
public int getDay() { | ||
return dayPicker.getValue(); | ||
} | ||
|
||
public String getMonth() { | ||
return monthPicker.getDisplayedValues()[monthPicker.getValue()]; | ||
} | ||
|
||
public int getYear() { | ||
return yearPicker.getValue(); | ||
} | ||
|
||
public LocalDateTime getDate() { | ||
return date; | ||
} | ||
|
||
protected abstract void updateDays(); | ||
|
||
protected abstract LocalDateTime getOriginalDate(); | ||
} |
90 changes: 90 additions & 0 deletions
90
...pp/src/main/java/org/odk/collect/android/fragments/dialogs/EthiopianDatePickerDialog.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright 2017 Nafundi | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.odk.collect.android.fragments.dialogs; | ||
|
||
import org.javarosa.core.model.FormIndex; | ||
import org.joda.time.LocalDateTime; | ||
import org.joda.time.chrono.EthiopicChronology; | ||
import org.odk.collect.android.R; | ||
import org.odk.collect.android.logic.DatePickerDetails; | ||
import org.odk.collect.android.utilities.DateTimeUtils; | ||
|
||
import java.util.Arrays; | ||
|
||
/** | ||
* @author Grzegorz Orczykowski ([email protected]) | ||
* @author Aurelio Di Pasquale ([email protected]) | ||
*/ | ||
public class EthiopianDatePickerDialog extends CustomDatePickerDialog { | ||
private static final int MIN_SUPPORTED_YEAR = 1893; //1900 in Gregorian calendar | ||
private static final int MAX_SUPPORTED_YEAR = 2093; //2100 in Gregorian calendar | ||
|
||
private String[] monthsArray; | ||
|
||
public static EthiopianDatePickerDialog newInstance(FormIndex formIndex, LocalDateTime date, DatePickerDetails datePickerDetails) { | ||
EthiopianDatePickerDialog dialog = new EthiopianDatePickerDialog(); | ||
dialog.setArguments(getArgs(formIndex, date, datePickerDetails)); | ||
|
||
return dialog; | ||
} | ||
|
||
@Override | ||
public void onResume() { | ||
super.onResume(); | ||
monthsArray = getResources().getStringArray(R.array.ethiopian_months); | ||
setUpValues(); | ||
} | ||
|
||
@Override | ||
protected void updateDays() { | ||
setUpDayPicker(getCurrentEthiopianDate()); | ||
} | ||
|
||
@Override | ||
protected LocalDateTime getOriginalDate() { | ||
return getCurrentEthiopianDate(); | ||
} | ||
|
||
private void setUpDatePicker() { | ||
LocalDateTime ethiopianDate = DateTimeUtils | ||
.skipDaylightSavingGapIfExists(getDate()) | ||
.toDateTime() | ||
.withChronology(EthiopicChronology.getInstance()) | ||
.toLocalDateTime(); | ||
setUpDayPicker(ethiopianDate); | ||
setUpMonthPicker(ethiopianDate, monthsArray); | ||
setUpYearPicker(ethiopianDate, MIN_SUPPORTED_YEAR, MAX_SUPPORTED_YEAR); | ||
} | ||
|
||
private void setUpValues() { | ||
setUpDatePicker(); | ||
updateGregorianDateLabel(); | ||
} | ||
|
||
private LocalDateTime getCurrentEthiopianDate() { | ||
int ethiopianDay = getDay(); | ||
int ethiopianMonth = Arrays.asList(monthsArray).indexOf(getMonth()); | ||
int ethiopianYear = getYear(); | ||
|
||
LocalDateTime ethiopianDate = new LocalDateTime(ethiopianYear, ethiopianMonth + 1, 1, 0, 0, 0, 0, EthiopicChronology.getInstance()); | ||
if (ethiopianDay > ethiopianDate.dayOfMonth().getMaximumValue()) { | ||
ethiopianDay = ethiopianDate.dayOfMonth().getMaximumValue(); | ||
} | ||
|
||
return new LocalDateTime(ethiopianYear, ethiopianMonth + 1, ethiopianDay, 0, 0, 0, 0, EthiopicChronology.getInstance()); | ||
} | ||
} |
Oops, something went wrong.