diff options
author | Niedermann IT-Dienstleistungen <stefan-niedermann@users.noreply.github.com> | 2024-01-19 23:51:42 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-19 23:51:42 +0300 |
commit | 3ae26363d56157a0869dd6717ce6e069a4a8cc44 (patch) | |
tree | 915b29251d207582d6d5a7666f1eaf720e2870f5 /app/src | |
parent | 8e38a9dbb28630619d40a722c9eff6fc50b30908 (diff) | |
parent | 01b4549095a30d5724c09b3d9dc320b71fb24eb4 (diff) |
Merge pull request #1563 from stefan-niedermann/mark-card-as-done
feat: Mark card as done
Diffstat (limited to 'app/src')
77 files changed, 1114 insertions, 477 deletions
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java index 5d3b28950..a95ab7a8c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/DataBaseAdapter.java @@ -60,6 +60,7 @@ import it.niedermann.nextcloud.deck.model.Stack; import it.niedermann.nextcloud.deck.model.User; import it.niedermann.nextcloud.deck.model.appwidgets.StackWidgetModel; import it.niedermann.nextcloud.deck.model.enums.DBStatus; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.model.full.FullCard; @@ -259,9 +260,9 @@ public class DataBaseAdapter { public LiveData<List<FullCard>> getFullCardsForStack(long accountId, long localStackId, @Nullable FilterInformation filter) { return new ReactiveLiveData<>( - filter == null - ? db.getCardDao().getFullCardsForStack(accountId, localStackId) - : db.getCardDao().getFilteredFullCardsForStack(getQueryForFilter(filter, accountId, localStackId))) + FilterInformation.hasActiveFilter(filter) + ? db.getCardDao().getFilteredFullCardsForStack(getQueryForFilter(filter, accountId, localStackId)) + : db.getCardDao().getFullCardsForStack(accountId, localStackId)) .tap(this::filterRelationsForCard, executor) .distinctUntilChanged(); @@ -284,9 +285,9 @@ public class DataBaseAdapter { @WorkerThread public List<FullCard> getFullCardsForStackDirectly(long accountId, long localStackId, @Nullable FilterInformation filter) { - return filter == null - ? db.getCardDao().getFullCardsForStackDirectly(accountId, localStackId) - : db.getCardDao().getFilteredFullCardsForStackDirectly(getQueryForFilter(filter, accountId, localStackId)); + return FilterInformation.hasActiveFilter(filter) + ? db.getCardDao().getFilteredFullCardsForStackDirectly(getQueryForFilter(filter, accountId, localStackId)) + : db.getCardDao().getFullCardsForStackDirectly(accountId, localStackId); } @AnyThread @@ -351,24 +352,38 @@ public class DataBaseAdapter { if (filter.getDueType() != EDueType.NO_FILTER) { switch (filter.getDueType()) { case NO_DUE: - query.append("and c.dueDate is null"); + query.append("and c.dueDate is null "); break; case OVERDUE: - query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') <= datetime('now', 'localtime')"); + query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') <= datetime('now', 'localtime') "); break; case TODAY: - query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') between datetime('now', 'localtime') and datetime('now', '+24 hour', 'localtime')"); + query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') between datetime('now', 'localtime') and datetime('now', '+24 hour', 'localtime') "); break; case WEEK: - query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') between datetime('now', 'localtime') and datetime('now', '+7 day', 'localtime')"); + query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') between datetime('now', 'localtime') and datetime('now', '+7 day', 'localtime') "); break; case MONTH: - query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') between datetime('now', 'localtime') and datetime('now', '+30 day', 'localtime')"); + query.append("and datetime(c.duedate/1000, 'unixepoch', 'localtime') between datetime('now', 'localtime') and datetime('now', '+30 day', 'localtime') "); break; default: - throw new IllegalArgumentException("You need to add your new EDueType value\"" + filter.getDueType() + "\" here!"); + throw new IllegalArgumentException("You need to add your new " + EDueType.class.getSimpleName() + " value\"" + filter.getDueType() + "\" here!"); } } + + if (filter.getDoneType() != EDoneType.NO_FILTER) { + switch (filter.getDoneType()) { + case DONE: + query.append("and (c.done is not null and c.done != 0) "); + break; + case UNDONE: + query.append("and (c.done is null or c.done = 0) "); + break; + default: + throw new IllegalArgumentException("You need to add your new " + EDoneType.class.getSimpleName() + " value\"" + filter.getDueType() + "\" here!"); + } + } + if (!TextUtils.isEmpty(filter.getFilterText())) { query.append(" and (c.description like ? or c.title like ?) "); String filterText = "%" + filter.getFilterText() + "%"; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/DeckDatabase.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/DeckDatabase.java index b71abece1..a41379e8d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/DeckDatabase.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/DeckDatabase.java @@ -65,6 +65,7 @@ import it.niedermann.nextcloud.deck.database.migration.Migration_28_29; import it.niedermann.nextcloud.deck.database.migration.Migration_29_30; import it.niedermann.nextcloud.deck.database.migration.Migration_30_31; import it.niedermann.nextcloud.deck.database.migration.Migration_31_32; +import it.niedermann.nextcloud.deck.database.migration.Migration_32_33; import it.niedermann.nextcloud.deck.database.migration.Migration_8_9; import it.niedermann.nextcloud.deck.database.migration.Migration_9_10; import it.niedermann.nextcloud.deck.model.AccessControl; @@ -135,7 +136,7 @@ import it.niedermann.nextcloud.deck.remote.api.LastSyncUtil; FilterWidgetSort.class, }, exportSchema = false, - version = 32 + version = 33 ) @TypeConverters({DateTypeConverter.class, EnumConverter.class}) public abstract class DeckDatabase extends RoomDatabase { @@ -188,6 +189,7 @@ public abstract class DeckDatabase extends RoomDatabase { .addMigrations(new Migration_29_30(context)) .addMigrations(new Migration_30_31()) .addMigrations(new Migration_31_32(context)) + .addMigrations(new Migration_32_33()) .fallbackToDestructiveMigration() .addCallback(ON_CREATE_CALLBACK) .build(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java index 5d25f943f..0f067381e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/dao/CardDao.java @@ -23,7 +23,8 @@ public interface CardDao extends GenericDao<Card> { "and (c.deletedAt is null or c.deletedAt = 0) " + "and (s.deletedAt is null or s.deletedAt = 0) " + "and (b.deletedAt is null or b.deletedAt = 0) " + - // FUll Logic: (hasDueDate AND isIn_PRIVATE_Board) OR (isInSharedBoard AND (assignedToMe OR (hasDueDate AND noAssignees))) + "and (c.done is null or c.done = 0) " + + // Full Logic: (hasDueDate AND isIn_PRIVATE_Board) OR (isInSharedBoard AND (assignedToMe OR (hasDueDate AND noAssignees))) "and (" + "(c.dueDate is not null AND NOT exists(select 1 from AccessControl ac where ac.boardId = b.localId and ac.status <> 3))" + //(hasDueDate AND isInPrivateBoard) "OR (" + diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java b/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java new file mode 100644 index 000000000..d6938dda6 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/database/migration/Migration_32_33.java @@ -0,0 +1,25 @@ +package it.niedermann.nextcloud.deck.database.migration; + +import androidx.annotation.NonNull; +import androidx.room.migration.Migration; +import androidx.sqlite.db.SupportSQLiteDatabase; + +/** + * Adds support for marking a card as done: https://github.com/stefan-niedermann/nextcloud-deck/issues/1556 + */ +public class Migration_32_33 extends Migration { + + public Migration_32_33() { + super(32, 33); + } + + @Override + public void migrate(@NonNull SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE `Card` add column done INTEGER"); + // Reset ETags: Refetch all cards to support Done state which did not change ETags + database.execSQL("UPDATE `Account` SET `boardsEtag` = NULL"); + database.execSQL("UPDATE `Board` SET `etag` = NULL"); + database.execSQL("UPDATE `Stack` SET `etag` = NULL"); + database.execSQL("UPDATE `Card` SET `etag` = NULL"); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/Card.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/Card.java index a5650f124..676753245 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/Card.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/Card.java @@ -57,6 +57,7 @@ public class Card extends AbstractRemoteEntity { private String type; private Instant createdAt; private Instant deletedAt; + private Instant done; private int attachmentCount; private Long userId; @@ -91,6 +92,7 @@ public class Card extends AbstractRemoteEntity { this.order = card.getOrder(); this.archived = card.isArchived(); this.dueDate = card.getDueDate(); + this.done = card.getDone(); this.notified = card.isNotified(); this.overdue = card.getOverdue(); this.commentsUnread = card.getCommentsUnread(); @@ -252,6 +254,14 @@ public class Card extends AbstractRemoteEntity { return this.order; } + public Instant getDone() { + return done; + } + + public void setDone(Instant done) { + this.done = done; + } + @Override public boolean equals(Object o) { if (this == o) return true; @@ -274,6 +284,8 @@ public class Card extends AbstractRemoteEntity { return false; if (deletedAt != null ? !deletedAt.equals(card.deletedAt) : card.deletedAt != null) return false; + if (done != null ? !done.equals(card.done) : card.done != null) + return false; if (userId != null ? !userId.equals(card.userId) : card.userId != null) return false; return dueDate != null ? dueDate.equals(card.dueDate) : card.dueDate == null; } @@ -286,6 +298,7 @@ public class Card extends AbstractRemoteEntity { result = 31 * result + (type != null ? type.hashCode() : 0); result = 31 * result + (createdAt != null ? createdAt.hashCode() : 0); result = 31 * result + (deletedAt != null ? deletedAt.hashCode() : 0); + result = 31 * result + (done != null ? done.hashCode() : 0); result = 31 * result + attachmentCount; result = 31 * result + (userId != null ? userId.hashCode() : 0); result = 31 * result + order; @@ -306,6 +319,7 @@ public class Card extends AbstractRemoteEntity { ", type='" + type + '\'' + ", createdAt=" + createdAt + ", deletedAt=" + deletedAt + + ", done=" + done + ", attachmentCount=" + attachmentCount + ", userId=" + userId + ", order=" + order + diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/enums/EDoneType.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/enums/EDoneType.java new file mode 100644 index 000000000..4222ee487 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/enums/EDoneType.java @@ -0,0 +1,40 @@ +package it.niedermann.nextcloud.deck.model.enums; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; + +import it.niedermann.nextcloud.deck.R; + +public enum EDoneType { + NO_FILTER(1, R.string.filter_done_no_filter), + DONE(2, R.string.filter_done_done), + UNDONE(3, R.string.filter_done_undone); + + private final int value; + private final int id; + + EDoneType(int id, @StringRes int value) { + this.value = value; + this.id = id; + } + + public int getId() { + return id; + } + + public static EDoneType findById(int id) { + for (EDoneType s : EDoneType.values()) { + if (s.getId() == id) { + return s; + } + } + throw new IllegalArgumentException("unknown " + EDoneType.class.getSimpleName() + " key: " + id); + } + + @NonNull + public String toString(Context context) { + return context.getString(this.value); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java index da0501779..8c7da2b24 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java @@ -9,6 +9,7 @@ import java.util.List; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.User; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.ocs.projects.OcsProject; @@ -20,6 +21,8 @@ public class FilterInformation implements Serializable { @NonNull private EDueType dueType = EDueType.NO_FILTER; + @NonNull + private EDoneType doneType = EDoneType.NO_FILTER; private boolean noAssignedLabel = false; private boolean noAssignedUser = false; private boolean noAssignedProject = false; @@ -41,6 +44,7 @@ public class FilterInformation implements Serializable { public FilterInformation(@Nullable FilterInformation filterInformation) { if (filterInformation != null) { this.dueType = filterInformation.getDueType(); + this.doneType = filterInformation.getDoneType(); this.archiveStatus = filterInformation.getArchiveStatus(); this.users.addAll(filterInformation.getUsers()); this.labels.addAll(filterInformation.getLabels()); @@ -72,6 +76,15 @@ public class FilterInformation implements Serializable { } @NonNull + public EDoneType getDoneType() { + return doneType; + } + + public void setDoneType(@NonNull EDoneType doneType) { + this.doneType = doneType; + } + + @NonNull public List<User> getUsers() { return users; } @@ -152,6 +165,7 @@ public class FilterInformation implements Serializable { public String toString() { return "FilterInformation{" + "dueType=" + dueType + + ", doneType=" + doneType + ", noAssignedLabel=" + noAssignedLabel + ", noAssignedUser=" + noAssignedUser + ", users=" + users + @@ -169,6 +183,7 @@ public class FilterInformation implements Serializable { return false; } return !(filterInformation.getDueType() == EDueType.NO_FILTER + && filterInformation.getDoneType() == EDoneType.NO_FILTER && filterInformation.getUsers().isEmpty() && filterInformation.getProjects().isEmpty() && filterInformation.getLabels().isEmpty() diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java index 355307ce0..7a6aba31d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java @@ -9,6 +9,7 @@ import java.util.regex.Pattern; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Attachment; +import it.niedermann.nextcloud.deck.model.Card; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; public class Version implements Comparable<Version> { @@ -17,6 +18,8 @@ public class Version implements Comparable<Version> { private static final Version VERSION_1_0_0 = new Version("1.0.0", 1, 0, 0); private static final Version VERSION_1_0_3 = new Version("1.0.3", 1, 0, 3); private static final Version VERSION_1_3_0 = new Version("1.3.0", 1, 3, 0); + private static final Version VERSION_1_12_0 = new Version("1.12.0", 1, 12, 0); + private static final Version VERSION_1_12_2 = new Version("1.12.2", 1, 12, 2); private String originalVersion = "?"; private final int major; @@ -137,7 +140,7 @@ public class Version implements Comparable<Version> { } /** - * {@link DeckComment} API only available starting with {@link Version} 1.0.0-alpha1 + * {@link DeckComment} API only available starting with {@link Version} <code>1.0.0-alpha1</code> * * @return whether or not the server supports the {@link DeckComment} API */ @@ -147,12 +150,12 @@ public class Version implements Comparable<Version> { /** * Replying to a {@link DeckComment} does cause synchronization errors because the API expected the - * <code>parentId</code> to be a {@link String} up until {@link Version} 1.0.3 - * https://github.com/nextcloud/deck/issues/1831#issuecomment-627207849 + * <code>parentId</code> to be a {@link String} up until {@link #VERSION_1_0_3} * * @return whether or not the server supports replying to comments + * @see <a href="https://github.com/nextcloud/deck/issues/1831#issuecomment-627207849">Deck server issue #1831</a> */ - public boolean supportsCommentsReplys() { + public boolean supportsCommentsReplies() { return isGreaterOrEqualTo(VERSION_1_0_3); } @@ -171,15 +174,25 @@ public class Version implements Comparable<Version> { } /** - * Title max length has been increased from 100 to 255 characters beginning with server {@link Version} 1.0.0 + * Cards started to have an additional property called <a href="https://github.com/nextcloud/deck/pull/4137"><code>done</code></a> with version <a href="https://github.com/nextcloud/deck/releases/tag/v1.12.0">{@link #VERSION_1_12_0}</a> of the Deck server app. + * However, there was an <a href="https://github.com/nextcloud/deck/issues/534#issuecomment-1892061055">issue that would have required to call a second endpoint when marking a card as <code>undone</code></a> which was <a href="https://github.com/nextcloud/deck/pull/5491">fixed</a> in {@link #VERSION_1_12_2}. + * We therefore support the <code>done</code> property only starting with {@link #VERSION_1_12_2}. + * + * @return whether or not the server supports the {@link Card#getDone()} state + * @see <a href="https://github.com/nextcloud/deck/issues/534">Deck server issue #534</a> + */ + public boolean supportsDone() { + return isGreaterOrEqualTo(VERSION_1_12_2); + } + + /** + * Title max length has been increased from <code>100</code> to <code>255</code> characters beginning with server {@link #VERSION_1_0_0} * * @return the number of characters that the title fields of cards allow * @see <a href="https://github.com/stefan-niedermann/nextcloud-deck/issues/422">issue</a> */ public int getCardTitleMaxLength() { - return isGreaterOrEqualTo(VERSION_1_0_0) - ? 255 - : 100; + return isGreaterOrEqualTo(VERSION_1_0_0) ? 255 : 100; } /** @@ -194,15 +207,19 @@ public class Version implements Comparable<Version> { } /** - * URL to view a card in the web interface has been changed in {@link Version} 1.0.0 + * URL to view a card in the web interface has been changed in {@link #VERSION_1_0_0} * * @return the id of the string resource which contains the partial URL to open a card in the web UI * @see <a href="https://github.com/nextcloud/deck/pull/1977">documentation in PR</a> */ @StringRes public int getShareLinkResource() { - return isGreaterOrEqualTo(VERSION_1_0_0) - ? R.string.url_fragment_share_card_since_1_0_0 - : R.string.url_fragment_share_card_pre_1_0_0; + if (isGreaterOrEqualTo(VERSION_1_12_0)) { // Probably even earlier, but there are likely redirects + return R.string.url_fragment_share_card_since_1_12_0; + } else if (isGreaterOrEqualTo(VERSION_1_0_0)) { + return R.string.url_fragment_share_card_since_1_0_0; + } else { + return R.string.url_fragment_share_card_pre_1_0_0; + } } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/propagation/CardUpdate.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/propagation/CardUpdate.java index b13b84e36..389e26489 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/propagation/CardUpdate.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/propagation/CardUpdate.java @@ -27,6 +27,7 @@ public class CardUpdate extends Card { setAccountId(card.getAccountId()); setId(card.getId()); setLocalId(card.getLocalId()); + setDone(card.getCard().getDone()); } public User getOwner() { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/JsonToEntityParser.java b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/JsonToEntityParser.java index 04a0b706e..5c2a83dff 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/JsonToEntityParser.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/remote/api/JsonToEntityParser.java @@ -408,6 +408,7 @@ public class JsonToEntityParser { card.setLastModified(getTimestampFromLong(e.get("lastModified"))); card.setCreatedAt(getTimestampFromLong(e.get("createdAt"))); card.setDeletedAt(getTimestampFromLong(e.get("deletedAt"))); + card.setDone(getTimestampFromString(e.get("done"))); if (e.has("labels") && !e.get("labels").isJsonNull()) { JsonArray labelsJson = e.getAsJsonArray("labels"); List<Label> labels = new ArrayList<>(); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java index 7f976970a..2f9671ce5 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherDialog.java @@ -24,7 +24,6 @@ import java.util.Objects; import java.util.stream.Collectors; import it.niedermann.android.reactivelivedata.ReactiveLiveData; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogAccountSwitcherBinding; import it.niedermann.nextcloud.deck.ui.ImportAccountActivity; @@ -76,7 +75,7 @@ public class AccountSwitcherDialog extends DialogFragment { binding.accountHost.setText(Uri.parse(currentAccount.getUrl()).getHost()); Glide.with(requireContext()) - .load(currentAccount.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.currentAccountItemAvatar.getContext(), R.dimen.avatar_size))) + .load(currentAccount.getAvatarUrl(binding.currentAccountItemAvatar.getResources().getDimensionPixelSize(R.dimen.avatar_size))) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_baseline_account_circle_24) .error(R.drawable.ic_baseline_account_circle_24) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java index 6d635b9d6..f3c61c3ce 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/accountswitcher/AccountSwitcherViewHolder.java @@ -11,7 +11,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemAccountChooseBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -34,7 +33,7 @@ public class AccountSwitcherViewHolder extends RecyclerView.ViewHolder { ); binding.accountHost.setText(Uri.parse(account.getUrl()).getHost()); Glide.with(itemView.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.accountItemAvatar.getContext(), R.dimen.avatar_size))) + .load(account.getAvatarUrl(binding.accountItemAvatar.getResources().getDimensionPixelSize(R.dimen.avatar_size))) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_baseline_account_circle_24) .error(R.drawable.ic_baseline_account_circle_24) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java index bb6add7f4..0c813d04e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlAdapter.java @@ -16,7 +16,6 @@ import java.util.LinkedList; import java.util.List; import java.util.NoSuchElementException; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemAccessControlBinding; import it.niedermann.nextcloud.deck.databinding.ItemAccessControlOwnerBinding; @@ -82,7 +81,7 @@ public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.View final var ownerHolder = (OwnerViewHolder) holder; ownerHolder.binding.owner.setText(ac.getUser().getDisplayname()); Glide.with(ownerHolder.binding.avatar.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(ownerHolder.binding.avatar.getContext(), R.dimen.avatar_size), ac.getUser().getUid())) + .load(account.getAvatarUrl(ownerHolder.binding.avatar.getResources().getDimensionPixelSize(R.dimen.avatar_size), ac.getUser().getUid())) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) @@ -93,7 +92,7 @@ public class AccessControlAdapter extends RecyclerView.Adapter<RecyclerView.View default: { final var acHolder = (AccessControlViewHolder) holder; Glide.with(acHolder.binding.avatar.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(acHolder.binding.avatar.getContext(), R.dimen.avatar_size), ac.getUser().getUid())) + .load(account.getAvatarUrl(acHolder.binding.avatar.getResources().getDimensionPixelSize(R.dimen.avatar_size), ac.getUser().getUid())) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) 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 697b21e1e..193fc7a2c 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 @@ -21,7 +21,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; import org.jetbrains.annotations.Contract; -import java.time.ZoneId; +import java.time.Instant; import java.util.List; import java.util.stream.Collectors; @@ -31,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; @@ -59,13 +58,15 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { if (utils != null) { utils.platform.colorImageView(getNotSyncedYet(), ColorRole.PRIMARY); + utils.platform.colorImageView(getCardMenu(), ColorRole.ON_SURFACE); + utils.platform.colorTextView(getCardTitle(), ColorRole.ON_SURFACE); } // TODO should be discussed with UX // utils.material.themeCardView(getCard()); getNotSyncedYet().setVisibility(DBStatus.LOCAL_EDITED.equals(fullCard.getStatusEnum()) ? View.VISIBLE : View.GONE); - if (fullCard.getCard().getDueDate() != null) { + if (fullCard.getCard().getDueDate() != null || fullCard.getCard().getDone() != null) { setupDueDate(getCardDueDate(), fullCard.getCard()); getCardDueDate().setVisibility(View.VISIBLE); } else { @@ -90,13 +91,13 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { }); } - protected abstract TextView getCardDueDate(); + protected abstract DueDateChip getCardDueDate(); protected abstract ImageView getNotSyncedYet(); protected abstract TextView getCardTitle(); - protected abstract View getCardMenu(); + protected abstract ImageView getCardMenu(); protected abstract MaterialCardView getCard(); @@ -112,9 +113,14 @@ public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder { return getCard(); } - private static void setupDueDate(@NonNull TextView cardDueDate, @NonNull Card card) { - cardDueDate.setText(DateUtil.getRelativeDateTimeString(cardDueDate.getContext(), card.getDueDate().toEpochMilli())); - DeckViewThemeUtils.themeDueDate(cardDueDate, card.getDueDate().atZone(ZoneId.systemDefault()).toLocalDate()); + private static void setupDueDate(@NonNull DueDateChip cardDueDate, @NonNull Card card) { + final boolean isDone = card.getDone() != null; + final Instant date = isDone ? card.getDone() : card.getDueDate(); + + 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..8dc4a3e48 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; } @@ -78,7 +79,7 @@ public class CompactCardViewHolder extends AbstractCardViewHolder { } @Override - protected View getCardMenu() { + protected ImageView getCardMenu() { return binding.cardMenu; } 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..4e16f8127 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 @@ -1,6 +1,5 @@ package it.niedermann.nextcloud.deck.ui.card; -import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.widget.ImageView; @@ -12,6 +11,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 +35,7 @@ public class DefaultCardOnlyTitleViewHolder extends AbstractCardViewHolder { } @Override - protected TextView getCardDueDate() { + protected DueDateChip getCardDueDate() { return binding.cardDueDate; } @@ -50,7 +50,7 @@ public class DefaultCardOnlyTitleViewHolder extends AbstractCardViewHolder { } @Override - protected View getCardMenu() { + protected ImageView getCardMenu() { return binding.cardMenu; } 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..20ce6dd1d 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 @@ -13,12 +13,16 @@ import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import com.google.android.material.card.MaterialCardView; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; + +import java.util.stream.Stream; import it.niedermann.nextcloud.deck.R; 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; @@ -78,7 +82,7 @@ public class DefaultCardViewHolder extends AbstractCardViewHolder { final var taskStatus = fullCard.getCard().getTaskStatus(); if (taskStatus.taskCount > 0) { binding.cardCountTasks.setText(context.getResources().getString(R.string.task_count, String.valueOf(taskStatus.doneCount), String.valueOf(taskStatus.taskCount))); - binding.cardCountTasks.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(context, R.drawable.ic_check_grey600_24dp), null, null, null); + binding.cardCountTasks.setCompoundDrawablesWithIntrinsicBounds(ContextCompat.getDrawable(context, R.drawable.ic_check_box_24), null, null, null); binding.cardCountTasks.setVisibility(View.VISIBLE); } else { final String description = fullCard.getCard().getDescription(); @@ -90,10 +94,20 @@ public class DefaultCardViewHolder extends AbstractCardViewHolder { binding.cardCountTasks.setVisibility(View.GONE); } } + + if (utils != null) { + Stream.of( + binding.cardCountAttachments, + binding.cardCountTasks, + binding.cardCountComments + ).forEach(v -> { + utils.platform.colorTextView(v, ColorRole.ON_SURFACE_VARIANT ); + }); + } } @Override - protected TextView getCardDueDate() { + protected DueDateChip getCardDueDate() { return binding.cardDueDate; } @@ -108,7 +122,7 @@ public class DefaultCardViewHolder extends AbstractCardViewHolder { } @Override - protected View getCardMenu() { + protected ImageView getCardMenu() { return binding.cardMenu; } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java index 286c95018..9140e305e 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/UserAutoCompleteAdapter.java @@ -15,7 +15,6 @@ import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundExce import java.util.List; import it.niedermann.android.reactivelivedata.ReactiveLiveData; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemAutocompleteUserBinding; @@ -82,7 +81,7 @@ public class UserAutoCompleteAdapter extends AutoCompleteAdapter<User> { } Glide.with(binding.icon.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.icon.getContext(), R.dimen.avatar_size), getItem(position).getUid())) + .load(account.getAvatarUrl(binding.icon.getResources().getDimensionPixelSize(R.dimen.avatar_size), getItem(position).getUid())) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsBottomsheetBehaviorCallback.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsBottomsheetBehaviorCallback.java index 6b60bbffd..473937753 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsBottomsheetBehaviorCallback.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsBottomsheetBehaviorCallback.java @@ -1,5 +1,9 @@ package it.niedermann.nextcloud.deck.ui.card.attachments; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN; + import android.content.Context; import android.view.View; @@ -16,12 +20,6 @@ import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.floatingactionbutton.FloatingActionButton; -import it.niedermann.android.util.DimensionUtil; - -import static android.view.View.GONE; -import static android.view.View.VISIBLE; -import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN; - public class CardAttachmentsBottomsheetBehaviorCallback extends BottomSheetBehavior.BottomSheetCallback { @NonNull private final OnBackPressedCallback backPressedCallback; @@ -55,7 +53,7 @@ public class CardAttachmentsBottomsheetBehaviorCallback extends BottomSheetBehav this.bottomNavigation = bottomNavigation; this.backdropColorExpanded = ContextCompat.getColor(context, backdropColorExpanded); this.backdropColorCollapsed = ContextCompat.getColor(context, backdropColorCollapsed); - this.bottomNavigationHeight = DimensionUtil.INSTANCE.dpToPx(context, bottomNavigationHeight); + this.bottomNavigationHeight = context.getResources().getDimensionPixelSize(bottomNavigationHeight); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java index ea4e3c9a4..458f048be 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java @@ -61,7 +61,6 @@ import id.zelory.compressor.constraint.QualityConstraint; import id.zelory.compressor.constraint.ResolutionConstraint; import id.zelory.compressor.constraint.SizeConstraint; import it.niedermann.android.reactivelivedata.ReactiveLiveData; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabAttachmentsBinding; @@ -155,7 +154,7 @@ public class CardAttachmentsFragment extends Fragment implements AttachmentDelet this.binding.attachmentsList.setVisibility(VISIBLE); } }); - galleryItemDecoration = new GalleryItemDecoration(DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.spacer_1qx)); + galleryItemDecoration = new GalleryItemDecoration(getResources().getDimensionPixelSize(R.dimen.spacer_1qx)); mBottomSheetBehaviour = BottomSheetBehavior.from(binding.bottomSheetParent); mBottomSheetBehaviour.setDraggable(true); mBottomSheetBehaviour.setHideable(true); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java index 4704ea45a..495aabd50 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsFragment.java @@ -24,7 +24,6 @@ import com.bumptech.glide.request.RequestOptions; import java.time.Instant; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabCommentsBinding; @@ -94,7 +93,7 @@ public class CardCommentsFragment extends Fragment implements Themed, CommentEdi binding.comments.setAdapter(adapter); binding.replyCommentCancelButton.setOnClickListener((v) -> commentsViewModel.setReplyToComment(null)); Glide.with(binding.avatar.getContext()) - .load(editCardViewModel.getAccount().getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.avatar.getContext(), R.dimen.icon_size_details))) + .load(editCardViewModel.getAccount().getAvatarUrl(binding.avatar.getResources().getDimensionPixelSize(R.dimen.icon_size_details))) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsMentionProposer.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsMentionProposer.java index 403e26886..fe10f6cfa 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsMentionProposer.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/CardCommentsMentionProposer.java @@ -19,7 +19,6 @@ import java.util.ArrayList; import java.util.List; import it.niedermann.android.reactivelivedata.ReactiveLiveData; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.User; @@ -56,9 +55,9 @@ public class CardCommentsMentionProposer implements TextWatcher { this.mentionProposerWrapper = mentionProposerWrapper; this.mentionProposer = avatarProposer; baseRepository = new BaseRepository(editText.getContext()); - avatarSize = DimensionUtil.INSTANCE.dpToPx(mentionProposer.getContext(), R.dimen.avatar_size_small); + avatarSize = mentionProposer.getResources().getDimensionPixelSize(R.dimen.avatar_size_small); layoutParams = new LinearLayout.LayoutParams(avatarSize, avatarSize); - layoutParams.setMarginEnd(DimensionUtil.INSTANCE.dpToPx(mentionProposer.getContext(), R.dimen.spacer_1x)); + layoutParams.setMarginEnd(mentionProposer.getResources().getDimensionPixelSize(R.dimen.spacer_1x)); } @Override diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java index 221beea76..7a4df435f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/comments/ItemCommentViewHolder.java @@ -21,7 +21,6 @@ import java.util.HashMap; import java.util.function.Consumer; import it.niedermann.android.util.ClipboardUtil; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemCommentBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -43,7 +42,7 @@ public class ItemCommentViewHolder extends RecyclerView.ViewHolder { public void bind(@NonNull FullDeckComment comment, @NonNull Account account, @Nullable ThemeUtils utils, @NonNull MenuInflater inflater, @NonNull CommentDeletedListener deletedListener, @NonNull CommentSelectAsReplyListener selectAsReplyListener, @NonNull FragmentManager fragmentManager, @NonNull Consumer<CharSequence> editListener) { Glide.with(binding.avatar.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.avatar.getContext(), R.dimen.avatar_size), comment.getComment().getActorId())) + .load(account.getAvatarUrl(binding.avatar.getResources().getDimensionPixelSize(R.dimen.avatar_size), comment.getComment().getActorId())) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) @@ -64,7 +63,7 @@ public class ItemCommentViewHolder extends RecyclerView.ViewHolder { inflater.inflate(R.menu.comment_menu, menu); menu.findItem(android.R.id.copy).setOnMenuItemClickListener(item -> ClipboardUtil.INSTANCE.copyToClipboard(itemView.getContext(), comment.getComment().getMessage())); final var replyMenuItem = menu.findItem(R.id.reply); - if (comment.getStatusEnum() != DBStatus.LOCAL_EDITED && account.getServerDeckVersionAsObject().supportsCommentsReplys()) { + if (comment.getStatusEnum() != DBStatus.LOCAL_EDITED && account.getServerDeckVersionAsObject().supportsCommentsReplies()) { replyMenuItem.setOnMenuItemClickListener(item -> { selectAsReplyListener.onSelectAsReply(comment); return true; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/AssigneeViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/AssigneeViewHolder.java index 989b17ef7..8148ff783 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/AssigneeViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/AssigneeViewHolder.java @@ -8,7 +8,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemAssigneeBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -25,7 +24,7 @@ public class AssigneeViewHolder extends RecyclerView.ViewHolder { public void bind(@NonNull Account account, @NonNull User user, @Nullable Consumer<User> onClickListener) { Glide.with(binding.avatar.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.avatar.getContext(), R.dimen.avatar_size), user.getUid())) + .load(account.getAvatarUrl(binding.avatar.getResources().getDimensionPixelSize(R.dimen.avatar_size), user.getUid())) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java index 76c99abe9..d86f9361a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDetailsFragment.java @@ -27,23 +27,14 @@ import androidx.recyclerview.widget.GridLayoutManager; import com.google.android.material.chip.Chip; import com.google.android.material.snackbar.Snackbar; +import com.nextcloud.android.common.ui.theme.utils.ColorRole; import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException; -import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; -import com.wdullaer.materialdatetimepicker.date.DatePickerDialog.OnDateSetListener; -import com.wdullaer.materialdatetimepicker.time.TimePickerDialog; -import com.wdullaer.materialdatetimepicker.time.TimePickerDialog.OnTimeSetListener; - -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.time.Instant; import java.util.stream.Stream; import it.niedermann.android.markdown.MarkdownEditor; import it.niedermann.android.util.ColorUtil; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.FragmentCardEditTabDetailsBinding; @@ -59,17 +50,13 @@ import it.niedermann.nextcloud.deck.ui.card.assignee.CardAssigneeDialog; import it.niedermann.nextcloud.deck.ui.card.assignee.CardAssigneeListener; import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; -import it.niedermann.nextcloud.deck.ui.theme.ThemedDatePickerDialog; import it.niedermann.nextcloud.deck.ui.theme.ThemedSnackbar; -import it.niedermann.nextcloud.deck.ui.theme.ThemedTimePickerDialog; -public class CardDetailsFragment extends Fragment implements OnDateSetListener, OnTimeSetListener, CardAssigneeListener { +public class CardDetailsFragment extends Fragment implements CardDueDateView.DueDateChangedListener, CardAssigneeListener { private FragmentCardEditTabDetailsBinding binding; private EditCardViewModel viewModel; private AssigneeAdapter adapter; - private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); - private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); private static final String KEY_ACCOUNT = "account"; public static Fragment newInstance(@NonNull Account account) { @@ -102,9 +89,9 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, return binding.getRoot(); } - @Px final int avatarSize = DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.avatar_size); + @Px final int avatarSize = getResources().getDimensionPixelSize(R.dimen.avatar_size); final var avatarLayoutParams = new LinearLayout.LayoutParams(avatarSize, avatarSize); - avatarLayoutParams.setMargins(0, 0, DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.spacer_1x), 0); + avatarLayoutParams.setMargins(0, 0, getResources().getDimensionPixelSize(R.dimen.spacer_1x), 0); setupAssignees(); setupLabels((Account) args.getSerializable(KEY_ACCOUNT)); @@ -121,16 +108,16 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, viewModel.getBoardColor().observe(getViewLifecycleOwner(), this::applyTheme); } - @Override - public void onResume() { - super.onResume(); - - // https://github.com/wdullaer/MaterialDateTimePicker#why-are-my-callbacks-lost-when-the-device-changes-orientation - final var dpd = (DatePickerDialog) getChildFragmentManager().findFragmentByTag(ThemedDatePickerDialog.class.getCanonicalName()); - final var tpd = (TimePickerDialog) getChildFragmentManager().findFragmentByTag(ThemedTimePickerDialog.class.getCanonicalName()); - if (tpd != null) tpd.setOnTimeSetListener(this); - if (dpd != null) dpd.setOnDateSetListener(this); - } +// @Override +// public void onResume() { +// super.onResume(); +// +// // https://github.com/wdullaer/MaterialDateTimePicker#why-are-my-callbacks-lost-when-the-device-changes-orientation +// final var dpd = (DatePickerDialog) getChildFragmentManager().findFragmentByTag(ThemedDatePickerDialog.class.getCanonicalName()); +// final var tpd = (TimePickerDialog) getChildFragmentManager().findFragmentByTag(ThemedTimePickerDialog.class.getCanonicalName()); +// if (tpd != null) tpd.setOnTimeSetListener(this); +// if (dpd != null) dpd.setOnDateSetListener(this); +// } @Override public void onDestroy() { @@ -143,12 +130,13 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, Stream.of( binding.labelsWrapper, - binding.dueDateDateWrapper, - binding.dueDateTimeWrapper, binding.peopleWrapper, binding.descriptionEditorWrapper ).forEach(utils.material::colorTextInputLayout); + utils.platform.colorImageView(binding.descriptionToggle, ColorRole.SECONDARY); + + binding.cardDueDateView.applyTheme(color); binding.descriptionEditor.setSearchColor(color); binding.descriptionViewer.setSearchColor(color); @@ -194,53 +182,27 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, } private void setupDueDate() { - if (this.viewModel.getFullCard().getCard().getDueDate() != null) { - final var dueDate = this.viewModel.getFullCard().getCard().getDueDate().atZone(ZoneId.systemDefault()); - binding.dueDateDate.setText(dueDate == null ? null : dueDate.format(dateFormatter)); - binding.dueDateTime.setText(dueDate == null ? null : dueDate.format(timeFormatter)); - binding.clearDueDate.setVisibility(VISIBLE); - } else { - binding.clearDueDate.setVisibility(GONE); - binding.dueDateDate.setText(null); - binding.dueDateTime.setText(null); - } - - if (viewModel.canEdit()) { - binding.dueDateDate.setOnClickListener(v -> { - final LocalDate date; - if (viewModel.getFullCard() != null && viewModel.getFullCard().getCard() != null && viewModel.getFullCard().getCard().getDueDate() != null) { - date = viewModel.getFullCard().getCard().getDueDate().atZone(ZoneId.systemDefault()).toLocalDate(); - } else { - date = LocalDate.now(); - } - viewModel.getCurrentBoardColor(viewModel.getAccount().getId(), viewModel.getBoardId()) - .thenAcceptAsync(color -> ThemedDatePickerDialog.newInstance(this, date.getYear(), date.getMonthValue(), date.getDayOfMonth(), color) - .show(getChildFragmentManager(), ThemedDatePickerDialog.class.getCanonicalName()), ContextCompat.getMainExecutor(requireContext())); - }); + final var version = this.viewModel.getAccount().getServerDeckVersionAsObject(); + final var card = this.viewModel.getFullCard().getCard(); + binding.cardDueDateView.setDueDateListener(this); + binding.cardDueDateView.setEnabled(this.viewModel.canEdit()); + binding.cardDueDateView.setDueDate(getChildFragmentManager(), version, card.getDueDate(), card.getDone()); + } - binding.dueDateTime.setOnClickListener(v -> { - final LocalTime time; - if (viewModel.getFullCard() != null && viewModel.getFullCard().getCard() != null && viewModel.getFullCard().getCard().getDueDate() != null) { - time = viewModel.getFullCard().getCard().getDueDate().atZone(ZoneId.systemDefault()).toLocalTime(); - } else { - time = LocalTime.now(); - } - viewModel.getCurrentBoardColor(viewModel.getAccount().getId(), viewModel.getBoardId()) - .thenAcceptAsync(color -> ThemedTimePickerDialog.newInstance(this, time.getHour(), time.getMinute(), true, color) - .show(getChildFragmentManager(), ThemedTimePickerDialog.class.getCanonicalName()), ContextCompat.getMainExecutor(requireContext())); - }); + @Override + public void onDueDateChanged(@Nullable Instant dueDate) { + final var version = this.viewModel.getAccount().getServerDeckVersionAsObject(); + final var card = this.viewModel.getFullCard().getCard(); + card.setDueDate(dueDate); + binding.cardDueDateView.setDueDate(getChildFragmentManager(), version, card.getDueDate(), card.getDone()); + } - binding.clearDueDate.setOnClickListener(v -> { - binding.dueDateDate.setText(null); - binding.dueDateTime.setText(null); - viewModel.getFullCard().getCard().setDueDate(null); - binding.clearDueDate.setVisibility(GONE); - }); - } else { - binding.dueDateDate.setEnabled(false); - binding.dueDateTime.setEnabled(false); - binding.clearDueDate.setVisibility(GONE); - } + @Override + public void onDoneChanged(@Nullable Instant done) { + final var version = this.viewModel.getAccount().getServerDeckVersionAsObject(); + final var card = this.viewModel.getFullCard().getCard(); + card.setDone(done); + binding.cardDueDateView.setDueDate(getChildFragmentManager(), version, card.getDueDate(), card.getDone()); } private void setupLabels(@NonNull Account account) { @@ -333,8 +295,8 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, adapter = new AssigneeAdapter((user) -> CardAssigneeDialog.newInstance(user).show(getChildFragmentManager(), CardAssigneeDialog.class.getSimpleName()), viewModel.getAccount()); binding.assignees.setAdapter(adapter); binding.assignees.post(() -> { - @Px final int gutter = DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.spacer_1x); - final int spanCount = (int) (float) binding.labelsWrapper.getWidth() / (DimensionUtil.INSTANCE.dpToPx(requireContext(), R.dimen.avatar_size) + gutter); + @Px final int gutter = getResources().getDimensionPixelSize(R.dimen.spacer_1x); + final int spanCount = (int) (float) binding.labelsWrapper.getWidth() / (getResources().getDimensionPixelSize(R.dimen.avatar_size) + gutter); binding.assignees.setLayoutManager(new GridLayoutManager(getContext(), spanCount)); binding.assignees.addItemDecoration(new AssigneeDecoration(spanCount, gutter)); }); @@ -364,53 +326,6 @@ public class CardDetailsFragment extends Fragment implements OnDateSetListener, } } - @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 LocalTime oldTime = LocalTime.from(this.viewModel.getFullCard().getCard().getDueDate().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.viewModel.getFullCard().getCard().setDueDate(newDateTime.toInstant()); - binding.dueDateDate.setText(newDateTime.format(dateFormatter)); - - if (this.viewModel.getFullCard().getCard().getDueDate() == null || this.viewModel.getFullCard().getCard().getDueDate().toEpochMilli() == 0) { - binding.clearDueDate.setVisibility(GONE); - } else { - binding.clearDueDate.setVisibility(VISIBLE); - } - } - - @Override - public void onTimeSet(TimePickerDialog view, int hourOfDay, int minute, int second) { - final var oldInstant = this.viewModel.getFullCard().getCard().getDueDate(); - final var oldDateTime = oldInstant == null ? ZonedDateTime.now() : oldInstant.atZone(ZoneId.systemDefault()); - final var newDateTime = oldDateTime.with( - LocalTime.of(hourOfDay, minute) - ); - - this.viewModel.getFullCard().getCard().setDueDate(newDateTime.toInstant()); - binding.dueDateTime.setText(newDateTime.format(timeFormatter)); - if (this.viewModel.getFullCard().getCard().getDueDate() == null || this.viewModel.getFullCard().getCard().getDueDate().toEpochMilli() == 0) { - binding.clearDueDate.setVisibility(GONE); - } else { - binding.clearDueDate.setVisibility(VISIBLE); - } - } - private void setupProjects() { if (viewModel.getFullCard().getProjects().size() > 0) { binding.projectsTitle.setVisibility(VISIBLE); 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..2266e6c7d --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/details/CardDueDateView.java @@ -0,0 +1,302 @@ +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.R; +import it.niedermann.nextcloud.deck.databinding.ViewCardDueDateBinding; +import it.niedermann.nextcloud.deck.model.ocs.Version; +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 dateTimeFormatter = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm"); + private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM); + private final DateTimeFormatter timeFormatter = DateTimeFormatter.ofLocalizedTime(FormatStyle.SHORT); + boolean supportsDone = false; + @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); + } + + /** + * @noinspection unused + */ + 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, @NonNull Version version, @Nullable Instant dueDate, @Nullable Instant done) { + this.fragmentManager = fragmentManager; + this.supportsDone = version.supportsDone(); + this.dueDate = dueDate; + this.done = done; + render(); + } + + private void render() { + setVisibilityState(); + setTextState(); + setInteraction(); + } + + private void setVisibilityState() { + if (done == null) { + Stream.of( + 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.markAsDone.setVisibility(supportsDone ? View.VISIBLE : View.GONE); + binding.clearDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); + } else { + + Stream.of( + binding.doneCheck, + binding.doneDate + ).forEach(v -> v.setVisibility(View.VISIBLE)); + + Stream.of( + binding.markAsDone, + binding.dueDateDateWrapper, + binding.dueDateTimeWrapper, + binding.clearDueDate + ).forEach(v -> v.setVisibility(View.GONE)); + + binding.clearDone.setVisibility(supportsDone ? View.VISIBLE : View.GONE); + binding.doneDueDate.setVisibility(dueDate == null || !isEnabled() ? View.GONE : View.VISIBLE); + } + } + + 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(dateTimeFormatter)); + + if (this.dueDate == null) { + binding.doneDueDate.setText(null); + + } else { + final var dueDate = this.dueDate.atZone(ZoneId.systemDefault()); + binding.doneDueDate.setText(getContext().getString(R.string.label_due_at, dueDate.format(dateTimeFormatter))); + } + } + } + + 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); + } + }); + + if (supportsDone) { + binding.markAsDone.setOnClickListener(v -> { + if (this.dueDateChangedListener != null) { + this.dueDateChangedListener.onDoneChanged(Instant.now()); + } + }); + } else { + binding.markAsDone.setOnClickListener(null); + } + + 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)); + } + } + + @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 { + assert this.dueDate != null; // Since selectedTime is not empty and is derived from dueDate, dueDate itself shouldn't be null here. + 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.clearDone, + binding.clearDueDate + ).forEach(v -> utils.platform.colorImageView(v, ColorRole.SECONDARY)); + + utils.platform.colorTextView(binding.doneDate, ColorRole.ON_SURFACE); + utils.platform.colorTextView(binding.doneDueDate, ColorRole.ON_SURFACE_VARIANT); + 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); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java index 8eac0dbb4..fc722226a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDialogFragment.java @@ -19,6 +19,7 @@ import com.nextcloud.android.common.ui.theme.utils.ColorRole; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.DialogFilterBinding; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; import it.niedermann.nextcloud.deck.ui.theme.ThemedDialogFragment; @@ -32,6 +33,7 @@ public class FilterDialogFragment extends ThemedDialogFragment { private final static int[] tabTitles = new int[]{ R.string.filter_tags_title, R.string.filter_user_title, + R.string.filter_done_title, R.string.filter_duedate_title }; @@ -63,6 +65,9 @@ public class FilterDialogFragment extends ThemedDialogFragment { tab.setIcon(draft.getUsers().size() > 0 || draft.isNoAssignedUser() ? indicator : null); break; case 2: + tab.setIcon(draft.getDoneType() != EDoneType.NO_FILTER ? indicator : null); + break; + case 3: tab.setIcon(draft.getDueType() != EDueType.NO_FILTER ? indicator : null); break; default: @@ -86,7 +91,7 @@ public class FilterDialogFragment extends ThemedDialogFragment { .setTitle(R.string.simple_filter) .setView(binding.getRoot()) .setNeutralButton(android.R.string.cancel, null) - .setNegativeButton(R.string.simple_clear, (a, b) -> filterViewModel.clearFilterInformation(false)) + .setNegativeButton(R.string.simple_reset, (a, b) -> filterViewModel.clearFilterInformation(false)) .setPositiveButton(R.string.simple_filter, (a, b) -> filterViewModel.publishFilterInformationDraft()) .create(); } @@ -124,6 +129,8 @@ public class FilterDialogFragment extends ThemedDialogFragment { case 1: return new FilterUserFragment(); case 2: + return new FilterDoneTypeFragment(); + case 3: return new FilterDueTypeFragment(); default: throw new IllegalArgumentException("position must be between 0 and 2 but was " + position); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeAdapter.java new file mode 100644 index 000000000..d194f78ab --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeAdapter.java @@ -0,0 +1,96 @@ +package it.niedermann.nextcloud.deck.ui.filter; + +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; + +import java.util.Arrays; + +import it.niedermann.nextcloud.deck.databinding.ItemFilterDonetypeBinding; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; +import it.niedermann.nextcloud.deck.ui.theme.ThemeUtils; +import it.niedermann.nextcloud.deck.ui.theme.Themed; + +public class FilterDoneTypeAdapter extends RecyclerView.Adapter<FilterDoneTypeAdapter.DoneTypeViewHolder> { + @NonNull + private final EDoneType[] doneTypes = EDoneType.values(); + private int selectedDoneTypePosition; + @Nullable + private final SelectionListener<EDoneType> selectionListener; + @ColorInt + private final int color; + + @SuppressWarnings("WeakerAccess") + public FilterDoneTypeAdapter(@NonNull EDoneType selectedDoneType, @Nullable SelectionListener<EDoneType> selectionListener, @ColorInt int color) { + super(); + this.selectedDoneTypePosition = Arrays.binarySearch(doneTypes, selectedDoneType); + this.selectionListener = selectionListener; + this.color = color; + setHasStableIds(true); + } + + @NonNull + @Override + public DoneTypeViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new DoneTypeViewHolder(ItemFilterDonetypeBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull DoneTypeViewHolder viewHolder, int position) { + viewHolder.bind(doneTypes[position]); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getItemCount() { + return doneTypes.length; + } + + class DoneTypeViewHolder extends RecyclerView.ViewHolder implements Themed { + private final ItemFilterDonetypeBinding binding; + + DoneTypeViewHolder(@NonNull ItemFilterDonetypeBinding binding) { + super(binding.getRoot()); + this.binding = binding; + } + + void bind(final EDoneType doneType) { + binding.doneType.setText(doneType.toString(binding.doneType.getContext())); + itemView.setSelected(doneTypes[selectedDoneTypePosition].equals(doneType)); + applyTheme(color); + + itemView.setOnClickListener(view -> { + final int oldSelection = selectedDoneTypePosition; + if (doneTypes[selectedDoneTypePosition].equals(doneType)) { + selectedDoneTypePosition = Arrays.binarySearch(doneTypes, EDoneType.NO_FILTER); + itemView.setSelected(false); + if (selectionListener != null) { + selectionListener.onItemSelected(EDoneType.NO_FILTER); + } + notifyItemChanged(selectedDoneTypePosition); + } else { + selectedDoneTypePosition = Arrays.binarySearch(doneTypes, doneType); + itemView.setSelected(true); + if (selectionListener != null) { + selectionListener.onItemSelected(doneType); + } + } + notifyItemChanged(oldSelection); + }); + } + + @Override + public void applyTheme(int color) { + final var utils = ThemeUtils.of(color, itemView.getContext()); + utils.deck.themeSelectedCheck(binding.selectedCheck.getContext(), binding.selectedCheck.getDrawable()); + } + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeFragment.java new file mode 100644 index 000000000..8cee39d1e --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterDoneTypeFragment.java @@ -0,0 +1,47 @@ +package it.niedermann.nextcloud.deck.ui.filter; + +import static java.util.Objects.requireNonNull; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import it.niedermann.nextcloud.deck.databinding.DialogFilterDoneBinding; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; + +public class FilterDoneTypeFragment extends Fragment implements SelectionListener<EDoneType> { + + private FilterViewModel filterViewModel; + private DialogFilterDoneBinding binding; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + binding = DialogFilterDoneBinding.inflate(requireActivity().getLayoutInflater()); + + filterViewModel = new ViewModelProvider(requireActivity()).get(FilterViewModel.class); + + binding.doneType.setItemAnimator(null); + filterViewModel.getCurrentBoardColor$().observe(getViewLifecycleOwner(), + color -> binding.doneType.setAdapter(new FilterDoneTypeAdapter(requireNonNull(filterViewModel.getFilterInformationDraft().getValue()).getDoneType(), this, color))); + + return binding.getRoot(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + this.binding = null; + } + + @Override + public void onItemSelected(EDoneType item) { + filterViewModel.setFilterInformationDraftDoneType(item); + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java index fec2186a1..1657efdff 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterUserAdapter.java @@ -15,7 +15,6 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemFilterUserBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -89,7 +88,7 @@ public class FilterUserAdapter extends RecyclerView.Adapter<FilterUserAdapter.Us void bind(@NonNull final User user) { binding.title.setText(user.getDisplayname()); Glide.with(binding.avatar.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.avatar.getContext(), R.dimen.avatar_size), user.getUid())) + .load(account.getAvatarUrl(binding.avatar.getResources().getDimensionPixelSize(R.dimen.avatar_size), user.getUid())) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_person_grey600_24dp) .error(R.drawable.ic_person_grey600_24dp) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java index 09f2f4fa1..5bd226e9f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/filter/FilterViewModel.java @@ -15,6 +15,7 @@ import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Label; import it.niedermann.nextcloud.deck.model.User; +import it.niedermann.nextcloud.deck.model.enums.EDoneType; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.internal.FilterInformation; import it.niedermann.nextcloud.deck.ui.viewmodel.BaseViewModel; @@ -80,6 +81,12 @@ public class FilterViewModel extends BaseViewModel { this.filterInformationDraft.postValue(newDraft); } + public void setFilterInformationDraftDoneType(@NonNull EDoneType doneType) { + final var newDraft = new FilterInformation(filterInformationDraft.getValue()); + newDraft.setDoneType(doneType); + this.filterInformationDraft.postValue(newDraft); + } + public void addFilterInformationDraftLabel(@NonNull Label label) { final var newDraft = new FilterInformation(filterInformationDraft.getValue()); newDraft.addLabel(label); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/search/SearchCardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/search/SearchCardViewHolder.java index 868d9c877..29a7373d1 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/search/SearchCardViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/main/search/SearchCardViewHolder.java @@ -18,7 +18,6 @@ import org.jetbrains.annotations.Contract; import java.util.List; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemSearchCardBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -64,7 +63,7 @@ public class SearchCardViewHolder extends SearchViewHolder { .load(new SingleSignOnUrl(account.getName(), AttachmentUtil.getThumbnailUrl(account, fullCard.getId(), coverImages.get(), binding.coverImages.getWidth()))) .apply(new RequestOptions().transform( new CenterCrop(), - new RoundedCorners(DimensionUtil.INSTANCE.dpToPx(context, R.dimen.spacer_1x)) + new RoundedCorners(context.getResources().getDimensionPixelSize(R.dimen.spacer_1x)) )) .placeholder(R.drawable.ic_image_grey600_24dp) .error(R.drawable.ic_image_grey600_24dp) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java index c4684fe55..10249fc5b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/manageaccounts/ManageAccountViewHolder.java @@ -15,7 +15,6 @@ import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemAccountChooseBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -38,7 +37,7 @@ public class ManageAccountViewHolder extends RecyclerView.ViewHolder { ); binding.accountHost.setText(Uri.parse(account.getUrl()).getHost()); Glide.with(itemView.getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.accountItemAvatar.getContext(), R.dimen.avatar_size))) + .load(account.getAvatarUrl(binding.accountItemAvatar.getResources().getDimensionPixelSize(R.dimen.avatar_size))) .apply(RequestOptions.circleCropTransform()) .placeholder(R.drawable.ic_baseline_account_circle_24) .error(R.drawable.ic_baseline_account_circle_24) diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/AccountAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/AccountAdapter.java index bf919cd66..279540b72 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/AccountAdapter.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/AccountAdapter.java @@ -11,7 +11,6 @@ import com.bumptech.glide.request.RequestOptions; import java.net.URL; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ItemPrepareCreateAccountBinding; @@ -49,7 +48,7 @@ public class AccountAdapter extends AbstractAdapter<Account> { } Glide.with(getContext()) - .load(account.getAvatarUrl(DimensionUtil.INSTANCE.dpToPx(binding.avatar.getContext(), R.dimen.avatar_size))) + .load(account.getAvatarUrl(binding.avatar.getResources().getDimensionPixelSize(R.dimen.avatar_size))) .placeholder(R.drawable.ic_baseline_account_circle_24) .error(R.drawable.ic_baseline_account_circle_24) .apply(RequestOptions.circleCropTransform()) 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 7b8c81ab4..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,36 +191,6 @@ public class DeckViewThemeUtils extends ViewThemeUtilsBase { return Optional.empty(); } - - @Deprecated(forRemoval = true) - public static void themeDueDate(@NonNull TextView cardDueDate, @NonNull LocalDate dueDate) { - final var context = cardDueDate.getContext(); - final long diff = DAYS.between(LocalDate.now(), dueDate); - - @ColorInt @Nullable Integer textColor = null; - @DrawableRes int backgroundDrawable = 0; - - if (diff == 1) { - // due date: tomorrow - backgroundDrawable = R.drawable.due_tomorrow_background; - textColor = ContextCompat.getColor(context, R.color.due_text_tomorrow); - } else 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); - } - - 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/theme/ThemedDatePickerDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDatePickerDialog.java index 926a394e4..a78bc972f 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDatePickerDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedDatePickerDialog.java @@ -36,11 +36,11 @@ public class ThemedDatePickerDialog extends DatePickerDialog implements Themed { public void applyTheme(int color) { final var scheme = ThemeUtils.createScheme(color, requireContext()); - @ColorInt final int buttonTextColor = scheme.getOnPrimaryContainer(); + @ColorInt final int buttonTextColor = scheme.getOnSecondaryContainer(); setOkColor(buttonTextColor); setCancelColor(buttonTextColor); - setAccentColor(Scheme.dark(color).getPrimaryContainer()); + setAccentColor(Scheme.dark(color).getSecondaryContainer()); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedTimePickerDialog.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedTimePickerDialog.java index d835a5723..f9bc8bd13 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedTimePickerDialog.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/theme/ThemedTimePickerDialog.java @@ -36,11 +36,11 @@ public class ThemedTimePickerDialog extends TimePickerDialog implements Themed { public void applyTheme(int color) { final var scheme = ThemeUtils.createScheme(color, requireContext()); - @ColorInt final int buttonTextColor = scheme.getOnPrimaryContainer(); + @ColorInt final int buttonTextColor = scheme.getOnSecondaryContainer(); setOkColor(buttonTextColor); setCancelColor(buttonTextColor); - setAccentColor(Scheme.dark(color).getPrimaryContainer()); + setAccentColor(Scheme.dark(color).getSecondaryContainer()); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsUtil.java index 2341c6590..30ba2296c 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsUtil.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/upcomingcards/UpcomingCardsUtil.java @@ -1,5 +1,7 @@ package it.niedermann.nextcloud.deck.ui.upcomingcards; +import static java.time.temporal.ChronoUnit.DAYS; + import android.content.Context; import androidx.annotation.NonNull; @@ -17,8 +19,6 @@ import java.util.List; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.model.Card; -import static java.time.temporal.ChronoUnit.DAYS; - public class UpcomingCardsUtil { private UpcomingCardsUtil() { @@ -31,7 +31,7 @@ public class UpcomingCardsUtil { return EUpcomingDueType.NO_DUE; } - long diff = DAYS.between(LocalDate.now(), dueDate.atZone(ZoneId.systemDefault()).toLocalDate()); + final long diff = DAYS.between(LocalDate.now(), dueDate.atZone(ZoneId.systemDefault()).toLocalDate()); if (diff > 7) { return EUpcomingDueType.LATER; diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java index ae520cf91..a653feac8 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/ColorChooser.java @@ -18,7 +18,6 @@ import com.skydoves.colorpickerview.listeners.ColorEnvelopeListener; import java.util.Arrays; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.WidgetColorChooserBinding; import it.niedermann.nextcloud.deck.ui.theme.DeckViewThemeUtils; @@ -45,7 +44,7 @@ public class ColorChooser extends LinearLayout { ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT ); - params.setMargins(0, DimensionUtil.INSTANCE.dpToPx(context, R.dimen.spacer_1x), 0, 0); + params.setMargins(0, getResources().getDimensionPixelSize(R.dimen.spacer_1x), 0, 0); params.setFlexBasisPercent(.15f); final var styles = context.obtainStyledAttributes(attrs, R.styleable.ColorChooser, 0, 0); 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..095a46bd3 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/DueDateChip.java @@ -0,0 +1,111 @@ +package it.niedermann.nextcloud.deck.ui.view; + +import static java.time.temporal.ChronoUnit.HOURS; + +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.LocalDateTime; +import java.time.ZoneId; + +import it.niedermann.nextcloud.deck.R; +import it.niedermann.nextcloud.deck.util.DateUtil; + +public class DueDateChip extends Chip { + + @ColorInt + protected final int colorOnSurface; + protected final boolean compactMode; + + public DueDateChip(Context context) { + this(context, null); + } + + public DueDateChip(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.chipStyle); + } + + public DueDateChip(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + final var typedValue = new TypedValue(); + final var theme = getContext().getTheme(); + theme.resolveAttribute(R.attr.colorOnSurfaceVariant, typedValue, true); + this.colorOnSurface = typedValue.data; + + final var styles = context.obtainStyledAttributes(attrs, R.styleable.DueDateChip, defStyleAttr, 0); + this.compactMode = styles.getBoolean(R.styleable.DueDateChip_compactMode, false); + styles.recycle(); + + setEnsureMinTouchTargetSize(false); + setClickable(false); + + @Px final var padding = getResources().getDimensionPixelSize(R.dimen.spacer_1x); + setPadding(padding, padding, padding, padding); + setMinHeight(0); + setChipMinHeight(0); + + if (compactMode) { + setChipEndPadding(0); + setTextEndPadding(0); + } + } + + public void setDueDate(@NonNull Instant date, boolean isDone) { + if (compactMode) { + setText(null); + } else { + 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_circle_24; + backgroundColorRes = R.color.due_done; + textColorRes = R.color.due_text_done; + + } else if (date.isBefore(Instant.now())) { // Overdue + chipIconRes = R.drawable.ic_time_filled_24; + backgroundColorRes = R.color.due_overdue; + textColorRes = R.color.due_text_overdue; + + } else if (HOURS.between(LocalDateTime.now(), date.atZone(ZoneId.systemDefault())) < 24) { // Next 24 Hours + chipIconRes = R.drawable.ic_time_24; + backgroundColorRes = R.color.due_today; + textColorRes = R.color.due_text_today; + + } 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/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java index c1d5af3a2..4c52ffc1a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/OverlappingAvatars.java @@ -16,7 +16,6 @@ import com.bumptech.glide.request.RequestOptions; import java.util.List; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.User; @@ -41,10 +40,12 @@ public class OverlappingAvatars extends RelativeLayout { public OverlappingAvatars(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); - maxAvatarCount = context.getResources().getInteger(R.integer.max_avatar_count); - avatarBorderSize = DimensionUtil.INSTANCE.dpToPx(context, R.dimen.avatar_size_small_overlapping_border); - avatarSize = DimensionUtil.INSTANCE.dpToPx(context, R.dimen.avatar_size_small) + avatarBorderSize * 2; - overlapPx = DimensionUtil.INSTANCE.dpToPx(context, R.dimen.avatar_size_small_overlapping); + + final var resources = getResources(); + maxAvatarCount = resources.getInteger(R.integer.max_avatar_count); + avatarBorderSize = resources.getDimensionPixelSize(R.dimen.avatar_size_small_overlapping_border); + avatarSize = resources.getDimensionPixelSize(R.dimen.avatar_size_small) + avatarBorderSize * 2; + overlapPx = resources.getDimensionPixelSize(R.dimen.avatar_size_small_overlapping); borderDrawable = ContextCompat.getDrawable(context, R.drawable.avatar_border); assert borderDrawable != null; DrawableCompat.setTint(borderDrawable, ContextCompat.getColor(context, R.color.bg_card)); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/CompactLabelChip.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/CompactLabelChip.java index a2a50430c..3516b036b 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/CompactLabelChip.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/CompactLabelChip.java @@ -6,7 +6,6 @@ import android.content.Context; import androidx.annotation.NonNull; import androidx.annotation.Px; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Label; @@ -16,6 +15,6 @@ public class CompactLabelChip extends LabelChip { public CompactLabelChip(@NonNull Context context, @NonNull Label label, @Px int gutter) { super(context, label, gutter); params.setFlexBasisPercent(1 / 6.5f); - setHeight(DimensionUtil.INSTANCE.dpToPx(context, R.dimen.compact_label_height)); + setHeight(getResources().getDimensionPixelSize(R.dimen.compact_label_height)); } }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java index 526bdbaef..ce7e38262 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java @@ -11,7 +11,6 @@ import com.google.android.flexbox.FlexboxLayout; import java.util.LinkedList; import java.util.List; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.Label; @@ -26,7 +25,7 @@ public abstract class LabelLayout extends FlexboxLayout { public LabelLayout(Context context, AttributeSet attrs) { super(context, attrs); - this.gutter = DimensionUtil.INSTANCE.dpToPx(context, R.dimen.spacer_1hx); + this.gutter = context.getResources().getDimensionPixelSize(R.dimen.spacer_1hx); } /** diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java index 08e32bf25..f674047cc 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java @@ -13,9 +13,11 @@ import android.text.TextUtils; import android.view.View; import android.widget.RemoteViews; +import androidx.annotation.DrawableRes; import androidx.annotation.IdRes; import androidx.annotation.NonNull; +import java.time.Instant; import java.util.NoSuchElementException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -53,19 +55,31 @@ public class SingleCardWidget extends AppWidgetProvider { views.setViewVisibility(R.id.description_lv, View.VISIBLE); } + final var card = fullModel.getFullCard().getCard(); + views.setOnClickPendingIntent(R.id.widget_card, pendingIntent); views.setPendingIntentTemplate(R.id.description_lv, pendingIntent); - views.setTextViewText(R.id.title, fullModel.getFullCard().getCard().getTitle()); + views.setTextViewText(R.id.title, card.getTitle()); views.setRemoteAdapter(R.id.description_lv, serviceIntent); - if (fullModel.getFullCard().getCard().getDueDate() != null) { - views.setTextViewText(R.id.card_due_date, DateUtil.getRelativeDateTimeString(context, fullModel.getFullCard().getCard().getDueDate().toEpochMilli())); - // TODO Use multiple views for background colors and only set the necessary to View.VISIBLE - // https://stackoverflow.com/a/3376537 - // Because otherwise using Reflection is the only way + // TODO Use multiple views for background colors and only set the necessary to View.VISIBLE + // https://stackoverflow.com/a/3376537 + // Because otherwise using Reflection is the only way + if (card.getDone() != null) { + views.setTextViewText(R.id.card_due_date, DateUtil.getRelativeDateTimeString(context, card.getDone().toEpochMilli())); + views.setViewVisibility(R.id.card_due_date, View.VISIBLE); + views.setViewVisibility(R.id.card_due_date_image, View.VISIBLE); + views.setImageViewResource(R.id.card_due_date_image, R.drawable.ic_check_circle_24); + } else if (card.getDueDate() != null) { + views.setTextViewText(R.id.card_due_date, DateUtil.getRelativeDateTimeString(context, card.getDueDate().toEpochMilli())); views.setViewVisibility(R.id.card_due_date, View.VISIBLE); views.setViewVisibility(R.id.card_due_date_image, View.VISIBLE); - views.setImageViewResource(R.id.card_due_date_image, R.drawable.calendar_blank_grey600_24dp); + + @DrawableRes final var dueDateImage = card.getDueDate().isBefore(Instant.now()) + ? R.drawable.ic_time_filled_24 + : R.drawable.ic_time_24; + + views.setImageViewResource(R.id.card_due_date_image, dueDateImage); } else { views.setViewVisibility(R.id.card_due_date, View.GONE); views.setViewVisibility(R.id.card_due_date_image, View.GONE); diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/upcoming/UpcomingWidgetFactory.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/upcoming/UpcomingWidgetFactory.java index d7c320a95..902d82d88 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/upcoming/UpcomingWidgetFactory.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/upcoming/UpcomingWidgetFactory.java @@ -12,7 +12,6 @@ import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; -import it.niedermann.android.util.DimensionUtil; import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.model.full.FullCard; @@ -35,8 +34,10 @@ public class UpcomingWidgetFactory implements RemoteViewsService.RemoteViewsFact this.context = context; this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); this.baseRepository = new BaseRepository(context); - this.headerHorizontalPadding = DimensionUtil.INSTANCE.dpToPx(context, R.dimen.spacer_1hx); - this.headerVerticalPaddingNth = DimensionUtil.INSTANCE.dpToPx(context, R.dimen.spacer_2x); + + final var resources = context.getResources(); + this.headerHorizontalPadding = resources.getDimensionPixelSize(R.dimen.spacer_1hx); + this.headerVerticalPaddingNth = resources.getDimensionPixelSize(R.dimen.spacer_2x); } @Override diff --git a/app/src/main/res/animator/appbar_elevation_off.xml b/app/src/main/res/animator/appbar_elevation_off.xml deleted file mode 100644 index d24dcb340..000000000 --- a/app/src/main/res/animator/appbar_elevation_off.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item> - <objectAnimator - android:propertyName="elevation" - android:valueTo="0dp" - android:valueType="floatType" /> - </item> -</selector> diff --git a/app/src/main/res/animator/appbar_elevation_on.xml b/app/src/main/res/animator/appbar_elevation_on.xml deleted file mode 100644 index 6bd52cf3b..000000000 --- a/app/src/main/res/animator/appbar_elevation_on.xml +++ /dev/null @@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<selector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - tools:ignore="PrivateResource"> - <item> - <objectAnimator - android:propertyName="elevation" - android:valueTo="@dimen/design_appbar_elevation" - android:valueType="floatType" /> - </item> -</selector> diff --git a/app/src/main/res/drawable-hdpi/ic_splash_screen.png b/app/src/main/res/drawable-hdpi/ic_splash_screen.png Binary files differdeleted file mode 100644 index 070efc396..000000000 --- a/app/src/main/res/drawable-hdpi/ic_splash_screen.png +++ /dev/null diff --git a/app/src/main/res/drawable-mdpi/ic_splash_screen.png b/app/src/main/res/drawable-mdpi/ic_splash_screen.png Binary files differdeleted file mode 100644 index 0a3717448..000000000 --- a/app/src/main/res/drawable-mdpi/ic_splash_screen.png +++ /dev/null diff --git a/app/src/main/res/drawable-xhdpi/ic_splash_screen.png b/app/src/main/res/drawable-xhdpi/ic_splash_screen.png Binary files differdeleted file mode 100644 index bfe6dca49..000000000 --- a/app/src/main/res/drawable-xhdpi/ic_splash_screen.png +++ /dev/null diff --git a/app/src/main/res/drawable-xxhdpi/ic_splash_screen.png b/app/src/main/res/drawable-xxhdpi/ic_splash_screen.png Binary files differdeleted file mode 100644 index c34788f0e..000000000 --- a/app/src/main/res/drawable-xxhdpi/ic_splash_screen.png +++ /dev/null diff --git a/app/src/main/res/drawable-xxxhdpi/ic_splash_screen.png b/app/src/main/res/drawable-xxxhdpi/ic_splash_screen.png Binary files differdeleted file mode 100644 index 32c45e466..000000000 --- a/app/src/main/res/drawable-xxxhdpi/ic_splash_screen.png +++ /dev/null diff --git a/app/src/main/res/drawable/bottom_sheet_rounded.xml b/app/src/main/res/drawable/bottom_sheet_rounded.xml deleted file mode 100644 index ba266ed32..000000000 --- a/app/src/main/res/drawable/bottom_sheet_rounded.xml +++ /dev/null @@ -1,9 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="rectangle"> - <solid android:color="?attr/colorSurface" /> - <corners - android:topLeftRadius="16dp" - android:topRightRadius="16dp" /> - -</shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/circle.xml b/app/src/main/res/drawable/circle.xml deleted file mode 100644 index 8a1da5d66..000000000 --- a/app/src/main/res/drawable/circle.xml +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<shape xmlns:android="http://schemas.android.com/apk/res/android" - android:shape="ring" - android:innerRadiusRatio="2.5" - android:thickness="4dp" - android:useLevel="true"> - <solid android:color="@color/defaultBrand" /> -</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/due_tomorrow_background.xml b/app/src/main/res/drawable/due_tomorrow_background.xml deleted file mode 100644 index a986c42e2..000000000 --- a/app/src/main/res/drawable/due_tomorrow_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_tomorrow" /> - - <corners - android:radius="4dp" /> -</shape>
\ No newline at end of file diff --git a/app/src/main/res/drawable/ic_check_box_24.xml b/app/src/main/res/drawable/ic_check_box_24.xml new file mode 100644 index 000000000..0c3970b1a --- /dev/null +++ b/app/src/main/res/drawable/ic_check_box_24.xml @@ -0,0 +1,5 @@ +<vector android:autoMirrored="true" android:height="24dp" + android:tint="#757575" 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="M19,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.11,0 2,-0.9 2,-2L21,5c0,-1.1 -0.89,-2 -2,-2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_check_circle_24.xml b/app/src/main/res/drawable/ic_check_circle_24.xml new file mode 100644 index 000000000..45c61fa3f --- /dev/null +++ b/app/src/main/res/drawable/ic_check_circle_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="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/> +</vector> diff --git a/app/src/main/res/drawable/ic_format_paint_grey600_24dp.xml b/app/src/main/res/drawable/ic_format_paint_grey600_24dp.xml deleted file mode 100644 index a2308a55f..000000000 --- a/app/src/main/res/drawable/ic_format_paint_grey600_24dp.xml +++ /dev/null @@ -1,5 +0,0 @@ -<vector android:autoMirrored="true" android:height="24dp" - android:tint="#757575" android:viewportHeight="24.0" - android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> - <path android:fillColor="#FF000000" android:pathData="M18,4V3c0,-0.55 -0.45,-1 -1,-1H5c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1V6h1v4H9v11c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-9h8V4h-3z"/> -</vector> 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/drawable/splash_screen.xml b/app/src/main/res/drawable/splash_screen.xml deleted file mode 100644 index d22531b3f..000000000 --- a/app/src/main/res/drawable/splash_screen.xml +++ /dev/null @@ -1,12 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> - - <item android:drawable="@color/defaultBrand" /> - - <item> - <bitmap - android:gravity="center" - android:src="@drawable/ic_splash_screen" /> - </item> - -</layer-list>
\ No newline at end of file diff --git a/app/src/main/res/layout/dialog_filter.xml b/app/src/main/res/layout/dialog_filter.xml index b95c2b0c8..2b1e3499c 100644 --- a/app/src/main/res/layout/dialog_filter.xml +++ b/app/src/main/res/layout/dialog_filter.xml @@ -11,7 +11,7 @@ android:layout_height="wrap_content" android:background="@null" app:tabInlineLabel="true" - app:tabMode="fixed" + app:tabMode="auto" app:tabUnboundedRipple="true" /> <androidx.viewpager2.widget.ViewPager2 diff --git a/app/src/main/res/layout/dialog_filter_done.xml b/app/src/main/res/layout/dialog_filter_done.xml new file mode 100644 index 000000000..5cf3fedb0 --- /dev/null +++ b/app/src/main/res/layout/dialog_filter_done.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/doneType" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="?attr/dialogPreferredPadding" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + tools:listitem="@layout/item_filter_duetype" />
\ No newline at end of file diff --git a/app/src/main/res/layout/fragment_card_edit_tab_details.xml b/app/src/main/res/layout/fragment_card_edit_tab_details.xml index 631bc342f..c39a00fc5 100644 --- a/app/src/main/res/layout/fragment_card_edit_tab_details.xml +++ b/app/src/main/res/layout/fragment_card_edit_tab_details.xml @@ -69,81 +69,22 @@ android:layout_marginTop="@dimen/spacer_1x" android:layout_marginBottom="@dimen/spacer_1hx" app:layoutManager="androidx.recyclerview.widget.GridLayoutManager" - app:layout_constraintBottom_toTopOf="@id/dueDateDateWrapper" + app:layout_constraintBottom_toTopOf="@id/cardDueDateView" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/peopleWrapper" tools:listitem="@tools:sample/avatars" /> - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/dueDateDateWrapper" - android:layout_width="0dp" + <it.niedermann.nextcloud.deck.ui.card.details.CardDueDateView + android:id="@+id/cardDueDateView" + android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="@dimen/spacer_2x" - android:layout_marginEnd="@dimen/spacer_2x" - android:hint="@string/hint_due_date_date" - android:labelFor="@id/dueDateDate" app:layout_constraintBottom_toTopOf="@id/descriptionWrapper" - app:layout_constraintEnd_toStartOf="@id/dueDateTimeWrapper" - app:layout_constraintHorizontal_weight="2" + app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/assignees" - app:startIconDrawable="@drawable/calendar_blank_grey600_24dp"> - - <EditText - android:id="@+id/dueDateDate" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:enabled="true" - android:focusable="false" - android:importantForAutofill="no" - android:inputType="date" - android:maxLines="1" - tools:text="01/07/2020" /> - </com.google.android.material.textfield.TextInputLayout> + app:layout_constraintTop_toBottomOf="@id/assignees" /> - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/dueDateTimeWrapper" - android:layout_width="0dp" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/spacer_1hx" - android:hint="@string/hint_due_date_time" - android:labelFor="@id/dueDateTime" - app:layout_constraintBottom_toBottomOf="@id/dueDateDateWrapper" - app:layout_constraintEnd_toStartOf="@id/clearDueDate" - app:layout_constraintHorizontal_weight="1" - app:layout_constraintStart_toEndOf="@id/dueDateDateWrapper" - app:layout_constraintTop_toTopOf="@id/dueDateDateWrapper"> - - <EditText - android:id="@+id/dueDateTime" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:enabled="true" - android:focusable="false" - android:importantForAutofill="no" - android:inputType="datetime" - android:maxLines="1" - android:minLines="0" - android:textAlignment="center" - tools:text="11:45" /> - </com.google.android.material.textfield.TextInputLayout> - - <ImageView - android:id="@+id/clearDueDate" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginVertical="@dimen/spacer_1hx" - android:layout_marginStart="@dimen/spacer_1x" - android:layout_marginEnd="0dp" - android:contentDescription="@string/label_clear_due_date" - android:padding="@dimen/spacer_11qx" - android:translationY="@dimen/spacer_1hx" - app:layout_constraintBottom_toBottomOf="@id/dueDateTimeWrapper" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintStart_toEndOf="@id/dueDateTimeWrapper" - app:layout_constraintTop_toTopOf="@id/dueDateTimeWrapper" - app:srcCompat="@drawable/ic_close_circle_grey600" /> <RelativeLayout android:id="@+id/descriptionWrapper" @@ -153,7 +94,7 @@ app:layout_constraintBottom_toTopOf="@id/projectsTitle" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/dueDateDateWrapper"> + app:layout_constraintTop_toBottomOf="@id/cardDueDateView"> <com.google.android.material.textfield.TextInputLayout android:id="@+id/descriptionEditorWrapper" diff --git a/app/src/main/res/layout/item_card_compact.xml b/app/src/main/res/layout/item_card_compact.xml index 3a14b0d43..75d95f796 100644 --- a/app/src/main/res/layout/item_card_compact.xml +++ b/app/src/main/res/layout/item_card_compact.xml @@ -52,15 +52,12 @@ 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" + app:compactMode="true" + 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..3dfcff46e 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> @@ -103,9 +99,10 @@ android:id="@+id/card_count_tasks" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:drawablePadding="@dimen/spacer_1qx" android:gravity="center_vertical" android:padding="@dimen/spacer_1hx" - tools:drawableStartCompat="@drawable/ic_check_grey600_24dp" + tools:drawableStartCompat="@drawable/ic_check_box_24" tools:text="1/2" /> <com.google.android.material.textview.MaterialTextView 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 diff --git a/app/src/main/res/layout/item_filter_donetype.xml b/app/src/main/res/layout/item_filter_donetype.xml new file mode 100644 index 000000000..9cf823c77 --- /dev/null +++ b/app/src/main/res/layout/item_filter_donetype.xml @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="utf-8"?> +<com.google.android.flexbox.FlexboxLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="?attr/selectableItemBackground" + android:orientation="horizontal"> + + <TextView + android:id="@+id/doneType" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:ellipsize="middle" + android:padding="@dimen/spacer_2x" + android:textAppearance="?attr/textAppearanceListItem" + tools:text="@tools:sample/lorem" /> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/selected_check" + android:layout_width="22dp" + android:layout_height="22dp" + android:layout_marginStart="@dimen/spacer_1x" + app:layout_alignSelf="center" + app:layout_flexShrink="0" + app:srcCompat="@drawable/selected_check" + tools:src="@drawable/ic_check_grey600_24dp" /> + +</com.google.android.flexbox.FlexboxLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/view_card_due_date.xml b/app/src/main/res/layout/view_card_due_date.xml new file mode 100644 index 000000000..77398f5b1 --- /dev/null +++ b/app/src/main/res/layout/view_card_due_date.xml @@ -0,0 +1,143 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + <ImageView + android:id="@+id/doneCheck" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacer_1qx" + android:layout_marginEnd="0dp" + android:layout_marginBottom="@dimen/spacer_1hx" + android:contentDescription="@null" + android:padding="@dimen/spacer_11qx" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" + app:srcCompat="@drawable/ic_check_white_24dp" + app:tint="?attr/colorOnSurface" + tools:visibility="gone" /> + + <TextView + android:id="@+id/doneDate" + style="?attr/textAppearanceBody1" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintBottom_toBottomOf="@id/doneCheck" + app:layout_constraintEnd_toStartOf="@id/clearDone" + app:layout_constraintStart_toEndOf="@id/doneCheck" + app:layout_constraintTop_toTopOf="@id/doneCheck" + tools:text="@tools:sample/date/ddmmyy" + tools:visibility="gone" /> + + <TextView + android:id="@+id/done_due_date" + style="?attr/textAppearanceBody1" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacer_1x" + app:layout_constraintEnd_toStartOf="@id/clearDone" + app:layout_constraintStart_toEndOf="@id/doneCheck" + app:layout_constraintTop_toBottomOf="@id/doneDate" + tools:text="@tools:sample/date/ddmmyy" + tools:visibility="gone" /> + + <ImageView + android:id="@+id/clearDone" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginVertical="@dimen/spacer_1hx" + android:layout_marginStart="@dimen/spacer_1x" + android:layout_marginEnd="0dp" + android:contentDescription="@string/label_clear_done" + android:padding="@dimen/spacer_11qx" + app:layout_constraintBottom_toBottomOf="@id/doneDate" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="@id/doneDate" + app:srcCompat="@drawable/ic_close_circle_grey600" + tools:visibility="gone" /> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/dueDateDateWrapper" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginEnd="@dimen/spacer_2x" + android:hint="@string/hint_due_date_date" + android:labelFor="@id/dueDateDate" + app:layout_constraintBottom_toTopOf="@id/markAsDone" + app:layout_constraintEnd_toStartOf="@id/dueDateTimeWrapper" + app:layout_constraintHorizontal_weight="2" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@id/done_due_date" + app:startIconDrawable="@drawable/calendar_blank_grey600_24dp"> + + <EditText + android:id="@+id/dueDateDate" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:enabled="true" + android:focusable="false" + android:importantForAutofill="no" + android:inputType="date" + android:maxLines="1" + tools:text="01/07/2020" /> + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/dueDateTimeWrapper" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/spacer_1hx" + android:hint="@string/hint_due_date_time" + android:labelFor="@id/dueDateTime" + app:layout_constraintBottom_toBottomOf="@id/dueDateDateWrapper" + app:layout_constraintEnd_toStartOf="@id/clearDueDate" + app:layout_constraintHorizontal_weight="1" + app:layout_constraintStart_toEndOf="@id/dueDateDateWrapper" + app:layout_constraintTop_toTopOf="@id/dueDateDateWrapper"> + + <EditText + android:id="@+id/dueDateTime" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:enabled="true" + android:focusable="false" + android:importantForAutofill="no" + android:inputType="datetime" + android:maxLines="1" + android:minLines="0" + android:textAlignment="center" + tools:text="11:45" /> + </com.google.android.material.textfield.TextInputLayout> + + <ImageView + android:id="@+id/clearDueDate" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginVertical="@dimen/spacer_1hx" + android:layout_marginStart="@dimen/spacer_1x" + android:layout_marginEnd="0dp" + android:contentDescription="@string/label_clear_due_date" + android:padding="@dimen/spacer_11qx" + android:translationY="@dimen/spacer_1hx" + app:layout_constraintBottom_toBottomOf="@id/dueDateTimeWrapper" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/dueDateTimeWrapper" + app:layout_constraintTop_toTopOf="@id/dueDateTimeWrapper" + app:srcCompat="@drawable/ic_close_circle_grey600" /> + + <com.google.android.material.button.MaterialButton + android:id="@+id/markAsDone" + style="@style/Widget.Material3.Button" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/spacer_2x" + android:layout_marginBottom="@dimen/spacer_1hx" + android:text="@string/simple_completed" + app:backgroundTint="@color/defaultBrand" + app:icon="@drawable/ic_check_white_24dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toBottomOf="@id/dueDateDateWrapper" /> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/widget_single_card.xml b/app/src/main/res/layout/widget_single_card.xml index 5646b98f1..b868a59d5 100644 --- a/app/src/main/res/layout/widget_single_card.xml +++ b/app/src/main/res/layout/widget_single_card.xml @@ -39,7 +39,7 @@ android:layout_height="wrap_content" android:layout_gravity="center" android:contentDescription="@null" - tools:src="@drawable/calendar_blank_grey600_24dp" /> + tools:src="@drawable/ic_time_24" /> <TextView android:id="@+id/card_due_date" diff --git a/app/src/main/res/menu/archived_card_menu.xml b/app/src/main/res/menu/archived_card_menu.xml deleted file mode 100644 index 12448e691..000000000 --- a/app/src/main/res/menu/archived_card_menu.xml +++ /dev/null @@ -1,13 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<menu xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto"> - <item - android:id="@+id/action_card_dearchive" - android:title="@string/action_card_dearchive" - app:showAsAction="never" /> - - <item - android:id="@+id/action_card_delete" - android:title="@string/action_card_delete" - app:showAsAction="never" /> -</menu> diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index d3e6cb425..81e38f628 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -34,10 +34,10 @@ <!-- ======================================= --> <!-- Due Date badges --> - <color name="due_tomorrow">#232323</color> - <color name="due_today">#ac7c06</color> - <color name="due_overdue">#aa2926</color> - <color name="due_text_tomorrow">#ffffff</color> - <color name="due_text_today">#ffffff</color> - <color name="due_text_overdue">#ffffff</color> + <color name="due_today">#19c28900</color> + <color name="due_overdue">#19ee312b</color> + <color name="due_done">#1936914e</color> + <color name="due_text_today">#c28900</color> + <color name="due_text_overdue">#f36864</color> + <color name="due_text_done">#3ea75a</color> </resources> diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 715f8b814..8e70e68ea 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -8,4 +8,7 @@ <attr name="description" format="string" /> <attr name="image" format="reference" /> </declare-styleable> + <declare-styleable name="DueDateChip"> + <attr name="compactMode" format="boolean" /> + </declare-styleable> </resources>
\ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 1983b6367..b556a9e94 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -37,12 +37,12 @@ <!-- ======================================= --> <!-- Due Date badges --> - <color name="due_tomorrow">#f2f2f2</color> - <color name="due_today">#f1c14b</color> - <color name="due_overdue">#ef6e6b</color> - <color name="due_text_tomorrow">#666666</color> - <color name="due_text_today">#333333</color> - <color name="due_text_overdue">#ffffff</color> + <color name="due_today">#f8f2e5</color> + <color name="due_overdue">#fbe7e6</color> + <color name="due_done">#e9f1eb</color> + <color name="due_text_today">#855d00</color> + <color name="due_text_overdue">#c61610</color> + <color name="due_text_done">#286c39</color> <!-- Activity --> <color name="activity_create">#00D400</color> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c6eca37fa..4b59ce20d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,7 +5,6 @@ <string name="navigation_drawer_close">Close navigation drawer</string> <string name="hint_search_deck">Search deck</string> - <string name="simple_boards">Boards</string> <string name="simple_add">Add</string> <string name="simple_save">Save</string> <string name="simple_more">More</string> @@ -16,11 +15,9 @@ <string name="simple_error">Error</string> <string name="simple_exception">Exception</string> <string name="simple_close">Close</string> - <string name="simple_open">Open</string> - <string name="simple_switch">Switch</string> <string name="simple_filter">Filter</string> - <string name="simple_overdue">Overdue</string> - <string name="simple_clear">Clear</string> + <string name="simple_completed">Completed</string> + <string name="simple_reset">Reset</string> <string name="simple_discard">Discard</string> <string name="simple_update">Update</string> <string name="simple_delete">Delete</string> @@ -32,7 +29,6 @@ <string name="simple_select">Select</string> <string name="simple_comment">Comment</string> <string name="simple_disabled">disabled</string> - <string name="simple_copied">Copied</string> <string name="simple_archive">Archive</string> <string name="simple_unassigned">Unassigned</string> <string name="hint_error_appeared">An error appeared</string> @@ -97,6 +93,8 @@ <string name="add_board">Add board</string> <string name="label_clear_due_date">Clear due date</string> + <string name="label_clear_done">Not completed</string> + <string name="label_due_at">Due at: %1$s</string> <string name="label_add">Add %1$s</string> <string name="url_maintainer" translatable="false">https://www.niedermann.it/</string> @@ -112,6 +110,7 @@ <string name="url_fragment_server_logs" translatable="false">/index.php/settings/admin/logging</string> <string name="url_fragment_share_card_pre_1_0_0" translatable="false">/index.php/apps/deck/#!/board/%1$d/card/%2$d</string> <string name="url_fragment_share_card_since_1_0_0" translatable="false">/index.php/apps/deck/#/board/%1$d/card/%2$d</string> + <string name="url_fragment_share_card_since_1_12_0" translatable="false">/index.php/apps/deck/board/%1$d/card/%2$d</string> <string name="card_edit_details">Details</string> <string name="card_edit_attachments">Attachments</string> @@ -120,9 +119,7 @@ <string name="no_files_attached_to_this_card">There are no files attached to this card.</string> <string name="attachments">Attachments</string> <string name="no_cards">No cards yet</string> - <string name="no_account">No account configured</string> <string name="account_already_added">The account %1$s has already been added</string> - <string name="account_is_getting_imported">Account is getting imported</string> <string name="not_synced_yet">Not synced yet</string> <string name="no_lists_yet">No lists yet</string> <string name="do_you_want_to_save_your_changes">Do you want to save your changes?</string> @@ -138,11 +135,9 @@ </plurals> <string name="add_a_new_list_using_the_button">Add a new list using the + button</string> <string name="add_a_new_card_using_the_button">Add a new card using the + button</string> - <string name="update_deck">Update Deck web app</string> <string name="deck_outdated_please_update">Your Deck web app version is too old (%1$s). Please update to use this Android app as client.</string> <string name="delete_board_message">This will permanently delete this board including all lists and cards.</string> <string name="settings_theme_title">Theme</string> - <string name="settings_branding_title">Branding</string> <string name="settings_compact_title">Compact mode</string> <string name="settings_cover_images_title">Cover images</string> <string name="settings_debugging">Debug logs</string> @@ -178,7 +173,6 @@ <string name="provide_at_least_a_title_or_description">Provide at least a title or description</string> <string name="welcome_text">Welcome to %1$s</string> <string name="welcome_text_further_accounts">Add another account</string> - <string name="maintenance_mode_explanation">The server %1$s is currently in maintenance mode. Please contact your administrator or try later again.</string> <string name="share_add_to_card">Add to card</string> <string name="share_success">Successfully added %1$s to %2$s</string> <string name="add_comment">Add comment</string> @@ -187,7 +181,6 @@ <string name="no_boards">No boards yet</string> <string name="add_a_new_board_using_the_button">Add a new board using the + button</string> <string name="choose_board">Choose board</string> - <string name="choose_list">Choose list</string> <string name="task_count">%1$s/%2$s</string> <string name="open_in_browser">Open in browser</string> <string name="updating_card">Updating card…</string> @@ -203,13 +196,12 @@ <string name="filter_month">Next 30 days</string> <string name="filter_no_due">No due date</string> <string name="filter_later">Later</string> - <string name="filter_by_tag">Filter by tag</string> - <string name="filter_by_assigned_user">Filter by assigned user</string> - <string name="filter_by_duedate">Filter by due date</string> + + <string name="filter_done_no_filter">All</string> + <string name="filter_done_done">Completed</string> + <string name="filter_done_undone">Not completed</string> <string name="archived_cards">Archived cards</string> - <string name="action_card_dearchive">Undo card archiving</string> - <string name="action_archived_cards">Browse archived cards</string> <string name="attachment_already_exists">Attachment already exists</string> <string name="pick_custom_color">Pick custom color</string> <string name="manage_tags">Manage tags</string> @@ -220,6 +212,7 @@ <string name="filter_tags_title">Tags</string> <string name="filter_user_title">Users</string> <string name="filter_duedate_title">Due date</string> + <string name="filter_done_title">Completed</string> <string name="action_board_dearchive">Undo board archiving</string> <string name="archived_boards">Archived boards</string> @@ -230,7 +223,6 @@ <string name="error_revoking_ac">Error while revoking the access for %1$s</string> <string name="error_create_label">Error while creating label %1$s</string> <string name="maintenance_mode">Server is in maintenance mode</string> - <string name="server_error">Server error</string> <string name="error_edit_activity_killed_by_android">Android finished the edit mode because it needed more system resources for other apps.</string> <string name="error_dialog_title">Oh no - What now? 🙁</string> @@ -298,10 +290,8 @@ <string name="card_does_not_yet_exist">Card does not yet exist in Deck</string> <string name="widget_stack_title">List</string> - <string name="widget_filter_title">Filter</string> <string name="widget_stack_header_icon">Widget header icon</string> <string name="widget_placeholder_icon">Widget placeholder icon</string> - <string name="select_stack">Select list</string> <string name="project_type_deck_board">Deck board</string> <string name="project_type_deck_card">Deck card</string> <string name="project_type_file">File</string> @@ -319,9 +309,6 @@ <string name="simple_clone">Clone</string> <string name="user_avatar">User avatar</string> <string name="simple_unassign">Unassign</string> - <string name="simple_contact">Contact</string> - <string name="simple_file">File</string> - <string name="simple_camera">Camera</string> <string name="take_photo">Take a photo</string> <string name="take_photo_switch_camera">Switch camera</string> <string name="take_photo_toggle_torch">Toggle torch</string> |