Skip to content

Commit

Permalink
feat(done): Enhance due date / done UX in board view
Browse files Browse the repository at this point in the history
- Use βœ”οΈ icon when done is present in favor of πŸ“† / πŸ•™ in board view
- Change icon from πŸ“† to πŸ•™ for due dates
- Move temporal information into own reusable component to encapsulate logic

Refs: #1556

Signed-off-by: Stefan Niedermann <info@niedermann.it>
  • Loading branch information
stefan-niedermann committed Jan 14, 2024
1 parent 8ab9514 commit 8471bf3
Show file tree
Hide file tree
Showing 14 changed files with 135 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.jetbrains.annotations.Contract;

import java.time.Instant;
import java.time.ZoneId;
import java.util.List;
import java.util.stream.Collectors;

Expand All @@ -32,10 +31,9 @@
import it.niedermann.nextcloud.deck.model.User;
import it.niedermann.nextcloud.deck.model.enums.DBStatus;
import it.niedermann.nextcloud.deck.model.full.FullCard;
import it.niedermann.nextcloud.deck.ui.theme.DeckViewThemeUtils;
import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils;
import it.niedermann.nextcloud.deck.ui.view.DueDateChip;
import it.niedermann.nextcloud.deck.util.AttachmentUtil;
import it.niedermann.nextcloud.deck.util.DateUtil;
import it.niedermann.nextcloud.deck.util.MimeTypeUtil;
import it.niedermann.nextcloud.sso.glide.SingleSignOnUrl;

Expand Down Expand Up @@ -91,7 +89,7 @@ public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable
});
}

protected abstract TextView getCardDueDate();
protected abstract DueDateChip getCardDueDate();

protected abstract ImageView getNotSyncedYet();

Expand All @@ -113,12 +111,14 @@ public MaterialCardView getDraggable() {
return getCard();
}

