diff options
author | Stefan Niedermann <info@niedermann.it> | 2024-01-14 13:18:03 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2024-01-14 13:18:03 +0300 |
commit | 8471bf3b4dea916ed87f70c6b497bb18336723e3 (patch) | |
tree | 032d730d37f4a1a044a760a5dae41a660afecc25 /app/src/main | |
parent | 8ab9514b4a129d7d0db55811e785be367c86a176 (diff) |
feat(done): Enhance due date / done UX in board view
- 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>
Diffstat (limited to 'app/src/main')
14 files changed, 135 insertions, 86 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java index da9790a0d..7653f4ff3 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java @@ -22,7 +22,6 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; import org.jetbrains.annotations.Contract; import java.time.Instant; -import java.time.ZoneId; import java.util.List; import java.util.stream.Collectors; @@ -32,10 +31,9 @@ import it.niedermann.nextcloud.deck.model.Card; 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; @@ -91,7 +89,7 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { }); } - protected abstract TextView getCardDueDate(); + protected abstract DueDateChip getCardDueDate(); protected abstract ImageView getNotSyncedYet(); @@ -113,12 +111,14 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { 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) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java index cf947c90b..7c53451a5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java @@ -19,6 +19,7 @@ import it.niedermann.nextcloud.deck.model.Account; 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; @@ -63,7 +64,7 @@ public class CompactCardViewHolder extends AbstractCardViewHolder { } @Override - protected TextView getCardDueDate() { + protected DueDateChip getCardDueDate() { return binding.cardDueDate; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardOnlyTitleViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardOnlyTitleViewHolder.java index 27aacc614..1b6b2b3a0 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardOnlyTitleViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardOnlyTitleViewHolder.java @@ -12,6 +12,7 @@ import androidx.annotation.Nullable; 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; @@ -35,7 +36,7 @@ public class DefaultCardOnlyTitleViewHolder extends AbstractCardViewHolder { } @Override - protected TextView getCardDueDate() { + protected DueDateChip getCardDueDate() { return binding.cardDueDate; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java index dc6e5a956..083480736 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java @@ -19,6 +19,7 @@ import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultBinding; 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; @@ -93,7 +94,7 @@ public class DefaultCardViewHolder extends AbstractCardViewHolder { } @Override - protected TextView getCardDueDate() { + protected DueDateChip getCardDueDate() { return binding.cardDueDate; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java index d1b1535fd..d52b78f96 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/DeckViewThemeUtils.java @@ -2,7 +2,6 @@ package it.niedermann.nextcloud.deck.ui.theme; 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; @@ -11,7 +10,6 @@ import android.graphics.drawable.LayerDrawable; 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; @@ -23,7 +21,6 @@ import androidx.annotation.Nullable; 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; @@ -35,7 +32,6 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; 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; @@ -195,39 +191,6 @@ public class DeckViewThemeUtils extends ViewThemeUtilsBase { 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); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/DueDateChip.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/DueDateChip.java new file mode 100644 index 000000000..568dfb053 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/DueDateChip.java @@ -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); + } + } +}
\ No newline at end of file diff --git a/app/src/main/res/drawable/due_done_background.xml b/app/src/main/res/drawable/due_done_background.xml deleted file mode 100644 index cdc93a0b9..000000000 --- a/app/src/main/res/drawable/due_done_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@color/due_done" /> - - <corners - android:radius="4dp" /> -</shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/due_overdue_background.xml b/app/src/main/res/drawable/due_overdue_background.xml deleted file mode 100644 index 658797d91..000000000 --- a/app/src/main/res/drawable/due_overdue_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@color/due_overdue" /> - - <corners - android:radius="4dp" /> -</shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/due_today_background.xml b/app/src/main/res/drawable/due_today_background.xml deleted file mode 100644 index fc0a6a727..000000000 --- a/app/src/main/res/drawable/due_today_background.xml +++ /dev/null @@ -1,7 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android"> - <solid android:color="@color/due_today" /> - - <corners - android:radius="4dp" /> -</shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/ic_time_24.xml b/app/src/main/res/drawable/ic_time_24.xml new file mode 100644 index 000000000..e0355a270 --- /dev/null +++ b/app/src/main/res/drawable/ic_time_24.xml @@ -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> diff --git a/app/src/main/res/drawable/ic_time_filled_24.xml b/app/src/main/res/drawable/ic_time_filled_24.xml new file mode 100644 index 000000000..77d3da030 --- /dev/null +++ b/app/src/main/res/drawable/ic_time_filled_24.xml @@ -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> diff --git a/app/src/main/res/layout/item_card_compact.xml b/app/src/main/res/layout/item_card_compact.xml index 3a14b0d43..16336d4d4 100644 --- a/app/src/main/res/layout/item_card_compact.xml +++ b/app/src/main/res/layout/item_card_compact.xml @@ -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 diff --git a/app/src/main/res/layout/item_card_default.xml b/app/src/main/res/layout/item_card_default.xml index 7454129f4..a46582418 100644 --- a/app/src/main/res/layout/item_card_default.xml +++ b/app/src/main/res/layout/item_card_default.xml @@ -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> diff --git a/app/src/main/res/layout/item_card_default_only_title.xml b/app/src/main/res/layout/item_card_default_only_title.xml index 1739a2905..145023167 100644 --- a/app/src/main/res/layout/item_card_default_only_title.xml +++ b/app/src/main/res/layout/item_card_default_only_title.xml @@ -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 |