Skip to content

Commit

Permalink
Implementing Date and Time pickers (#789)
Browse files Browse the repository at this point in the history
* TimeWidget: added new button and TextView

* TimeWidget: added setTime() method

* TimeWidget: added TimePickerDialog

* TimeWidget: removed unnecessary code

* DateWidget: added new button and TextView

* DateWidget: Added DatePickerDialog

* DateWidget: refactored hideDayFieldIfNotInFormat() method

* DateWidget: Removed unnecessary code

* DateWidget: Code refactoring

* DateWidget: Implemented setDate() method

* DateWidget: Fixed some bugs

* Added new DateTimeWidget

* Fixed bugs and lints

* Removed calendar appearance

* Hide dialog title

* Update strings.xml

* Update strings.xml

* Fixed test

* Added calendar back

* Fixed displayed text

* Fixed bug

* Added custom dialogs

* Fixed bugs and test

* Update strings.xml

* Changes for calendar appearance
  • Loading branch information
grzesiek2010 authored and lognaturel committed Apr 14, 2017
1 parent 8c1e696 commit 6cf1010
Show file tree
Hide file tree
Showing 7 changed files with 333 additions and 438 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,10 @@ public void setTextcolor(int color) {
mView_Text.setTextColor(color);
}

public TextView getView_Text() {
return mView_Text;
}

/**
* This is what gets called when the AudioButton gets clicked
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,17 @@

package org.odk.collect.android.widgets;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.os.Build;
import android.view.Gravity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.CalendarView;
import android.widget.DatePicker;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.TimePicker;

import org.javarosa.core.model.data.DateTimeData;
import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.form.api.FormEntryPrompt;
import org.joda.time.DateTime;

import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;
import org.odk.collect.android.application.Collect;
import org.odk.collect.android.utilities.DateWidgetUtils;

import java.util.Calendar;
import java.util.Date;

/**
* Displays a DatePicker widget. DateWidget handles leap years and does not allow dates that do not
* exist.
Expand All @@ -48,214 +34,60 @@
*/
public class DateTimeWidget extends QuestionWidget {

private DatePicker mDatePicker;
private TimePicker mTimePicker;
private DatePicker.OnDateChangedListener mDateListener;
private boolean hideDay = false;
private boolean hideMonth = false;
private boolean showCalendar = false;
private HorizontalScrollView scrollView = null;
private DateWidget mDateWidget;
private TimeWidget mTimeWidget;

public DateTimeWidget(Context context, FormEntryPrompt prompt) {
super(context, prompt);

mDatePicker = new DatePicker(getContext());
mDatePicker.setId(QuestionWidget.newUniqueId());
mDatePicker.setFocusable(!prompt.isReadOnly());
mDatePicker.setEnabled(!prompt.isReadOnly());

if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN) {
DateWidgetUtils.fixCalendarViewIfJellyBean(mDatePicker.getCalendarView());
}

mTimePicker = new TimePicker(getContext());
mTimePicker.setId(QuestionWidget.newUniqueId());
mTimePicker.setFocusable(!prompt.isReadOnly());
mTimePicker.setEnabled(!prompt.isReadOnly());
mTimePicker.setPadding(0, 20, 0, 0);

String clockType =
android.provider.Settings.System.getString(context.getContentResolver(),
android.provider.Settings.System.TIME_12_24);
if (clockType == null || clockType.equalsIgnoreCase("24")) {
mTimePicker.setIs24HourView(true);
}

hideDayFieldIfNotInFormat(prompt);

mDateListener = new DatePicker.OnDateChangedListener() {
@Override
public void onDateChanged(DatePicker view, int year, int month, int day) {
if (mPrompt.isReadOnly()) {
setAnswer();
} else {
// handle leap years and number of days in month
// TODO
// http://code.google.com/p/android/issues/detail?id=2081
// in older versions of android (1.6ish) the datepicker lets you pick bad dates
// in newer versions, calling updateDate() calls onDatechangedListener(),
// causing an
// endless loop.
Calendar c = Calendar.getInstance();
c.set(year, month, 1);
int max = c.getActualMaximum(Calendar.DAY_OF_MONTH);
if (day > max) {
if (!(mDatePicker.getDayOfMonth() == day && mDatePicker.getMonth() == month
&& mDatePicker.getYear() == year)) {
Collect.getInstance().getActivityLogger().logInstanceAction(
DateTimeWidget.this, "onDateChanged",
String.format("%1$04d-%2$02d-%3$02d", year, month, max),
mPrompt.getIndex());
mDatePicker.updateDate(year, month, max);
}
} else {
if (!(mDatePicker.getDayOfMonth() == day && mDatePicker.getMonth() == month
&& mDatePicker.getYear() == year)) {
Collect.getInstance().getActivityLogger().logInstanceAction(
DateTimeWidget.this, "onDateChanged",
String.format("%1$04d-%2$02d-%3$02d", year, month, day),
mPrompt.getIndex());
mDatePicker.updateDate(year, month, day);
}
}
}
}
};

mTimePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
@Override
public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
Collect.getInstance().getActivityLogger().logInstanceAction(DateTimeWidget.this,
"onTimeChanged",
String.format("%1$02d:%2$02d", hourOfDay, minute), mPrompt.getIndex());
}
});