private static void setupDueDate(@NonNull TextView cardDueDate, @NonNull Card card) {
private static void setupDueDate(@NonNull DueDateChip cardDueDate, @NonNull Card card) {
final boolean isDone = card.getDone() != null;
final Instant date = isDone ? card.getDone() : card.getDueDate();

cardDueDate.setText(DateUtil.getRelativeDateTimeString(cardDueDate.getContext(), date.toEpochMilli()));
DeckViewThemeUtils.themeDueDate(cardDueDate, date.atZone(ZoneId.systemDefault()).toLocalDate(), isDone);
if (date == null) {
throw new IllegalArgumentException("Expected due date or done date to be present but both were null.");
}
cardDueDate.setDueDate(date, isDone);
}

protected static void setupCoverImages(@NonNull Account account, @NonNull ViewGroup coverImagesHolder, @NonNull FullCard fullCard, int maxCoverImagesCount) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import it.niedermann.nextcloud.deck.model.Label;
import it.niedermann.nextcloud.deck.model.full.FullCard;
import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils;
import it.niedermann.nextcloud.deck.ui.view.DueDateChip;

public class CompactCardViewHolder extends AbstractCardViewHolder {
private final ItemCardCompactBinding binding;
Expand Down Expand Up @@ -63,7 +64,7 @@ public MaterialCardView getDraggable() {
}

@Override
protected TextView getCardDueDate() {
protected DueDateChip getCardDueDate() {
return binding.cardDueDate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import com.google.android.material.card.MaterialCardView;

import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultOnlyTitleBinding;
import it.niedermann.nextcloud.deck.ui.view.DueDateChip;

public class DefaultCardOnlyTitleViewHolder extends AbstractCardViewHolder {
private final ItemCardDefaultOnlyTitleBinding binding;
Expand All @@ -35,7 +36,7 @@ public MaterialCardView getDraggable() {
}

@Override
protected TextView getCardDueDate() {
protected DueDateChip getCardDueDate() {
return binding.cardDueDate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import it.niedermann.nextcloud.deck.model.Account;
import it.niedermann.nextcloud.deck.model.full.FullCard;
import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils;
import it.niedermann.nextcloud.deck.ui.view.DueDateChip;

public class DefaultCardViewHolder extends AbstractCardViewHolder {
private final ItemCardDefaultBinding binding;
Expand Down Expand Up @@ -93,7 +94,7 @@ public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable
}

@Override
protected TextView getCardDueDate() {
protected DueDateChip getCardDueDate() {
return binding.cardDueDate;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import static com.nextcloud.android.common.ui.util.ColorStateListUtilsKt.buildColorStateList;
import static com.nextcloud.android.common.ui.util.PlatformThemeUtil.isDarkMode;
import static java.time.temporal.ChronoUnit.DAYS;

import android.content.Context;
import android.content.res.ColorStateList;
Expand All @@ -11,7 +10,6 @@
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.AttrRes;
import androidx.annotation.ColorInt;
Expand All @@ -23,7 +21,6 @@
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import androidx.core.widget.TextViewCompat;

import com.google.android.material.search.SearchBar;
import com.google.android.material.search.SearchView;
Expand All @@ -35,7 +32,6 @@
import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils;

import java.lang.reflect.InvocationTargetException;
import java.time.LocalDate;
import java.util.Optional;

import it.niedermann.nextcloud.deck.R;
Expand Down Expand Up @@ -195,39 +191,6 @@ private Optional<LayerDrawable> getStateDrawable(@NonNull StateListDrawable draw
return Optional.empty();
}


@Deprecated(forRemoval = true)
public static void themeDueDate(@NonNull TextView cardDueDate, @NonNull LocalDate date, boolean isDone) {
final var context = cardDueDate.getContext();

@ColorInt @Nullable Integer textColor = null;
@DrawableRes int backgroundDrawable = 0;

if (isDone) {
// due date: done
backgroundDrawable = R.drawable.due_done_background;
textColor = ContextCompat.getColor(context, R.color.due_text_done);
} else {
final long diff = DAYS.between(LocalDate.now(), date);

if (diff == 0) {
// due date: today
backgroundDrawable = R.drawable.due_today_background;
textColor = ContextCompat.getColor(context, R.color.due_text_today);
} else if (diff < 0) {
// due date: overdue
backgroundDrawable = R.drawable.due_overdue_background;
textColor = ContextCompat.getColor(context, R.color.due_text_overdue);
} // else we use default text styling
}

cardDueDate.setBackgroundResource(backgroundDrawable);
if (textColor != null) {
cardDueDate.setTextColor(textColor);
TextViewCompat.setCompoundDrawableTintList(cardDueDate, ColorStateList.valueOf(textColor));
}
}

@Deprecated(forRemoval = true)
public static Drawable getTintedImageView(@NonNull Context context, @DrawableRes int imageId, @ColorInt int color) {
final var drawable = ContextCompat.getDrawable(context, imageId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package it.niedermann.nextcloud.deck.ui.view;

import static java.time.temporal.ChronoUnit.DAYS;

import android.content.Context;
import android.content.res.ColorStateList;
import android.util.AttributeSet;
import android.util.TypedValue;

import androidx.annotation.ColorInt;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.core.content.ContextCompat;

import com.google.android.material.chip.Chip;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;

import it.niedermann.android.util.DimensionUtil;
import it.niedermann.nextcloud.deck.R;
import it.niedermann.nextcloud.deck.util.DateUtil;

public class DueDateChip extends Chip {

protected @ColorInt int colorOnSurface;

public DueDateChip(Context context) {
super(context);
initialize();
}

public DueDateChip(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}

public DueDateChip(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize();
}

private void initialize() {
setEnsureMinTouchTargetSize(false);
setMinHeight(0);
setChipMinHeight(0);
@Px final var padding = DimensionUtil.INSTANCE.dpToPx(getContext(), R.dimen.spacer_1x);
setPadding(padding, padding, padding, padding);
setClickable(false);

final var typedValue = new TypedValue();
final var theme = getContext().getTheme();
theme.resolveAttribute(R.attr.colorOnSurface, typedValue, true);
this.colorOnSurface = typedValue.data;
}

public void setDueDate(@NonNull Instant date, boolean isDone) {
setText(DateUtil.getRelativeDateTimeString(getContext(), date.toEpochMilli()));

@DrawableRes final int chipIconRes;
@Nullable @ColorRes final Integer textColorRes;
@ColorRes final int backgroundColorRes;

if (isDone) { // Done
chipIconRes = R.drawable.ic_check_white_24dp;
backgroundColorRes = R.color.due_done;
textColorRes = R.color.due_text_done;

} else {
final long diff = DAYS.between(LocalDate.now(), date.atZone(ZoneId.systemDefault()).toLocalDate());

if (diff == 0) { // Today
chipIconRes = R.drawable.ic_time_24;
backgroundColorRes = R.color.due_today;
textColorRes = R.color.due_text_today;

} else if (diff < 0) { // Overdue
chipIconRes = R.drawable.ic_time_filled_24;
backgroundColorRes = R.color.due_overdue;
textColorRes = R.color.due_text_overdue;

} else { // Future
chipIconRes = R.drawable.ic_time_24;
backgroundColorRes = android.R.color.transparent;
textColorRes = null;
}
}

setChipIcon(ContextCompat.getDrawable(getContext(), chipIconRes));
setChipBackgroundColorResource(backgroundColorRes);

if (textColorRes == null) {
setTextColor(colorOnSurface);
setChipIconTint(ColorStateList.valueOf(colorOnSurface));

} else {
setTextColor(ContextCompat.getColor(getContext(), textColorRes));
setChipIconTintResource(textColorRes);
}
}
}
7 changes: 0 additions & 7 deletions app/src/main/res/drawable/due_done_background.xml

This file was deleted.

7 changes: 0 additions & 7 deletions app/src/main/res/drawable/due_overdue_background.xml

This file was deleted.

7 changes: 0 additions & 7 deletions app/src/main/res/drawable/due_today_background.xml

This file was deleted.

6 changes: 6 additions & 0 deletions app/src/main/res/drawable/ic_time_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="?attr/colorOnSurface" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
<path android:fillColor="@android:color/white" android:pathData="M12.5,7H11v6l5.25,3.15 0.75,-1.23 -4.5,-2.67z"/>
</vector>
5 changes: 5 additions & 0 deletions app/src/main/res/drawable/ic_time_filled_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector android:autoMirrored="true" android:height="24dp"
android:tint="?attr/colorOnSurface" android:viewportHeight="24"
android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM15.29,16.71L11,12.41V7h2v4.59l3.71,3.71L15.29,16.71z"/>
</vector>
8 changes: 2 additions & 6 deletions app/src/main/res/layout/item_card_compact.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,11 @@
app:srcCompat="@drawable/ic_sync_blue_24dp"
tools:visibility="visible" />

<com.google.android.material.textview.MaterialTextView
<it.niedermann.nextcloud.deck.ui.view.DueDateChip
android:id="@+id/card_due_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/spacer_1hx"
android:gravity="center"
android:paddingHorizontal="@dimen/spacer_1x"
android:paddingVertical="@dimen/spacer_1hx"
app:drawableStartCompat="@drawable/calendar_blank_grey600_24dp"
tools:chipIcon="@drawable/ic_time_24"
tools:text="tomorrow" />

<ImageView
Expand Down
8 changes: 2 additions & 6 deletions app/src/main/res/layout/item_card_default.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,11 @@
app:srcCompat="@drawable/ic_sync_blue_24dp"
tools:visibility="visible" />

<androidx.appcompat.widget.AppCompatTextView
<it.niedermann.nextcloud.deck.ui.view.DueDateChip
android:id="@+id/card_due_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/spacer_1hx"
android:gravity="center"
android:paddingHorizontal="@dimen/spacer_1x"
android:paddingVertical="@dimen/spacer_1hx"
app:drawableStartCompat="@drawable/calendar_blank_grey600_24dp"
tools:chipIcon="@drawable/ic_time_24"
tools:text="tomorrow" />
</LinearLayout>

Expand Down
8 changes: 2 additions & 6 deletions app/src/main/res/layout/item_card_default_only_title.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,11 @@
app:srcCompat="@drawable/ic_sync_blue_24dp"
tools:visibility="visible" />

<com.google.android.material.textview.MaterialTextView
<it.niedermann.nextcloud.deck.ui.view.DueDateChip
android:id="@+id/card_due_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="@dimen/spacer_1hx"
android:gravity="center"
android:paddingHorizontal="@dimen/spacer_1x"
android:paddingVertical="@dimen/spacer_1hx"
app:drawableStartCompat="@drawable/calendar_blank_grey600_24dp"
tools:chipIcon="@drawable/ic_time_24"
tools:text="tomorrow" />

<ImageView
Expand Down

0 comments on commit 8471bf3

Please sign in to comment.