mirror of https://github.com/tasks/tasks
Material components date and time pickers
parent
4e1b496c4b
commit
40d831f7da
@ -1,85 +0,0 @@
|
|||||||
package org.tasks.activities;
|
|
||||||
|
|
||||||
import static org.tasks.dialogs.MyDatePickerDialog.newDatePicker;
|
|
||||||
import static org.tasks.dialogs.MyTimePickerDialog.newTimePicker;
|
|
||||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
|
||||||
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import dagger.hilt.android.AndroidEntryPoint;
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import org.tasks.dialogs.MyDatePickerDialog;
|
|
||||||
import org.tasks.dialogs.MyTimePickerDialog;
|
|
||||||
import org.tasks.injection.InjectingAppCompatActivity;
|
|
||||||
import org.tasks.preferences.Preferences;
|
|
||||||
import org.tasks.themes.ThemeAccent;
|
|
||||||
import org.tasks.time.DateTime;
|
|
||||||
|
|
||||||
@AndroidEntryPoint
|
|
||||||
public class DateAndTimePickerActivity extends InjectingAppCompatActivity
|
|
||||||
implements MyDatePickerDialog.DatePickerCallback, MyTimePickerDialog.TimePickerCallback {
|
|
||||||
|
|
||||||
public static final String EXTRA_TIMESTAMP = "extra_timestamp";
|
|
||||||
private static final String FRAG_TAG_DATE_PICKER = "frag_tag_date_picker";
|
|
||||||
private static final String FRAG_TAG_TIME_PICKER = "frag_tag_time_picker";
|
|
||||||
private static final String EXTRA_DATE_SELECTED = "extra_date_selected";
|
|
||||||
@Inject ThemeAccent themeAccent;
|
|
||||||
@Inject Preferences preferences;
|
|
||||||
|
|
||||||
private DateTime initial;
|
|
||||||
private boolean dateSelected;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
|
|
||||||
initial = new DateTime(getIntent().getLongExtra(EXTRA_TIMESTAMP, currentTimeMillis()));
|
|
||||||
|
|
||||||
if (savedInstanceState != null) {
|
|
||||||
dateSelected = savedInstanceState.getBoolean(EXTRA_DATE_SELECTED, false);
|
|
||||||
if (dateSelected) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
themeAccent.applyStyle(getTheme());
|
|
||||||
|
|
||||||
androidx.fragment.app.FragmentManager fragmentManager = getSupportFragmentManager();
|
|
||||||
if (fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) == null) {
|
|
||||||
newDatePicker(null, 0, initial.getMillis())
|
|
||||||
.show(getSupportFragmentManager(), FRAG_TAG_DATE_PICKER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onSaveInstanceState(Bundle outState) {
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putBoolean(EXTRA_DATE_SELECTED, dateSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDatePicked(DialogInterface dialog, long timestamp) {
|
|
||||||
if (timestamp == MyDatePickerDialog.NO_DATE) {
|
|
||||||
finish();
|
|
||||||
} else {
|
|
||||||
dialog.dismiss();
|
|
||||||
dateSelected = true;
|
|
||||||
newTimePicker(
|
|
||||||
null,
|
|
||||||
0,
|
|
||||||
new DateTime(timestamp).withMillisOfDay(initial.getMillisOfDay()).getMillis())
|
|
||||||
.show(getSupportFragmentManager(), FRAG_TAG_TIME_PICKER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTimePicked(long timestamp) {
|
|
||||||
if (timestamp != MyTimePickerDialog.NO_TIME) {
|
|
||||||
final Intent data = new Intent();
|
|
||||||
data.putExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, timestamp);
|
|
||||||
setResult(RESULT_OK, data);
|
|
||||||
}
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker
|
||||||
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import org.tasks.dialogs.MyDatePickerDialog.Companion.newDatePicker
|
||||||
|
import org.tasks.dialogs.MyTimePickerDialog
|
||||||
|
import org.tasks.dialogs.MyTimePickerDialog.Companion.newTimePicker
|
||||||
|
import org.tasks.injection.InjectingAppCompatActivity
|
||||||
|
import org.tasks.preferences.Preferences
|
||||||
|
import org.tasks.themes.ThemeAccent
|
||||||
|
import org.tasks.time.DateTime
|
||||||
|
import org.tasks.time.DateTimeUtils.currentTimeMillis
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@AndroidEntryPoint
|
||||||
|
class DateAndTimePickerActivity : InjectingAppCompatActivity() {
|
||||||
|
@Inject lateinit var themeAccent: ThemeAccent
|
||||||
|
@Inject lateinit var preferences: Preferences
|
||||||
|
|
||||||
|
private var initial: DateTime? = null
|
||||||
|
private var dateSelected: DateTime? = null
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
initial = DateTime(intent.getLongExtra(EXTRA_TIMESTAMP, currentTimeMillis()))
|
||||||
|
dateSelected =
|
||||||
|
savedInstanceState
|
||||||
|
?.getLong(EXTRA_DATE_SELECTED)
|
||||||
|
?.takeIf { it > 0 }
|
||||||
|
?.let { DateTime(it, DateTime.UTC) }
|
||||||
|
themeAccent.applyStyle(theme)
|
||||||
|
if (dateSelected != null) {
|
||||||
|
showTimePicker()
|
||||||
|
} else {
|
||||||
|
showDatePicker(initial ?: DateTime())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showDatePicker(date: DateTime) {
|
||||||
|
dateSelected = null
|
||||||
|
val picker =
|
||||||
|
supportFragmentManager
|
||||||
|
.findFragmentByTag(FRAG_TAG_DATE_PICKER) as? MaterialDatePicker<Long>
|
||||||
|
?: newDatePicker(date.millis).apply {
|
||||||
|
show(supportFragmentManager, FRAG_TAG_DATE_PICKER)
|
||||||
|
}
|
||||||
|
picker.apply {
|
||||||
|
addOnPositiveButtonClickListener {
|
||||||
|
dateSelected = DateTime(selection!!, DateTime.UTC)
|
||||||
|
showTimePicker()
|
||||||
|
}
|
||||||
|
addOnCancelListener { finish() }
|
||||||
|
addOnNegativeButtonClickListener { finish() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun showTimePicker() {
|
||||||
|
val fragmentManager = supportFragmentManager
|
||||||
|
val picker =
|
||||||
|
fragmentManager
|
||||||
|
.findFragmentByTag(FRAG_TAG_TIME_PICKER) as? MaterialTimePicker
|
||||||
|
?: newTimePicker(
|
||||||
|
this,
|
||||||
|
DateTime(dateSelected!!.year, dateSelected!!.monthOfYear, dateSelected!!.dayOfMonth)
|
||||||
|
.withMillisOfDay(initial!!.millisOfDay).millis
|
||||||
|
).apply { show(fragmentManager, FRAG_TAG_TIME_PICKER) }
|
||||||
|
picker.apply {
|
||||||
|
addOnCancelListener {
|
||||||
|
dateSelected?.let { showDatePicker(it) } ?: finish()
|
||||||
|
}
|
||||||
|
addOnNegativeButtonClickListener {
|
||||||
|
dateSelected?.let { showDatePicker(it) } ?: finish()
|
||||||
|
}
|
||||||
|
addOnPositiveButtonClickListener {
|
||||||
|
val data = Intent()
|
||||||
|
data.putExtra(
|
||||||
|
MyTimePickerDialog.EXTRA_TIMESTAMP,
|
||||||
|
DateTime(
|
||||||
|
dateSelected!!.year,
|
||||||
|
dateSelected!!.monthOfYear,
|
||||||
|
dateSelected!!.dayOfMonth,
|
||||||
|
hour,
|
||||||
|
minute
|
||||||
|
).millis
|
||||||
|
)
|
||||||
|
setResult(RESULT_OK, data)
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
dateSelected?.let { outState.putLong(EXTRA_DATE_SELECTED, it.millis) }
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_TIMESTAMP = "extra_timestamp"
|
||||||
|
private const val FRAG_TAG_DATE_PICKER = "frag_tag_date_picker"
|
||||||
|
private const val FRAG_TAG_TIME_PICKER = "frag_tag_time_picker"
|
||||||
|
private const val EXTRA_DATE_SELECTED = "extra_date_selected"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,93 +0,0 @@
|
|||||||
package org.tasks.dialogs;
|
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_CANCELED;
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
|
||||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog;
|
|
||||||
import com.wdullaer.materialdatetimepicker.date.DatePickerDialog.OnDateSetListener;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.preferences.Preferences;
|
|
||||||
import org.tasks.time.DateTime;
|
|
||||||
|
|
||||||
public class MyDatePickerDialog extends DatePickerDialog implements OnDateSetListener {
|
|
||||||
|
|
||||||
public static final String EXTRA_TIMESTAMP = "extra_timestamp";
|
|
||||||
public static final int NO_DATE = -1;
|
|
||||||
|
|
||||||
public static MyDatePickerDialog newDatePicker(Fragment target, int rc, long initial) {
|
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
arguments.putLong(EXTRA_TIMESTAMP, initial);
|
|
||||||
MyDatePickerDialog dialog = new MyDatePickerDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.setTargetFragment(target, rc);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface DatePickerCallback{ // TODO: remove this after removing DateAndTimePickerActivity
|
|
||||||
void onDatePicked(DialogInterface dialog, long timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DatePickerCallback callback;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
long timestamp = getArguments().getLong(EXTRA_TIMESTAMP, currentTimeMillis());
|
|
||||||
DateTime initial = (timestamp > 0 ? new DateTime(timestamp) : new DateTime()).startOfDay();
|
|
||||||
initialize(
|
|
||||||
null,
|
|
||||||
initial.getYear(),
|
|
||||||
initial.getMonthOfYear() - 1,
|
|
||||||
initial.getDayOfMonth());
|
|
||||||
setVersion(DatePickerDialog.Version.VERSION_2);
|
|
||||||
int firstDayOfWeek = new Preferences(getContext()).getFirstDayOfWeek();
|
|
||||||
if (firstDayOfWeek >= 1 && firstDayOfWeek <= 7) {
|
|
||||||
setFirstDayOfWeek(firstDayOfWeek);
|
|
||||||
}
|
|
||||||
setThemeDark(getResources().getBoolean(R.bool.is_dark)); // TODO: remove this after removing DateAndTimePickerActivity
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnDateSetListener(this);
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(@NonNull Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
|
|
||||||
if (context instanceof DatePickerCallback) {
|
|
||||||
callback = (DatePickerCallback) context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
super.onCancel(dialog);
|
|
||||||
|
|
||||||
if (getTargetFragment() == null) {
|
|
||||||
callback.onDatePicked(dialog, NO_DATE);
|
|
||||||
} else {
|
|
||||||
getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_CANCELED, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) {
|
|
||||||
long result = new DateTime(year, monthOfYear + 1, dayOfMonth).getMillis();
|
|
||||||
if (getTargetFragment() == null) {
|
|
||||||
callback.onDatePicked(getDialog(), result);
|
|
||||||
} else {
|
|
||||||
Intent data = new Intent();
|
|
||||||
data.putExtra(EXTRA_TIMESTAMP, result);
|
|
||||||
getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_OK, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,76 @@
|
|||||||
|
package org.tasks.dialogs
|
||||||
|
|
||||||
|
import android.app.Activity.RESULT_CANCELED
|
||||||
|
import android.app.Activity.RESULT_OK
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.google.android.material.datepicker.MaterialDatePicker
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities
|
||||||
|
import org.tasks.time.DateTime
|
||||||
|
import org.tasks.time.DateTimeUtils.currentTimeMillis
|
||||||
|
import org.tasks.time.DateTimeUtils.startOfDay
|
||||||
|
|
||||||
|
class MyDatePickerDialog : DialogFragment() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val fragment =
|
||||||
|
(childFragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) as? MaterialDatePicker<Long>)
|
||||||
|
?: newDatePicker(initial)
|
||||||
|
.let {
|
||||||
|
childFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.add(it, FRAG_TAG_DATE_PICKER)
|
||||||
|
.commit()
|
||||||
|
it
|
||||||
|
}
|
||||||
|
with(fragment) {
|
||||||
|
addOnPositiveButtonClickListener {
|
||||||
|
val dt = DateTime(it, DateTime.UTC)
|
||||||
|
selected(dt.year, dt.monthOfYear, dt.dayOfMonth)
|
||||||
|
}
|
||||||
|
addOnCancelListener { cancel() }
|
||||||
|
addOnNegativeButtonClickListener { cancel() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val initial: Long
|
||||||
|
get() = arguments?.getLong(MyTimePickerDialog.EXTRA_TIMESTAMP) ?: DateUtilities.now().startOfDay()
|
||||||
|
|
||||||
|
private fun selected(year: Int, month: Int, day: Int) {
|
||||||
|
targetFragment?.onActivityResult(
|
||||||
|
targetRequestCode,
|
||||||
|
RESULT_OK,
|
||||||
|
Intent().putExtra(EXTRA_TIMESTAMP, DateTime(year, month, day).millis)
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancel() {
|
||||||
|
targetFragment?.onActivityResult(targetRequestCode, RESULT_CANCELED, null)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val FRAG_TAG_DATE_PICKER = "frag_date_picker"
|
||||||
|
const val EXTRA_TIMESTAMP = "extra_timestamp"
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newDatePicker(target: Fragment, rc: Int, initial: Long) =
|
||||||
|
MyDatePickerDialog().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putLong(EXTRA_TIMESTAMP, initial)
|
||||||
|
}
|
||||||
|
setTargetFragment(target, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newDatePicker(initial: Long) = MaterialDatePicker.Builder.datePicker()
|
||||||
|
// TODO: setInputMode for calendar or text
|
||||||
|
// TODO: figure out hack for first day of week
|
||||||
|
.setSelection(if (initial > 0) initial else currentTimeMillis())
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,91 +0,0 @@
|
|||||||
package org.tasks.dialogs;
|
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_CANCELED;
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
|
||||||
import static org.tasks.date.DateTimeUtils.newDateTime;
|
|
||||||
import static org.tasks.time.DateTimeUtils.currentTimeMillis;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.text.format.DateFormat;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import androidx.fragment.app.Fragment;
|
|
||||||
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog;
|
|
||||||
import com.wdullaer.materialdatetimepicker.time.TimePickerDialog.OnTimeSetListener;
|
|
||||||
import org.tasks.R;
|
|
||||||
import org.tasks.time.DateTime;
|
|
||||||
|
|
||||||
public class MyTimePickerDialog extends TimePickerDialog implements OnTimeSetListener {
|
|
||||||
|
|
||||||
public static final String EXTRA_TIMESTAMP = "extra_timestamp";
|
|
||||||
public static final int NO_TIME = -1;
|
|
||||||
|
|
||||||
public static MyTimePickerDialog newTimePicker(Fragment target, int rc, long initial) {
|
|
||||||
Bundle arguments = new Bundle();
|
|
||||||
arguments.putLong(EXTRA_TIMESTAMP, initial);
|
|
||||||
MyTimePickerDialog dialog = new MyTimePickerDialog();
|
|
||||||
dialog.setArguments(arguments);
|
|
||||||
dialog.setTargetFragment(target, rc);
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface TimePickerCallback { // TODO: remove this after removing DateAndTimePickerActivity
|
|
||||||
void onTimePicked(long timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DateTime initial;
|
|
||||||
private TimePickerCallback callback;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
|
||||||
initial = newDateTime(getArguments().getLong(EXTRA_TIMESTAMP, currentTimeMillis()));
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
initialize(
|
|
||||||
null,
|
|
||||||
initial.getHourOfDay(),
|
|
||||||
initial.getMinuteOfHour(),
|
|
||||||
0,
|
|
||||||
DateFormat.is24HourFormat(getContext()));
|
|
||||||
setThemeDark(getResources().getBoolean(R.bool.is_dark)); // TODO: remove this after removing DateAndTimePickerActivity
|
|
||||||
}
|
|
||||||
|
|
||||||
setOnTimeSetListener(this);
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAttach(@NonNull Context context) {
|
|
||||||
super.onAttach(context);
|
|
||||||
|
|
||||||
if (context instanceof TimePickerCallback) {
|
|
||||||
callback = (TimePickerCallback) context;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialog) {
|
|
||||||
super.onCancel(dialog);
|
|
||||||
|
|
||||||
if (getTargetFragment() == null) {
|
|
||||||
callback.onTimePicked(NO_TIME);
|
|
||||||
} else {
|
|
||||||
getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_CANCELED, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTimeSet(TimePickerDialog view, int hours, int minutes, int second) {
|
|
||||||
long result = initial.startOfDay().withHourOfDay(hours).withMinuteOfHour(minutes).getMillis();
|
|
||||||
if (getTargetFragment() == null) {
|
|
||||||
callback.onTimePicked(result);
|
|
||||||
} else {
|
|
||||||
Intent data = new Intent();
|
|
||||||
data.putExtra(EXTRA_TIMESTAMP, result);
|
|
||||||
getTargetFragment().onActivityResult(getTargetRequestCode(), RESULT_OK, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
package org.tasks.dialogs
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.Activity.RESULT_CANCELED
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.text.format.DateFormat.is24HourFormat
|
||||||
|
import androidx.fragment.app.DialogFragment
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import com.google.android.material.timepicker.MaterialTimePicker
|
||||||
|
import com.google.android.material.timepicker.TimeFormat.CLOCK_12H
|
||||||
|
import com.google.android.material.timepicker.TimeFormat.CLOCK_24H
|
||||||
|
import com.todoroo.andlib.utility.DateUtilities.now
|
||||||
|
import org.tasks.date.DateTimeUtils.toDateTime
|
||||||
|
import org.tasks.time.DateTime
|
||||||
|
import org.tasks.time.DateTimeUtils.startOfDay
|
||||||
|
|
||||||
|
class MyTimePickerDialog : DialogFragment() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
val fragment =
|
||||||
|
(childFragmentManager.findFragmentByTag(FRAG_TAG_TIME_PICKER) as? MaterialTimePicker)
|
||||||
|
?: newTimePicker(requireContext(), initial)
|
||||||
|
.let {
|
||||||
|
childFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.add(it, FRAG_TAG_TIME_PICKER)
|
||||||
|
.commit()
|
||||||
|
it
|
||||||
|
}
|
||||||
|
with(fragment) {
|
||||||
|
addOnPositiveButtonClickListener { selected(hour, minute) }
|
||||||
|
addOnNegativeButtonClickListener { cancel() }
|
||||||
|
addOnCancelListener { cancel() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val initial: Long
|
||||||
|
get() = arguments?.getLong(EXTRA_TIMESTAMP) ?: now().startOfDay()
|
||||||
|
|
||||||
|
private fun selected(hour: Int, minute: Int) {
|
||||||
|
targetFragment?.onActivityResult(
|
||||||
|
targetRequestCode,
|
||||||
|
Activity.RESULT_OK,
|
||||||
|
Intent().putExtra(
|
||||||
|
EXTRA_TIMESTAMP,
|
||||||
|
initial
|
||||||
|
.toDateTime()
|
||||||
|
.startOfDay()
|
||||||
|
.withHourOfDay(hour)
|
||||||
|
.withMinuteOfHour(minute)
|
||||||
|
.millis
|
||||||
|
)
|
||||||
|
)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun cancel() {
|
||||||
|
targetFragment?.onActivityResult(targetRequestCode, RESULT_CANCELED, null)
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val FRAG_TAG_TIME_PICKER = "frag_time_picker"
|
||||||
|
const val EXTRA_TIMESTAMP = "extra_timestamp"
|
||||||
|
|
||||||
|
fun newTimePicker(target: Fragment, rc: Int, initial: Long) =
|
||||||
|
MyTimePickerDialog().apply {
|
||||||
|
arguments = Bundle().apply {
|
||||||
|
putLong(EXTRA_TIMESTAMP, initial)
|
||||||
|
}
|
||||||
|
setTargetFragment(target, rc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun newTimePicker(context: Context?, initial: Long) = DateTime(initial).let {
|
||||||
|
MaterialTimePicker.Builder()
|
||||||
|
.setTimeFormat(if (is24HourFormat(context)) CLOCK_24H else CLOCK_12H)
|
||||||
|
.setHour(it.hourOfDay)
|
||||||
|
.setMinute(it.minuteOfHour)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2020 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ 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.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?attr/colorOnSecondary" android:state_selected="true" />
|
||||||
|
<item android:color="?attr/colorOnSurface" />
|
||||||
|
</selector>
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2019 The Android Open Source Project
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- 24% opacity -->
|
||||||
|
<item android:alpha="0.24" android:color="?attr/colorSecondary" android:state_enabled="true" android:state_selected="true"/>
|
||||||
|
<item android:alpha="0.24" android:color="?attr/colorSecondary" android:state_enabled="true" android:state_checked="true"/>
|
||||||
|
<!-- 12% of 87% opacity -->
|
||||||
|
<item android:alpha="0.10" android:color="?attr/colorOnSurface" android:state_enabled="true"/>
|
||||||
|
<item android:alpha="0.12" android:color="?attr/colorOnSurface"/>
|
||||||
|
|
||||||
|
</selector>
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2019 The Android Open Source Project
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
https://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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item android:color="?attr/colorSecondary" android:state_enabled="true" android:state_selected="true"/>
|
||||||
|
<item android:color="?attr/colorSecondary" android:state_enabled="true" android:state_checked="true"/>
|
||||||
|
<!-- 87% opacity. -->
|
||||||
|
<item android:alpha="0.87" android:color="?attr/colorOnSurface" android:state_enabled="true"/>
|
||||||
|
<!-- 38% of 87% opacity. -->
|
||||||
|
<item android:alpha="0.33" android:color="?attr/colorOnSurface"/>
|
||||||
|
|
||||||
|
</selector>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright 2018 The Android Open Source Project
|
||||||
|
~
|
||||||
|
~ 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
|
||||||
|
~
|
||||||
|
~ https://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.
|
||||||
|
-->
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:color="?attr/colorSecondary" android:state_focused="true"/>
|
||||||
|
<!-- 4% overlay over 42% colorOnSurface -->
|
||||||
|
<item android:alpha="0.46" android:color="?attr/colorOnSurface" android:state_hovered="true"/>
|
||||||
|
<item android:alpha="0.38" android:color="?attr/colorOnSurface" android:state_enabled="false"/>
|
||||||
|
<item android:alpha="0.42" android:color="?attr/colorOnSurface"/>
|
||||||
|
</selector>
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
Copyright 2017 The Android Open Source Project
|
||||||
|
|
||||||
|
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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:alpha="1.00" android:color="?attr/colorSecondary" android:state_checkable="true" android:state_checked="true" android:state_enabled="true"/>
|
||||||
|
<item android:alpha="0.60" android:color="?attr/colorOnSurface" android:state_checkable="true" android:state_checked="false" android:state_enabled="true"/>
|
||||||
|
<item android:alpha="1.00" android:color="?attr/colorSecondary" android:state_enabled="true"/>
|
||||||
|
<item android:alpha="0.38" android:color="?attr/colorOnSurface"/>
|
||||||
|
</selector>
|
||||||
Loading…
Reference in New Issue