setGravity(Gravity.START);
LinearLayout answerLayout = new LinearLayout(getContext());
answerLayout.setOrientation(LinearLayout.VERTICAL);
if (showCalendar) {
scrollView = new HorizontalScrollView(context);
LinearLayout ll = new LinearLayout(context);
ll.addView(mDatePicker);
ll.setPadding(10, 10, 10, 10);
scrollView.addView(ll);
answerLayout.addView(scrollView);
} else {
answerLayout.addView(mDatePicker);
}
answerLayout.addView(mTimePicker);
addAnswerView(answerLayout);

// If there's an answer, use it.
setAnswer();
}
mDateWidget = new DateWidget(context, prompt);
mTimeWidget= new TimeWidget(context, prompt);

mDateWidget.mQuestionMediaLayout.getView_Text().setVisibility(GONE);
mTimeWidget.mQuestionMediaLayout.getView_Text().setVisibility(GONE);

/**
* Shared between DateWidget and DateTimeWidget.
* There are extra appearance settings that do not apply for dateTime...
* TODO: move this into utilities or base class?
*/
@SuppressLint("NewApi")
private void hideDayFieldIfNotInFormat(FormEntryPrompt prompt) {
String appearance = prompt.getQuestion().getAppearanceAttr();
if (appearance == null) {
showCalendar = true;
this.mDatePicker.setCalendarViewShown(true);
CalendarView cv = this.mDatePicker.getCalendarView();
cv.setShowWeekNumber(false);
this.mDatePicker.setSpinnersShown(true);
hideDay = true;
hideMonth = false;
} else if ("month-year".equals(appearance)) {
hideDay = true;
this.mDatePicker.setCalendarViewShown(false);
this.mDatePicker.setSpinnersShown(true);
mTimePicker.setVisibility(GONE);
} else if ("year".equals(appearance)) {
hideMonth = true;
this.mDatePicker.setCalendarViewShown(false);
this.mDatePicker.setSpinnersShown(true);
mTimePicker.setVisibility(GONE);
} else if ("no-calendar".equals(appearance)) {
this.mDatePicker.setCalendarViewShown(false);
this.mDatePicker.setSpinnersShown(true);
} else {
showCalendar = true;
this.mDatePicker.setCalendarViewShown(true);
CalendarView cv = this.mDatePicker.getCalendarView();
cv.setShowWeekNumber(false);
this.mDatePicker.setSpinnersShown(true);
hideDay = true;
hideMonth = false;
}

if (hideMonth || hideDay) {
mDatePicker.findViewById(
Resources.getSystem().getIdentifier("day", "id", "android"))
.setVisibility(View.GONE);
if (hideMonth) {
mDatePicker
.findViewById(
Resources.getSystem().getIdentifier("month", "id", "android"))
.setVisibility(View.GONE);
}
LinearLayout linearLayout = new LinearLayout(getContext());
linearLayout.setOrientation(LinearLayout.VERTICAL);
linearLayout.addView(mDateWidget);
if (mDateWidget.isCalendarShown() || !mDateWidget.isDayHidden()) {
linearLayout.addView(mTimeWidget);
}
addAnswerView(linearLayout);
}

private void setAnswer() {

if (mPrompt.getAnswerValue() != null) {

DateTime ldt =
new DateTime(
((Date) mPrompt.getAnswerValue().getValue()).getTime
());
mDatePicker.init(ldt.getYear(), ldt.getMonthOfYear() - 1, ldt.getDayOfMonth(),
mDateListener);
mTimePicker.setCurrentHour(ldt.getHourOfDay());
mTimePicker.setCurrentMinute(ldt.getMinuteOfHour());

} else {
// create time widget with current time as of right now
clearAnswer();
}
}


/**
* Resets date to today.
*/
@Override
public void clearAnswer() {
DateTime ldt = new DateTime();
mDatePicker.init(ldt.getYear(), ldt.getMonthOfYear() - 1, ldt.getDayOfMonth(),
mDateListener);
mTimePicker.setCurrentHour(ldt.getHourOfDay());
mTimePicker.setCurrentMinute(ldt.getMinuteOfHour());
}


@Override
public IAnswerData getAnswer() {
if (showCalendar) {
scrollView.clearChildFocus(mDatePicker);
}
clearFocus();

boolean hideDay = mDateWidget.isDayHidden();
boolean hideMonth = mDateWidget.isMonthHidden();
boolean showCalendar = mDateWidget.isCalendarShown();

int year = mDateWidget.getYear();
int month = mDateWidget.getMonth();
int day = mDateWidget.getDay();
int hour = mTimeWidget.getHour();
int minute = mTimeWidget.getMinute();

LocalDateTime ldt = new LocalDateTime()
.withYear(mDatePicker.getYear())
.withMonthOfYear((!showCalendar && hideMonth) ? 1 : mDatePicker.getMonth() + 1)
.withDayOfMonth((!showCalendar && (hideMonth || hideDay)) ? 1 : mDatePicker.getDayOfMonth())
.withHourOfDay((!showCalendar && (hideMonth || hideDay)) ? 0 : mTimePicker.getCurrentHour())
.withMinuteOfHour((!showCalendar && (hideMonth || hideDay)) ? 0 : mTimePicker.getCurrentMinute())
.withYear(year)
.withMonthOfYear((!showCalendar && hideMonth) ? 1 : month)
.withDayOfMonth((!showCalendar && (hideMonth || hideDay)) ? 1 : day)
.withHourOfDay((!showCalendar && (hideMonth || hideDay)) ? 0 : hour)
.withMinuteOfHour((!showCalendar && (hideMonth || hideDay)) ? 0 : minute)
.withSecondOfMinute(0);

ldt = skipDaylightSavingGapIfExists(ldt);
return new DateTimeData(ldt.toDate());
}

@Override
public void clearAnswer() {
mDateWidget.clearAnswer();
mTimeWidget.clearAnswer();
}

@Override
public void setFocus(Context context) {
Expand All @@ -265,22 +97,19 @@ public void setFocus(Context context) {
inputManager.hideSoftInputFromWindow(this.getWindowToken(), 0);
}


@Override
public void setOnLongClickListener(OnLongClickListener l) {
mDatePicker.setOnLongClickListener(l);
mTimePicker.setOnLongClickListener(l);
mDateWidget.setOnLongClickListener(l);
mTimeWidget.setOnLongClickListener(l);
}


@Override
public void cancelLongPress() {
super.cancelLongPress();
mDatePicker.cancelLongPress();
mTimePicker.cancelLongPress();
mDateWidget.cancelLongPress();
mTimeWidget.cancelLongPress();
}


// Skip over a "daylight savings gap". This is needed on the day and time of a daylight savings
// transition because that date/time doesn't exist.
// Today clocks are almost always set one hour back or ahead.
Expand Down
Loading

0 comments on commit 6cf1010

Please sign in to comment.