diff options
author | Stefan Niedermann <info@niedermann.it> | 2024-01-15 19:08:58 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2024-01-15 19:08:58 +0300 |
commit | c97057858e03f0cfc0318a0acbc5dad94fabe7e7 (patch) | |
tree | 3ed1a2d83606cb16a1ec526ab1c11acca4baaa91 /app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java | |
parent | 35f94be61a18013a42cd5b1d1c2955b28a26d405 (diff) |
feat(done): Show done state in card detail view
Refs: #1556
Signed-off-by: Stefan Niedermann <info@niedermann.it>
Diffstat (limited to 'app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java')
-rw-r--r-- | app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java | 292 |
1 files changed, 292 insertions, 0 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java new file mode 100644 index 000000000..1d264d35f --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java @@ -0,0 +1,292 @@ +package it.niedermann.nextcloud.deck.ui.card.details; + +import android.content.Context; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.FragmentManager; + +import com.nextcloud.android.common.ui.theme.utils.ColorRole; +import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; +import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.LocalTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.stream.Stream; + +import it.niedermann.nextcloud.deck.databinding.ViewCardDueDateBinding; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.Themed; +import it.niedermann.nextcloud.deck.ui.theme.ThemedDatePickerDialog; +import it.niedermann.nextcloud.deck.ui.theme.ThemedTimePickerDialog; + +public class CardDueDateView extends FrameLayout implements DatePickerDialog.OnDateSetListener, TimePickerDialog.OnTimeSetListener, Themed { + + private final ViewCardDueDateBinding binding; + @Nullable + private DueDateChangedListener dueDateChangedListener; + private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); + private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); + @Nullable + private Instant dueDate = null; + @Nullable + private Instant done = null; + @Nullable + @ColorInt + private Integer color = null; + private FragmentManager fragmentManager = null; + + public CardDueDateView(Context context) { + super(context); + binding = ViewCardDueDateBinding.inflate(LayoutInflater.from(context), this, true); + } + + public CardDueDateView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + binding = ViewCardDueDateBinding.inflate(LayoutInflater.from(context), this, true); + } + + public CardDueDateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + binding = ViewCardDueDateBinding.inflate(LayoutInflater.from(context), this, true); + } + + public CardDueDateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + binding = ViewCardDueDateBinding.inflate(LayoutInflater.from(context), this, true); + } + + @Override + public void setEnabled(boolean enabled) { + if (enabled != isEnabled()) { + super.setEnabled(enabled); + render(); + } + } + + public void setDueDate(@NonNull FragmentManager fragmentManager, @Nullable Instant dueDate, @Nullable Instant done) { + this.fragmentManager = fragmentManager; + this.dueDate = dueDate; + this.done = done; + render(); + } + + private void render() { + setVisibilityState(); + setTextState(); + setInteraction(); + } + + private void setInteraction() { + final var enabled = isEnabled(); + + binding.dueDateDate.setEnabled(enabled); + binding.dueDateTime.setEnabled(enabled); + + if (enabled) { + binding.clearDone.setOnClickListener(v -> { + if (this.dueDateChangedListener != null) { + this.dueDateChangedListener.onDoneChanged(null); + } + }); + + binding.markAsDone.setOnClickListener(v -> { + if (this.dueDateChangedListener != null) { + this.dueDateChangedListener.onDoneChanged(Instant.now()); + } + }); + + binding.clearDueDate.setOnClickListener(v -> { + if (this.dueDateChangedListener != null) { + this.dueDateChangedListener.onDueDateChanged(null); + } + }); + + binding.dueDateDate.setOnClickListener(v -> { + if (this.fragmentManager == null || this.color == null) { + return; + } + final LocalDate date; + if (this.dueDate != null) { + date = this.dueDate.atZone(ZoneId.systemDefault()).toLocalDate(); + } else { + date = LocalDate.now(); + } + + ThemedDatePickerDialog.newInstance(this, date.getYear(), date.getMonthValue(), date.getDayOfMonth(), this.color) + .show(this.fragmentManager, ThemedDatePickerDialog.class.getCanonicalName()); + }); + + binding.dueDateTime.setOnClickListener(v -> { + if (this.fragmentManager == null || this.color == null) { + return; + } + final LocalTime time; + if (this.dueDate != null) { + time = this.dueDate.atZone(ZoneId.systemDefault()).toLocalTime(); + } else { + time = LocalTime.now(); + } + ThemedTimePickerDialog.newInstance(this, time.getHour(), time.getMinute(), true, this.color) + .show(this.fragmentManager, ThemedTimePickerDialog.class.getCanonicalName()); + }); + } else { + Stream.of( + binding.clearDone, + binding.markAsDone, + binding.clearDueDate, + binding.dueDateDate, + binding.dueDateTime + ).forEach(v -> v.setOnClickListener(null)); + } + } + + private void setTextState() { + if (done == null) { + binding.doneDate.setText(null); + binding.doneDueDate.setText(null); + + if (this.dueDate == null) { + binding.dueDateDate.setText(null); + binding.dueDateTime.setText(null); + + } else { + final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); + binding.dueDateDate.setText(dueDate.format(dateFormatter)); + binding.dueDateTime.setText(dueDate.format(timeFormatter)); + } + + } else { + binding.dueDateDate.setText(null); + binding.dueDateTime.setText(null); + + binding.doneDate.setText(done.atZone(ZoneId.systemDefault()).format(dateFormatter)); + + if (this.dueDate == null) { + binding.doneDueDate.setText(null); + + } else { + final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); + binding.doneDueDate.setText(dueDate.format(dateFormatter)); + } + } + } + + private void setVisibilityState() { + if (done == null) { + Stream.of( + binding.markAsDone, + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper + ).forEach(v -> v.setVisibility(View.VISIBLE)); + + Stream.of( + binding.doneCheck, + binding.doneDueDate, + binding.doneDate, + binding.clearDone + ).forEach(v -> v.setVisibility(View.GONE)); + + binding.clearDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); + } else { + + Stream.of( + binding.doneCheck, + binding.doneDate, + binding.clearDone + ).forEach(v -> v.setVisibility(View.VISIBLE)); + + Stream.of( + binding.markAsDone, + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper, + binding.clearDueDate + ).forEach(v -> v.setVisibility(View.GONE)); + + binding.doneDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); + } + } + + @Override + public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) { + int hourOfDay; + int minute; + + final var selectedTime = binding.dueDateTime.getText(); + if (TextUtils.isEmpty(selectedTime)) { + hourOfDay = 0; + minute = 0; + } else { + final var oldTime = LocalTime.from(this.dueDate.atZone(ZoneId.systemDefault())); + hourOfDay = oldTime.getHour(); + minute = oldTime.getMinute(); + } + + final var newDateTime = ZonedDateTime.of( + LocalDate.of(year, monthOfYear + 1, dayOfMonth), + LocalTime.of(hourOfDay, minute), + ZoneId.systemDefault() + ); + this.dueDate = newDateTime.toInstant(); + + if (dueDateChangedListener != null) { + dueDateChangedListener.onDueDateChanged(newDateTime.toInstant()); + } + } + + @Override + public void onTimeSet(TimePickerDialog view, int hourOfDay, int minute, int second) { + final var oldDateTime = this.dueDate == null ? ZonedDateTime.now() : this.dueDate.atZone(ZoneId.systemDefault()); + final var newDateTime = oldDateTime.with( + LocalTime.of(hourOfDay, minute) + ); + + if (dueDateChangedListener != null) { + dueDateChangedListener.onDueDateChanged(newDateTime.toInstant()); + } + } + + @Override + public void applyTheme(int color) { + this.color = color; + final var utils = ThemeUtils.of(color, getContext()); + + Stream.of( + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper + ).forEach(utils.material::colorTextInputLayout); + + Stream.of( + binding.doneDueDate, + binding.doneDate + ).forEach(utils.platform::colorTextView); + + Stream.of( + binding.clearDone, + binding.clearDueDate + ).forEach(v -> utils.platform.colorImageView(v, ColorRole.SECONDARY)); + + utils.material.colorMaterialButtonPrimaryTonal(binding.markAsDone); + } + + public void setDueDateListener(@Nullable DueDateChangedListener dueDateChangedListener) { + this.dueDateChangedListener = dueDateChangedListener; + } + + public interface DueDateChangedListener { + void onDueDateChanged(@Nullable Instant dueDate); + + void onDoneChanged(@Nullable Instant done); + } +} |