Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/stefan-niedermann/nextcloud-deck.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Niedermann <info@niedermann.it>2020-07-13 16:23:54 +0300
committerStefan Niedermann <info@niedermann.it>2020-07-13 16:23:54 +0300
commit6403676b4f6f6a29c06aa2dfa9e502dc4b54c888 (patch)
treeaf5de454eadda978abc995a5d910ffbc0c00ab6a /app/src/main
parent4ad16e456e1b5dea50ade8ef973a8386493aa0c3 (diff)
parentf8283d1a77aba90b45ec9d787606a973c4df6137 (diff)
Merge branch 'master' into stack-widget
# Conflicts: # app/src/main/res/values/strings.xml
Diffstat (limited to 'app/src/main')
-rw-r--r--app/src/main/AndroidManifest.xml3
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java2
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java26
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/exceptions/TraceableException.java18
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/model/internal/FilterInformation.java2
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java139
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java4
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java5
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/extrawurst/UserSearchLiveData.java6
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/SyncHelper.java5
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/LabelDataProvider.java6
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithAclDownSyncDataProvider.java (renamed from app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWitAclDownSyncDataProvider.java)2
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithStacksAndLabelsUpSyncDataProvider.java37
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java35
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java23
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java4
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/board/ArchiveBoardListener.java1
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java14
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java2
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java121
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java92
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java84
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardOnlyTitleViewHolder.java61
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java (renamed from app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardViewHolder.java)76
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentDeletedListener.java4
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/CardAttachmentsFragment.java198
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java124
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardListener.java5
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackFragment.java195
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackListener.java12
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java4
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java22
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/CompactLabelChip.java22
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/DefaultLabelChip.java21
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/LabelChip.java (renamed from app/src/main/java/it/niedermann/nextcloud/deck/ui/view/LabelChip.java)17
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/CompactLabelLayout.java22
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/DefaultLabelLayout.java21
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java (renamed from app/src/main/java/it/niedermann/nextcloud/deck/ui/view/LabelLayout.java)14
-rw-r--r--app/src/main/java/it/niedermann/nextcloud/deck/util/DrawerMenuUtil.java3
-rw-r--r--app/src/main/res/drawable/ic_baseline_compact_24.xml5
-rw-r--r--app/src/main/res/layout/activity_archived.xml2
-rw-r--r--app/src/main/res/layout/dialog_move_card.xml63
-rw-r--r--app/src/main/res/layout/fragment_pick_stack.xml28
-rw-r--r--app/src/main/res/layout/fragment_stack.xml2
-rw-r--r--app/src/main/res/layout/item_card_compact.xml94
-rw-r--r--app/src/main/res/layout/item_card_default.xml (renamed from app/src/main/res/layout/item_card.xml)2
-rw-r--r--app/src/main/res/layout/item_card_default_only_title.xml77
-rw-r--r--app/src/main/res/menu/navigation_context_menu.xml5
-rw-r--r--app/src/main/res/values-cs-rCZ/strings.xml1
-rw-r--r--app/src/main/res/values-de/strings.xml1
-rw-r--r--app/src/main/res/values-el/strings.xml6
-rw-r--r--app/src/main/res/values-es/strings.xml1
-rw-r--r--app/src/main/res/values-eu/strings.xml2
-rw-r--r--app/src/main/res/values-fr/strings.xml4
-rw-r--r--app/src/main/res/values-gl/strings.xml5
-rw-r--r--app/src/main/res/values-it/strings.xml1
-rw-r--r--app/src/main/res/values-pl/strings.xml2
-rw-r--r--app/src/main/res/values-pt-rBR/strings.xml1
-rw-r--r--app/src/main/res/values-ru/strings.xml1
-rw-r--r--app/src/main/res/values-sk-rSK/strings.xml1
-rw-r--r--app/src/main/res/values-sr/strings.xml15
-rw-r--r--app/src/main/res/values-tr/strings.xml1
-rw-r--r--app/src/main/res/values/dimens.xml2
-rw-r--r--app/src/main/res/values/setup.xml1
-rw-r--r--app/src/main/res/values/strings.xml5
-rw-r--r--app/src/main/res/xml/settings.xml8
66 files changed, 1486 insertions, 302 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 4b62c8cc5..0d9268e49 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,10 +3,7 @@
xmlns:tools="http://schemas.android.com/tools"
package="it.niedermann.nextcloud.deck">
- <uses-permission android:name="com.nextcloud.android.sso" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name="it.niedermann.nextcloud.deck.DeckApplication"
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java
index b9f6f403d..7c1727fb7 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/GsonConfig.java
@@ -40,6 +40,7 @@ public class GsonConfig {
Type capabilities = new TypeToken<Capabilities>() {}.getType();
Type ocsUserList = new TypeToken<OcsUserList>() {}.getType();
Type activity = new TypeToken<Activity>() {}.getType();
+ Type activityList = new TypeToken<List<Activity>>() {}.getType();
Type attachment = new TypeToken<Attachment>() {}.getType();
Type attachmentList = new TypeToken<List<Attachment>>() {}.getType();
Type comment = new TypeToken<OcsComment>() {}.getType();
@@ -59,6 +60,7 @@ public class GsonConfig {
.registerTypeAdapter(capabilities, new NextcloudDeserializer<>("capability", Capabilities.class))
.registerTypeAdapter(ocsUserList, new NextcloudDeserializer<>("ocsUserList", OcsUserList.class))
.registerTypeAdapter(activity, new NextcloudDeserializer<>("activity", Activity.class))
+ .registerTypeAdapter(activityList, new NextcloudDeserializer<>("activityList", Activity.class))
.registerTypeAdapter(attachmentList, new NextcloudArrayDeserializer<>("attachments", Attachment.class))
.registerTypeAdapter(attachment, new NextcloudDeserializer<>("attachment", Attachment.class))
.registerTypeAdapter(comment, new NextcloudDeserializer<>("comment", OcsComment.class))
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java b/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java
index 2bd7764e9..578104bba 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/api/JsonToEntityParser.java
@@ -14,7 +14,6 @@ import java.util.List;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.exceptions.DeckException;
-import it.niedermann.nextcloud.deck.exceptions.TraceableException;
import it.niedermann.nextcloud.deck.model.AccessControl;
import it.niedermann.nextcloud.deck.model.Attachment;
import it.niedermann.nextcloud.deck.model.Board;
@@ -36,6 +35,7 @@ import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser;
import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList;
import static it.niedermann.nextcloud.deck.exceptions.DeckException.Hint.CAPABILITIES_VERSION_NOT_PARSABLE;
+import static it.niedermann.nextcloud.deck.exceptions.TraceableException.makeTraceableIfFails;
public class JsonToEntityParser {
@@ -65,7 +65,7 @@ public class JsonToEntityParser {
private static OcsUserList parseOcsUserList(JsonObject obj) {
DeckLog.verbose(obj.toString());
OcsUserList ocsUserList = new OcsUserList();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
JsonElement data = obj.get("ocs").getAsJsonObject().get("data");
if (!data.isJsonNull() && data.getAsJsonObject().has("users")) {
JsonElement users = data.getAsJsonObject().get("users");
@@ -90,7 +90,7 @@ public class JsonToEntityParser {
private static OcsComment parseOcsComment(JsonObject obj) {
DeckLog.verbose(obj.toString());
OcsComment comment = new OcsComment();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
JsonElement data = obj.get("ocs").getAsJsonObject().get("data");
if (data.isJsonArray()) {
for (JsonElement deckComment : data.getAsJsonArray()) {
@@ -107,7 +107,7 @@ public class JsonToEntityParser {
DeckLog.verbose(data.toString());
DeckComment deckComment = new DeckComment();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
JsonObject commentJson = data.getAsJsonObject();
deckComment.setId(commentJson.get("id").getAsLong());
@@ -138,7 +138,7 @@ public class JsonToEntityParser {
DeckLog.verbose(mentionJson.toString());
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
JsonObject mentionObject = mentionJson.getAsJsonObject();
mention.setMentionId(mentionObject.get("mentionId").getAsString());
mention.setMentionType(mentionObject.get("mentionType").getAsString());
@@ -155,7 +155,7 @@ public class JsonToEntityParser {
DeckLog.verbose(e.toString());
Board board = new Board();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
board.setTitle(getNullAsEmptyString(e.get("title")));
board.setColor(getNullAsEmptyString(e.get("color")));
board.setArchived(e.get("archived").getAsBoolean());
@@ -234,7 +234,7 @@ public class JsonToEntityParser {
AccessControl acl = new AccessControl();
if (aclJson.has("participant") && !aclJson.get("participant").isJsonNull()) {
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
User participant = parseUser(aclJson.get("participant").getAsJsonObject());
acl.setUser(participant);
acl.setType(aclJson.get("type").getAsLong());
@@ -257,7 +257,7 @@ public class JsonToEntityParser {
FullCard fullCard = new FullCard();
Card card = new Card();
fullCard.setCard(card);
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
card.setId(e.get("id").getAsLong());
card.setTitle(getNullAsEmptyString(e.get("title")));
card.setDescription(getNullAsEmptyString(e.get("description")));
@@ -322,7 +322,7 @@ public class JsonToEntityParser {
protected static Attachment parseAttachment(JsonObject e) {
DeckLog.verbose(e.toString());
Attachment a = new Attachment();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
a.setId(e.get("id").getAsLong());
a.setCardId(e.get("cardId").getAsLong());
a.setType(e.get("type").getAsString());
@@ -353,7 +353,7 @@ public class JsonToEntityParser {
protected static User parseUser(JsonObject e) {
DeckLog.verbose(e.toString());
User user = new User();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
user.setDisplayname(getNullAsEmptyString(e.get("displayname")));
user.setPrimaryKey(getNullAsEmptyString(e.get("primaryKey")));
user.setUid(getNullAsEmptyString(e.get("uid")));
@@ -419,7 +419,7 @@ public class JsonToEntityParser {
DeckLog.verbose(e.toString());
List<Activity> activityList = new ArrayList<>();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
if (e.has("ocs")) {
JsonObject ocs = e.getAsJsonObject("ocs");
if (ocs.has("data")) {
@@ -447,7 +447,7 @@ public class JsonToEntityParser {
FullStack fullStack = new FullStack();
Stack stack = new Stack();
fullStack.setStack(stack);
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
stack.setTitle(getNullAsEmptyString(e.get("title")));
stack.setBoardId(e.get("boardId").getAsLong());
stack.setId(e.get("id").getAsLong());
@@ -474,7 +474,7 @@ public class JsonToEntityParser {
protected static Label parseLabel(JsonObject e) {
DeckLog.verbose(e.toString());
Label label = new Label();
- TraceableException.makeTraceableIfFails(() -> {
+ makeTraceableIfFails(() -> {
label.setId(e.get("id").getAsLong());
//todo: last modified!
// label.setLastModified(get);
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/TraceableException.java b/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/TraceableException.java
index a53ce8c1f..7acfb60bd 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/TraceableException.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/exceptions/TraceableException.java
@@ -12,21 +12,21 @@ public class TraceableException extends RuntimeException {
try {
runnable.run();
} catch (Throwable t) {
- String message = "Sorry, a wild error appeared!\n" +
- "### If you want to tell us about the following issue, " +
- "please make sure to censor sensitive data beforehand! ###\n" +
- "Failed to run traceable code";
+ final StringBuilder message = new StringBuilder("Sorry, a wild error appeared!\n\n" +
+ "⚠️ If you want to tell us about the following issue, " +
+ "please make sure to censor sensitive data beforehand! ⚠️\n\n" +
+ "Failed to run traceable code");
if (args != null && args.length > 0) {
- message += " with arguments:\n";
+ message.append(" with arguments:\n");
for (Object arg : args) {
- message += (arg == null ? "null" : arg.toString())+"\n";
+ message.append(arg == null ? "null" : arg.toString()).append("\n");
}
} else {
- message += ":\n";
+ message.append(":\n");
}
- message += "Cause: " + t.getLocalizedMessage();
- TraceableException ex = new TraceableException(message, t);
+ message.append("Cause: ").append(t.getLocalizedMessage());
+ TraceableException ex = new TraceableException(message.toString(), t);
DeckLog.logError(ex);
throw ex;
}
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 54fb192ff..a10097c36 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
@@ -77,7 +77,7 @@ public class FilterInformation implements Serializable {
}
/**
- * @return whether or not the given filterInformation has any actual filters set
+ * @return whether or not the given {@param filterInformation} has any actual filters set
*/
public static boolean hasActiveFilter(@Nullable FilterInformation filterInformation) {
if (filterInformation == null) {
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java
index d5b497683..850bdf9dc 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/SyncManager.java
@@ -21,6 +21,8 @@ import java.util.Date;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.concurrent.CountDownLatch;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.api.GsonConfig;
@@ -47,13 +49,10 @@ import it.niedermann.nextcloud.deck.model.ocs.Capabilities;
import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment;
import it.niedermann.nextcloud.deck.model.ocs.comment.OcsComment;
import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment;
-import it.niedermann.nextcloud.deck.model.ocs.user.OcsUser;
-import it.niedermann.nextcloud.deck.model.ocs.user.OcsUserList;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.ServerAdapter;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.DataBaseAdapter;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData;
-import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.extrawurst.Debouncer;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.extrawurst.UserSearchLiveData;
import it.niedermann.nextcloud.deck.persistence.sync.helpers.DataPropagationHelper;
import it.niedermann.nextcloud.deck.persistence.sync.helpers.SyncHelper;
@@ -67,7 +66,8 @@ import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.CardPropa
import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.DeckCommentsDataProvider;
import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.LabelDataProvider;
import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.StackDataProvider;
-import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.partial.BoardWitAclDownSyncDataProvider;
+import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.partial.BoardWithAclDownSyncDataProvider;
+import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.partial.BoardWithStacksAndLabelsUpSyncDataProvider;
import it.niedermann.nextcloud.deck.util.DateUtil;
import static java.net.HttpURLConnection.HTTP_UNAVAILABLE;
@@ -336,7 +336,7 @@ public class SyncManager {
public void onResponse(Boolean response) {
liveData.postValue(dataBaseAdapter.readAccountsForHostWithReadAccessToBoardDirectly(host, boardRemoteId));
}
- }).doSyncFor(new BoardWitAclDownSyncDataProvider());
+ }).doSyncFor(new BoardWithAclDownSyncDataProvider());
}
});
});
@@ -452,21 +452,43 @@ public class SyncManager {
* Does <strong>not</strong> clone any {@link Card} or {@link AccessControl} from the origin {@link Board}.
*/
@AnyThread
- public WrappedLiveData<FullBoard> cloneBoard(long originAccountId, long originBoardLocalId, long targetAccountId, String targetBoardTitle, String targetBoardColor) {
+ public WrappedLiveData<FullBoard> cloneBoard(long originAccountId, long originBoardLocalId, long targetAccountId, String targetBoardColor) {
WrappedLiveData<FullBoard> liveData = new WrappedLiveData<>();
doAsync(() -> {
Account originAccount = dataBaseAdapter.getAccountByIdDirectly(originAccountId);
User newOwner = dataBaseAdapter.getUserByUidDirectly(originAccountId, originAccount.getUserName());
FullBoard originalBoard = dataBaseAdapter.getFullBoardByLocalIdDirectly(originAccountId, originBoardLocalId);
+ String newBoardTitleBaseName = originalBoard.getBoard().getTitle().trim();
+ int newBoardTitleCopyIndex = 0;
+ //already a copy?
+ String regex = " \\(copy [0-9]+\\)$";
+ Pattern pattern = Pattern.compile(regex);
+ Matcher matcher = pattern.matcher(originalBoard.getBoard().getTitle());
+ if (matcher.find()) {
+ String found = matcher.group();
+ newBoardTitleBaseName = newBoardTitleBaseName.substring(0, newBoardTitleBaseName.length() - found.length());
+ Matcher indexMatcher = Pattern.compile("[0-9]+").matcher(found);
+ indexMatcher.find();
+ String oldIndexString = indexMatcher.group();
+ newBoardTitleCopyIndex = Integer.parseInt(oldIndexString);
+ }
+
+ String newBoardTitle;
+ do {
+ newBoardTitleCopyIndex++;
+ newBoardTitle = newBoardTitleBaseName + " (copy " + newBoardTitleCopyIndex + ")";
+
+ } while (dataBaseAdapter.getBoardForAccountByNameDirectly(targetAccountId, newBoardTitle) != null);
+
originalBoard.setAccountId(targetAccountId);
- originalBoard.getBoard().setTitle(targetBoardTitle);
+ originalBoard.setId(null);
+ originalBoard.setLocalId(null);
+ originalBoard.getBoard().setTitle(newBoardTitle);
originalBoard.getBoard().setColor(targetBoardColor);
- originalBoard.getBoard().setOwnerId(newOwner.getId());
+ originalBoard.getBoard().setOwnerId(newOwner.getLocalId());
originalBoard.setStatusEnum(DBStatus.LOCAL_EDITED);
originalBoard.setOwner(newOwner);
- originalBoard.setId(null);
- originalBoard.setLocalId(null);
long newBoardId = dataBaseAdapter.createBoardDirectly(originAccountId, originalBoard.getBoard());
originalBoard.setLocalId(newBoardId);
@@ -478,6 +500,7 @@ public class SyncManager {
stack.setBoardId(newBoardId);
dataBaseAdapter.createStack(targetAccountId, stack);
}
+ originalBoard.setStacks(null);
for (Label label : originalBoard.getLabels()) {
label.setLocalId(null);
label.setId(null);
@@ -486,21 +509,28 @@ public class SyncManager {
label.setBoardId(newBoardId);
dataBaseAdapter.createLabel(targetAccountId, label);
}
- Account targetAccount = dataBaseAdapter.getAccountByIdDirectly(targetAccountId);
- new SyncHelper(serverAdapter, dataBaseAdapter, null)
- .setResponseCallback(new IResponseCallback<Boolean>(targetAccount) {
- @Override
- public void onResponse(Boolean response) {
- liveData.postValue(dataBaseAdapter.getFullBoardByLocalIdDirectly(targetAccountId, newBoardId));
- }
-
- @Override
- public void onError(Throwable throwable) {
- super.onError(throwable);
- liveData.postError(throwable);
- }
- }).doSyncFor(new BoardDataProvider());
+ if (serverAdapter.hasInternetConnection()) {
+ Account targetAccount = dataBaseAdapter.getAccountByIdDirectly(targetAccountId);
+ ServerAdapter serverAdapterToUse = this.serverAdapter;
+ if (originAccountId != targetAccountId) {
+ serverAdapterToUse = new ServerAdapter(appContext, targetAccount.getName());
+ }
+ new SyncHelper(serverAdapterToUse, dataBaseAdapter, null)
+ .setResponseCallback(new IResponseCallback<Boolean>(targetAccount) {
+ @Override
+ public void onResponse(Boolean response) {
+ liveData.postValue(dataBaseAdapter.getFullBoardByLocalIdDirectly(targetAccountId, newBoardId));
+ }
+ @Override
+ public void onError(Throwable throwable) {
+ super.onError(throwable);
+ liveData.postError(throwable);
+ }
+ }).doUpSyncFor(new BoardWithStacksAndLabelsUpSyncDataProvider(originalBoard));
+ } else {
+ liveData.postValue(dataBaseAdapter.getFullBoardByLocalIdDirectly(targetAccountId, newBoardId));
+ }
});
return liveData;
}
@@ -626,8 +656,7 @@ public class SyncManager {
new AccessControlDataProvider(null, board, Collections.singletonList(entity)), entity, getCallbackToLiveDataConverter(account, liveData), ((entity1, response) -> {
response.setBoardId(entity.getBoardId());
response.setUserId(entity.getUser().getLocalId());
- }
- )
+ })
);
});
return liveData;
@@ -892,7 +921,7 @@ public class SyncManager {
doAsync(() -> {
FullCard fullCard = dataBaseAdapter.getFullCardByLocalIdDirectly(card.getAccountId(), card.getLocalId());
if (fullCard == null) {
- throw new IllegalArgumentException("card to delete does not exist.");
+ throw new IllegalArgumentException("card with id " + card.getLocalId() + " to delete does not exist.");
}
Account account = dataBaseAdapter.getAccountByIdDirectly(card.getAccountId());
FullStack stack = dataBaseAdapter.getFullStackByLocalIdDirectly(card.getStackId());
@@ -970,6 +999,7 @@ public class SyncManager {
return liveData;
}
+ // TODO return WrappedLiveData for error handling
@AnyThread
public void archiveBoard(@NonNull Board board) {
doAsync(() -> {
@@ -979,6 +1009,7 @@ public class SyncManager {
});
}
+ // TODO return WrappedLiveData for error handling
@AnyThread
public void dearchiveBoard(@NonNull Board board) {
doAsync(() -> {
@@ -1077,7 +1108,7 @@ public class SyncManager {
}
// ### get rid of original card where it is now.
Card originalInnerCard = originalCard.getCard();
- deleteCard(originalInnerCard);
+ deleteCard(new Card(originalInnerCard));
// ### clone card itself
Card targetCard = originalInnerCard;
targetCard.setAccountId(targetAccountId);
@@ -1086,7 +1117,9 @@ public class SyncManager {
targetCard.setStatusEnum(DBStatus.LOCAL_EDITED);
targetCard.setStackId(targetStackLocalId);
targetCard.setOrder(newIndex);
- //TODO: this needs to propagate to server as well, since anything else propagates as well (otherwise card isn't known on server)
+ targetCard.setArchived(false);
+ targetCard.setAttachmentCount(0);
+ targetCard.setCommentsUnread(0);
FullCard fullCardForServerPropagation = new FullCard();
fullCardForServerPropagation.setCard(targetCard);
@@ -1095,7 +1128,11 @@ public class SyncManager {
FullStack targetFullStack = dataBaseAdapter.getFullStackByLocalIdDirectly(targetStackLocalId);
User userOfTargetAccount = dataBaseAdapter.getUserByUidDirectly(targetAccountId, targetAccount.getUserName());
CountDownLatch latch = new CountDownLatch(1);
- new DataPropagationHelper(serverAdapter, dataBaseAdapter).createEntity(new CardPropagationDataProvider(null, targetBoard.getBoard(), targetFullStack), fullCardForServerPropagation, new IResponseCallback<FullCard>(targetAccount) {
+ ServerAdapter serverToUse = serverAdapter;
+ if (originAccountId != targetAccountId) {
+ serverToUse = new ServerAdapter(appContext, targetAccount.getName());
+ }
+ new DataPropagationHelper(serverToUse, dataBaseAdapter).createEntity(new CardPropagationDataProvider(null, targetBoard.getBoard(), targetFullStack), fullCardForServerPropagation, new IResponseCallback<FullCard>(targetAccount) {
@Override
public void onResponse(FullCard response) {
targetCard.setId(response.getId());
@@ -1109,8 +1146,10 @@ public class SyncManager {
throw new RuntimeException("unable to create card in moveCard target", throwable);
}
}, (FullCard entity, FullCard response) -> {
- response.getCard().setUserId(entity.getCard().getUserId());
+ response.getCard().setUserId(userOfTargetAccount.getLocalId());
response.getCard().setStackId(targetFullStack.getLocalId());
+ entity.getCard().setUserId(userOfTargetAccount.getLocalId());
+ entity.getCard().setStackId(targetFullStack.getLocalId());
});
try {
@@ -1153,10 +1192,10 @@ public class SyncManager {
originalLabel.setLocalId(null);
originalLabel.setStatusEnum(DBStatus.LOCAL_EDITED);
originalLabel.setAccountId(targetBoard.getAccountId());
- createAndAssignLabelToCard(originalBoard.getAccountId(), originalLabel, newCardId);
+ createAndAssignLabelToCard(targetBoard.getAccountId(), originalLabel, newCardId, serverToUse);
}
} else {
- assignLabelToCard(existingMatch, targetCard);
+ assignLabelToCard(existingMatch, targetCard, serverToUse);
}
}
@@ -1215,14 +1254,18 @@ public class SyncManager {
return liveData;
}
- @AnyThread
public MutableLiveData<Label> createAndAssignLabelToCard(long accountId, @NonNull Label label, long localCardId) {
+ return createAndAssignLabelToCard(accountId, label, localCardId, serverAdapter);
+ }
+
+ @AnyThread
+ private MutableLiveData<Label> createAndAssignLabelToCard(long accountId, @NonNull Label label, long localCardId, ServerAdapter serverAdapterToUse) {
MutableLiveData<Label> liveData = new MutableLiveData<>();
doAsync(() -> {
Account account = dataBaseAdapter.getAccountByIdDirectly(accountId);
Board board = dataBaseAdapter.getBoardByLocalCardIdDirectly(localCardId);
label.setAccountId(accountId);
- new DataPropagationHelper(serverAdapter, dataBaseAdapter).createEntity(new LabelDataProvider(null, board, null), label, new IResponseCallback<Label>(account) {
+ new DataPropagationHelper(serverAdapterToUse, dataBaseAdapter).createEntity(new LabelDataProvider(null, board, null), label, new IResponseCallback<Label>(account) {
@Override
public void onResponse(Label response) {
assignLabelToCard(response, dataBaseAdapter.getCardByLocalIdDirectly(accountId, localCardId));
@@ -1292,6 +1335,11 @@ public class SyncManager {
@AnyThread
public void assignLabelToCard(@NonNull Label label, @NonNull Card card) {
+ assignLabelToCard(label, card, serverAdapter);
+ }
+
+ @AnyThread
+ public void assignLabelToCard(@NonNull Label label, @NonNull Card card, ServerAdapter serverAdapterToUse) {
doAsync(() -> {
final long localLabelId = label.getLocalId();
final long localCardId = card.getLocalId();
@@ -1302,8 +1350,8 @@ public class SyncManager {
Stack stack = dataBaseAdapter.getStackByLocalIdDirectly(card.getStackId());
Board board = dataBaseAdapter.getBoardByLocalIdDirectly(stack.getBoardId());
Account account = dataBaseAdapter.getAccountByIdDirectly(card.getAccountId());
- if (serverAdapter.hasInternetConnection()) {
- serverAdapter.assignLabelToCard(board.getId(), stack.getId(), card.getId(), label.getId(), new IResponseCallback<Void>(account) {
+ if (serverAdapterToUse.hasInternetConnection()) {
+ serverAdapterToUse.assignLabelToCard(board.getId(), stack.getId(), card.getId(), label.getId(), new IResponseCallback<Void>(account) {
@Override
public void onResponse(Void response) {
@@ -1639,15 +1687,14 @@ public class SyncManager {
@AnyThread
private static Attachment populateAttachmentEntityForFile(@NonNull Attachment target, long localCardId, @NonNull String mimeType, @NonNull File file) {
- Attachment attachment = target;
- attachment.setCardId(localCardId);
- attachment.setMimetype(mimeType);
- attachment.setData(file.getName());
- attachment.setFilename(file.getName());
- attachment.setBasename(file.getName());
- attachment.setLocalPath(file.getAbsolutePath());
- attachment.setFilesize(file.length());
- return attachment;
+ target.setCardId(localCardId);
+ target.setMimetype(mimeType);
+ target.setData(file.getName());
+ target.setFilename(file.getName());
+ target.setBasename(file.getName());
+ target.setLocalPath(file.getAbsolutePath());
+ target.setFilesize(file.length());
+ return target;
}
@AnyThread
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java
index 507882ee7..30a6a6b49 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java
@@ -940,4 +940,8 @@ public class DataBaseAdapter {
public List<Account> readAccountsForHostWithReadAccessToBoardDirectly(String host, long boardRemoteId) {
return db.getAccountDao().readAccountsForHostWithReadAccessToBoardDirectly("%"+host+"%", boardRemoteId);
}
+
+ public Board getBoardForAccountByNameDirectly(long account, String title) {
+ return db.getBoardDao().getBoardForAccountByNameDirectly(account, title);
+ }
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java
index bd189e846..5b61f926e 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/BoardDao.java
@@ -52,7 +52,7 @@ public interface BoardDao extends GenericDao<Board> {
@Query("SELECT b.* FROM board b JOIN stack s ON s.boardId = b.localId JOIN card c ON c.localId = :localCardId")
Board getBoardByLocalCardIdDirectly(long localCardId);
- @Query("SELECT b.* FROM board b JOIN stack s ON s.boardId = b.localId JOIN card c ON c.localId = :localCardId")
+ @Query("SELECT b.* FROM board b JOIN stack s ON s.boardId = b.localId JOIN card c ON c.localId = :localCardId and c.stackId = s.localId")
FullBoard getFullBoardByLocalCardIdDirectly(long localCardId);
@Transaction
@@ -72,4 +72,7 @@ public interface BoardDao extends GenericDao<Board> {
@Query("SELECT count(*) FROM board WHERE accountId = :accountId and archived = 1 and (deletedAt = 0 or deletedAt is null) and status <> 3")
LiveData<Integer> countArchivedBoards(long accountId);
+
+ @Query("SELECT * FROM board WHERE accountId = :accountId and title = :title")
+ Board getBoardForAccountByNameDirectly(long accountId, String title);
} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/extrawurst/UserSearchLiveData.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/extrawurst/UserSearchLiveData.java
index 46e77c415..179d816eb 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/extrawurst/UserSearchLiveData.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/util/extrawurst/UserSearchLiveData.java
@@ -34,8 +34,6 @@ public class UserSearchLiveData extends MediatorLiveData<List<User>> implements
this.accountId = accountId;
this.searchTerm = searchTerm;
this.notYetAssignedInACL = notYetAssignedInACL;
- // TODO: remove log when stable
- DeckLog.info("###DeckUserSearch: UI triggered! term: " + searchTerm);
new Thread(() -> debouncer.call(notYetAssignedInACL)).start();
return this;
}
@@ -72,8 +70,6 @@ public class UserSearchLiveData extends MediatorLiveData<List<User>> implements
}
}
if (!term.equals(searchTerm)) {
- // TODO: remove log when stable
- DeckLog.info("###DeckUserSearch: skip posting for term " + term + ": current searchTerm is " + searchTerm);
return;
}
postCurrentFromDB(term);
@@ -93,7 +89,5 @@ public class UserSearchLiveData extends MediatorLiveData<List<User>> implements
private void postCurrentFromDB(String term) {
List<User> foundInDB = db.searchUserByUidOrDisplayNameForACLDirectly(accountId, notYetAssignedInACL, term);
postValue(foundInDB);
- // TODO: remove log when stable
- DeckLog.info("###DeckUserSearch: posting for term " + term + ": " + foundInDB);
}
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/SyncHelper.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/SyncHelper.java
index 18d8c3672..f53696a8d 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/SyncHelper.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/SyncHelper.java
@@ -37,6 +37,11 @@ public class SyncHelper {
if (response != null) {
provider.goingDeeper();
for (T entityFromServer : response) {
+ if (entityFromServer == null) {
+ // see https://github.com/stefan-niedermann/nextcloud-deck/issues/574
+ DeckLog.error("Skipped null value from server for DataProvider: " + provider.getClass().getSimpleName());
+ continue;
+ }
entityFromServer.setAccountId(accountId);
T existingEntity = provider.getSingleFromDB(dataBaseAdapter, accountId, entityFromServer);
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/LabelDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/LabelDataProvider.java
index 403d71f87..7510d9be3 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/LabelDataProvider.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/LabelDataProvider.java
@@ -3,6 +3,7 @@ package it.niedermann.nextcloud.deck.persistence.sync.helpers.providers;
import java.util.Date;
import java.util.List;
+import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.api.IResponseCallback;
import it.niedermann.nextcloud.deck.exceptions.HandledServerErrors;
import it.niedermann.nextcloud.deck.model.Board;
@@ -63,9 +64,12 @@ public class LabelDataProvider extends AbstractSyncDataProvider<Label> {
@Override
public void onError(Throwable throwable) {
if (HandledServerErrors.LABELS_TITLE_MUST_BE_UNIQUE == HandledServerErrors.fromThrowable(throwable)){
+ DeckLog.log(throwable.getCause().getMessage() + ": " + entitiy.toString());
dataBaseAdapter.deleteLabelPhysically(entitiy);
+ responder.onResponse(entitiy);
+ } else {
+ responder.onError(throwable);
}
- responder.onError(throwable);
}
};
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWitAclDownSyncDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithAclDownSyncDataProvider.java
index 326d257ab..8516f0fc0 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWitAclDownSyncDataProvider.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithAclDownSyncDataProvider.java
@@ -11,7 +11,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.helpers.SyncHelper;
import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.AccessControlDataProvider;
import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.BoardDataProvider;
-public class BoardWitAclDownSyncDataProvider extends BoardDataProvider {
+public class BoardWithAclDownSyncDataProvider extends BoardDataProvider {
@Override
public void goDeeper(SyncHelper syncHelper, FullBoard existingEntity, FullBoard entityFromServer, IResponseCallback<Boolean> callback) {
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithStacksAndLabelsUpSyncDataProvider.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithStacksAndLabelsUpSyncDataProvider.java
new file mode 100644
index 000000000..278971a9d
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/helpers/providers/partial/BoardWithStacksAndLabelsUpSyncDataProvider.java
@@ -0,0 +1,37 @@
+package it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.partial;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+
+import it.niedermann.nextcloud.deck.api.IResponseCallback;
+import it.niedermann.nextcloud.deck.model.full.FullBoard;
+import it.niedermann.nextcloud.deck.persistence.sync.adapters.ServerAdapter;
+import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.DataBaseAdapter;
+import it.niedermann.nextcloud.deck.persistence.sync.helpers.SyncHelper;
+import it.niedermann.nextcloud.deck.persistence.sync.helpers.providers.BoardDataProvider;
+
+public class BoardWithStacksAndLabelsUpSyncDataProvider extends BoardDataProvider {
+
+ private FullBoard board;
+
+ public BoardWithStacksAndLabelsUpSyncDataProvider(FullBoard boardToSync) {
+ board = boardToSync;
+ }
+
+ @Override
+ public List<FullBoard> getAllChangedFromDB(DataBaseAdapter dataBaseAdapter, long accountId, Date lastSync) {
+ return Collections.singletonList(board);
+ }
+
+ @Override
+ public void goDeeper(SyncHelper syncHelper, FullBoard existingEntity, FullBoard entityFromServer, IResponseCallback<Boolean> callback) {
+ // do nothing!
+
+ }
+
+ @Override
+ public void handleDeletes(ServerAdapter serverAdapter, DataBaseAdapter dataBaseAdapter, long accountId, List<FullBoard> entitiesFromServer) {
+ // do nothing!
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java
index b5713909b..ce4c279a0 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/MainActivity.java
@@ -508,7 +508,12 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener
@Override
public void onUpdateBoard(FullBoard fullBoard) {
- syncManager.updateBoard(fullBoard);
+ final WrappedLiveData<FullBoard> updateLiveData = syncManager.updateBoard(fullBoard);
+ observeOnce(updateLiveData, this, (next) -> {
+ if (updateLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(updateLiveData.getError(), mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
}
private void refreshCapabilities(final Account account) {
@@ -946,7 +951,14 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener
EditBoardDialogFragment.newInstance().show(getSupportFragmentManager(), addBoard);
}
}
- syncManager.deleteBoard(board);
+
+ final WrappedLiveData<Void> deleteLiveData = syncManager.deleteBoard(board);
+ observeOnce(deleteLiveData, this, (next) -> {
+ if (deleteLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(deleteLiveData.getError(), mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
+
binding.drawerLayout.closeDrawer(GravityCompat.START);
}
@@ -968,4 +980,23 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener
public void onArchive(@NonNull Board board) {
syncManager.archiveBoard(board);
}
+
+ @Override
+ public void onClone(Board board) {
+ binding.drawerLayout.closeDrawer(GravityCompat.START);
+ final Snackbar snackbar = BrandedSnackbar.make(binding.coordinatorLayout, getString(R.string.cloning_board, board.getTitle()), Snackbar.LENGTH_INDEFINITE);
+ snackbar.show();
+ final WrappedLiveData<FullBoard> liveData = syncManager.cloneBoard(board.getAccountId(), board.getLocalId(), board.getAccountId(), board.getColor());
+ observeOnce(liveData, this, (fullBoard -> {
+ snackbar.dismiss();
+ if (liveData.hasError()) {
+ ExceptionDialogFragment.newInstance(liveData.getError(), mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ } else {
+ setCurrentBoard(fullBoard.getBoard());
+ BrandedSnackbar.make(binding.coordinatorLayout, getString(R.string.successfully_cloned_board, fullBoard.getBoard().getTitle()), Snackbar.LENGTH_LONG)
+ .setAction(R.string.edit, v -> EditBoardDialogFragment.newInstance(fullBoard.getLocalId()).show(getSupportFragmentManager(), EditBoardDialogFragment.class.getSimpleName()))
+ .show();
+ }
+ }));
+ }
} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java
index 7c3a2d23c..c159ff98e 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedboards/ArchivedBoardsActvitiy.java
@@ -15,13 +15,17 @@ import it.niedermann.nextcloud.deck.model.Account;
import it.niedermann.nextcloud.deck.model.Board;
import it.niedermann.nextcloud.deck.model.full.FullBoard;
import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData;
import it.niedermann.nextcloud.deck.ui.MainViewModel;
import it.niedermann.nextcloud.deck.ui.board.ArchiveBoardListener;
import it.niedermann.nextcloud.deck.ui.board.DeleteBoardListener;
import it.niedermann.nextcloud.deck.ui.board.EditBoardListener;
import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity;
+import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler;
+import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce;
+
public class ArchivedBoardsActvitiy extends BrandedActivity implements DeleteBoardListener, EditBoardListener, ArchiveBoardListener {
private static final String BUNDLE_KEY_ACCOUNT = "accountId";
@@ -80,16 +84,31 @@ public class ArchivedBoardsActvitiy extends BrandedActivity implements DeleteBoa
@Override
public void onBoardDeleted(Board board) {
- syncManager.deleteBoard(board);
+ final WrappedLiveData<Void> deleteLiveData = syncManager.deleteBoard(board);
+ observeOnce(deleteLiveData, this, (next) -> {
+ if (deleteLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(deleteLiveData.getError(), viewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
}
@Override
public void onUpdateBoard(FullBoard fullBoard) {
- syncManager.updateBoard(fullBoard);
+ final WrappedLiveData<FullBoard> updateLiveData = syncManager.updateBoard(fullBoard);
+ observeOnce(updateLiveData, this, (next) -> {
+ if (updateLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(updateLiveData.getError(), viewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
}
@Override
public void onArchive(Board board) {
syncManager.dearchiveBoard(board);
}
+
+ @Override
+ public void onClone(Board board) {
+
+ }
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java
index bc2891360..a5a065cd1 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsAdapter.java
@@ -12,8 +12,8 @@ import it.niedermann.nextcloud.deck.model.Account;
import it.niedermann.nextcloud.deck.model.full.FullCard;
import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData;
+import it.niedermann.nextcloud.deck.ui.card.AbstractCardViewHolder;
import it.niedermann.nextcloud.deck.ui.card.CardAdapter;
-import it.niedermann.nextcloud.deck.ui.card.CardViewHolder;
import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce;
@@ -26,7 +26,7 @@ public class ArchivedCardsAdapter extends CardAdapter {
}
@Override
- public void onBindViewHolder(@NonNull CardViewHolder viewHolder, int position) {
+ public void onBindViewHolder(@NonNull AbstractCardViewHolder viewHolder, int position) {
viewHolder.bind(cardList.get(position), account, boardRemoteId, hasEditPermission, R.menu.archived_card_menu, this, counterMaxValue, mainColor);
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/ArchiveBoardListener.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/ArchiveBoardListener.java
index b7e27aa97..ff0d3e941 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/ArchiveBoardListener.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/ArchiveBoardListener.java
@@ -4,4 +4,5 @@ import it.niedermann.nextcloud.deck.model.Board;
public interface ArchiveBoardListener {
void onArchive(Board board);
+ void onClone(Board board);
} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java
index 33c0fe5b1..9c1d63195 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/board/accesscontrol/AccessControlDialogFragment.java
@@ -103,7 +103,12 @@ public class AccessControlDialogFragment extends BrandedDialogFragment implement
@Override
public void updateAccessControl(AccessControl accessControl) {
- syncManager.updateAccessControl(accessControl);
+ WrappedLiveData<AccessControl> updateLiveData = syncManager.updateAccessControl(accessControl);
+ observeOnce(updateLiveData, requireActivity(), (next) -> {
+ if (updateLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(updateLiveData.getError(), viewModel.getCurrentAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
}
@Override
@@ -129,7 +134,12 @@ public class AccessControlDialogFragment extends BrandedDialogFragment implement
ac.setType(0L); // https://github.com/nextcloud/deck/blob/master/docs/API.md#post-boardsboardidacl---add-new-acl-rule
ac.setUserId(user.getLocalId());
ac.setUser(user);
- syncManager.createAccessControl(viewModel.getCurrentAccount().getId(), ac);
+ final WrappedLiveData<AccessControl> createLiveData = syncManager.createAccessControl(viewModel.getCurrentAccount().getId(), ac);
+ observeOnce(createLiveData, this, (next) -> {
+ if (createLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(createLiveData.getError(), viewModel.getCurrentAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
binding.people.setText("");
userAutoCompleteAdapter.exclude(user);
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java
index 20e6f8dc8..8f7219b11 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/branding/BrandedSnackbar.java
@@ -27,7 +27,7 @@ public class BrandedSnackbar {
@ColorInt final int color = readBrandMainColor(view.getContext());
snackbar.setActionTextColor(ColorUtil.isColorDark(color) ? Color.WHITE : color);
} else {
- snackbar.setActionTextColor(ContextCompat.getColor(view.getContext(), R.color.primary));
+ snackbar.setActionTextColor(ContextCompat.getColor(view.getContext(), R.color.defaultBrand));
}
return snackbar;
}
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
new file mode 100644
index 000000000..984f7099f
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/AbstractCardViewHolder.java
@@ -0,0 +1,121 @@
+package it.niedermann.nextcloud.deck.ui.card;
+
+import android.content.Context;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.ColorInt;
+import androidx.annotation.MenuRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.widget.PopupMenu;
+import androidx.core.graphics.drawable.DrawableCompat;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.google.android.material.card.MaterialCardView;
+
+import org.jetbrains.annotations.Contract;
+
+import java.util.List;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.model.Account;
+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.util.DateUtil;
+import it.niedermann.nextcloud.deck.util.ViewUtil;
+
+public abstract class AbstractCardViewHolder extends RecyclerView.ViewHolder {
+
+ public AbstractCardViewHolder(@NonNull View itemView) {
+ super(itemView);
+ }
+
+ /**
+ * Removes all {@link OnClickListener} and {@link OnLongClickListener}
+ */
+ @CallSuper
+ public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @ColorInt int mainColor) {
+ final Context context = itemView.getContext();
+
+ bindCardClickListener(null);
+ bindCardLongClickListener(null);
+
+ getCardMenu().setVisibility(hasEditPermission ? View.VISIBLE : View.GONE);
+ getCardTitle().setText(fullCard.getCard().getTitle().trim());
+
+ DrawableCompat.setTint(getNotSyncedYet().getDrawable(), mainColor);
+ getNotSyncedYet().setVisibility(DBStatus.LOCAL_EDITED.equals(fullCard.getStatusEnum()) ? View.VISIBLE : View.GONE);
+
+ if (fullCard.getCard().getDueDate() != null) {
+ setupDueDate(getCardDueDate(), fullCard.getCard());
+ getCardDueDate().setVisibility(View.VISIBLE);
+ } else {
+ getCardDueDate().setVisibility(View.GONE);
+ }
+
+ getCardMenu().setOnClickListener(view -> {
+ final PopupMenu popup = new PopupMenu(context, view);
+ popup.inflate(optionsMenu);
+ final Menu menu = popup.getMenu();
+ if (containsUser(fullCard.getAssignedUsers(), account.getUserName())) {
+ menu.removeItem(menu.findItem(R.id.action_card_assign).getItemId());
+ } else {
+ menu.removeItem(menu.findItem(R.id.action_card_unassign).getItemId());
+ }
+ if (boardRemoteId == null || fullCard.getCard().getId() == null) {
+ menu.removeItem(R.id.share_link);
+ }
+
+ popup.setOnMenuItemClickListener(item -> optionsItemsSelectedListener.onCardOptionsItemSelected(item, fullCard));
+ popup.show();
+ });
+ }
+
+ protected abstract TextView getCardDueDate();
+
+ protected abstract ImageView getNotSyncedYet();
+
+ protected abstract TextView getCardTitle();
+
+ protected abstract View getCardMenu();
+
+ protected abstract MaterialCardView getCard();
+
+ public void bindCardClickListener(@Nullable OnClickListener l) {
+ getCard().setOnClickListener(l);
+ }
+
+ public void bindCardLongClickListener(@Nullable OnLongClickListener l) {
+ getCard().setOnLongClickListener(l);
+ }
+
+ public MaterialCardView getDraggable() {
+ return getCard();
+ }
+
+ private static void setupDueDate(@NonNull TextView cardDueDate, @NonNull Card card) {
+ final Context context = cardDueDate.getContext();
+ cardDueDate.setText(DateUtil.getRelativeDateTimeString(context, card.getDueDate().getTime()));
+ ViewUtil.themeDueDate(context, cardDueDate, card.getDueDate());
+ }
+
+ @Contract("null, _ -> false")
+ private static boolean containsUser(List<User> userList, String username) {
+ if (userList != null) {
+ for (User user : userList) {
+ if (user.getPrimaryKey().equals(username)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java
index 5910850fa..27a6518ee 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardAdapter.java
@@ -1,6 +1,5 @@
package it.niedermann.nextcloud.deck.ui.card;
-import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
@@ -9,42 +8,48 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
+import androidx.core.content.ContextCompat;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.LifecycleOwner;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
import it.niedermann.android.crosstabdnd.DragAndDropAdapter;
import it.niedermann.android.crosstabdnd.DraggedItemLocalState;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.R;
-import it.niedermann.nextcloud.deck.databinding.ItemCardBinding;
+import it.niedermann.nextcloud.deck.databinding.ItemCardCompactBinding;
+import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultBinding;
+import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultOnlyTitleBinding;
import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.model.Card;
import it.niedermann.nextcloud.deck.model.Stack;
import it.niedermann.nextcloud.deck.model.full.FullCard;
-import it.niedermann.nextcloud.deck.model.full.FullStack;
import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
-import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper;
import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData;
import it.niedermann.nextcloud.deck.ui.branding.Branded;
-import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder;
import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
+import it.niedermann.nextcloud.deck.ui.movecard.MoveCardDialogFragment;
+import static androidx.preference.PreferenceManager.getDefaultSharedPreferences;
import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce;
import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.getSecondaryForegroundColorDependingOnTheme;
import static it.niedermann.nextcloud.deck.util.MimeTypeUtil.TEXT_PLAIN;
-public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements DragAndDropAdapter<FullCard>, CardOptionsItemSelectedListener, Branded {
+public class CardAdapter extends RecyclerView.Adapter<AbstractCardViewHolder> implements DragAndDropAdapter<FullCard>, CardOptionsItemSelectedListener, Branded {
+ private final boolean compactMode;
+ @NonNull
protected final SyncManager syncManager;
-
+ @NonNull
protected final FragmentManager fragmentManager;
+ @NonNull
protected final Account account;
@Nullable
protected final Long boardRemoteId;
@@ -55,12 +60,13 @@ public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements
private final Context context;
@Nullable
private final SelectCardListener selectCardListener;
- protected List<FullCard> cardList = new LinkedList<>();
+ @NonNull
+ protected List<FullCard> cardList = new ArrayList<>();
+ @NonNull
protected LifecycleOwner lifecycleOwner;
@NonNull
- final private List<FullStack> availableStacks = new ArrayList<>();
protected String counterMaxValue;
-
+ @ColorInt
protected int mainColor;
@StringRes
private int shareLinkRes;
@@ -78,11 +84,8 @@ public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements
this.hasEditPermission = hasEditPermission;
this.syncManager = syncManager;
this.selectCardListener = selectCardListener;
- this.mainColor = context.getResources().getColor(R.color.defaultBrand);
- syncManager.getStacksForBoard(account.getId(), boardLocalId).observe(this.lifecycleOwner, (stacks) -> {
- availableStacks.clear();
- availableStacks.addAll(stacks);
- });
+ this.mainColor = ContextCompat.getColor(context, R.color.defaultBrand);
+ this.compactMode = getDefaultSharedPreferences(context).getBoolean(context.getString(R.string.pref_key_compact), false);
setHasStableIds(true);
}
@@ -93,13 +96,35 @@ public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements
@NonNull
@Override
- public CardViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int position) {
- return new CardViewHolder(ItemCardBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false));
+ public AbstractCardViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
+ switch (viewType) {
+ case R.layout.item_card_compact:
+ return new CompactCardViewHolder(ItemCardCompactBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false));
+ case R.layout.item_card_default_only_title:
+ return new DefaultCardOnlyTitleViewHolder(ItemCardDefaultOnlyTitleBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false));
+ case R.layout.item_card_default:
+ default:
+ return new DefaultCardViewHolder(ItemCardDefaultBinding.inflate(LayoutInflater.from(viewGroup.getContext()), viewGroup, false));
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (compactMode) {
+ return R.layout.item_card_compact;
+ } else {
+ final FullCard fullCard = cardList.get(position);
+ if (fullCard.getAttachments().size() == 0
+ && fullCard.getAssignedUsers().size() == 0
+ && fullCard.getCommentCount() == 0) {
+ return R.layout.item_card_default_only_title;
+ }
+ return R.layout.item_card_default;
+ }
}
- @SuppressLint("SetTextI18n")
@Override
- public void onBindViewHolder(@NonNull CardViewHolder viewHolder, int position) {
+ public void onBindViewHolder(@NonNull AbstractCardViewHolder viewHolder, int position) {
@NonNull FullCard fullCard = cardList.get(position);
viewHolder.bind(fullCard, account, boardRemoteId, hasEditPermission, R.menu.card_menu, this, counterMaxValue, mainColor);
@@ -128,7 +153,7 @@ public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements
@Override
public int getItemCount() {
- return cardList == null ? 0 : cardList.size();
+ return cardList.size();
}
public void insertItem(FullCard fullCard, int position) {
@@ -136,6 +161,7 @@ public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements
notifyItemInserted(position);
}
+ @NonNull
@Override
public List<FullCard> getItemList() {
return this.cardList;
@@ -186,28 +212,8 @@ public class CardAdapter extends RecyclerView.Adapter<CardViewHolder> implements
return true;
}
case R.id.action_card_move: {
- int currentStackItem = 0;
- CharSequence[] items = new CharSequence[availableStacks.size()];
- for (int i = 0; i < availableStacks.size(); i++) {
- final Stack stack = availableStacks.get(i).getStack();
- items[i] = stack.getTitle();
- if (stack.getLocalId().equals(stackId)) {
- currentStackItem = i;
- }
- }
- final FullCard newCard = fullCard;
- new BrandedAlertDialogBuilder(context)
- .setSingleChoiceItems(items, currentStackItem, (dialog, which) -> {
- dialog.cancel();
- newCard.getCard().setStackId(availableStacks.get(which).getStack().getLocalId());
- LiveDataHelper.observeOnce(syncManager.updateCard(newCard), lifecycleOwner, (c) -> {
- // Nothing to do here...
- });
- DeckLog.log("Moved card \"" + fullCard.getCard().getTitle() + "\" to \"" + availableStacks.get(which).getStack().getTitle() + "\"");
- })
- .setNeutralButton(android.R.string.cancel, null)
- .setTitle(context.getString(R.string.action_card_move_title, fullCard.getCard().getTitle()))
- .show();
+ DeckLog.verbose("[Move card] Launch move dialog for " + Card.class.getSimpleName() + " \"" + fullCard.getCard().getTitle() + "\" (#" + fullCard.getLocalId() + ") from " + Stack.class.getSimpleName() + " #" + +stackId);
+ MoveCardDialogFragment.newInstance(fullCard.getAccountId(), boardLocalId, fullCard.getLocalId()).show(fragmentManager, MoveCardDialogFragment.class.getSimpleName());
return true;
}
case R.id.action_card_archive: {
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
new file mode 100644
index 000000000..e9f366d99
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CompactCardViewHolder.java
@@ -0,0 +1,84 @@
+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;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.MenuRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.material.card.MaterialCardView;
+
+import java.util.List;
+
+import it.niedermann.nextcloud.deck.databinding.ItemCardCompactBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.model.Label;
+import it.niedermann.nextcloud.deck.model.full.FullCard;
+
+public class CompactCardViewHolder extends AbstractCardViewHolder {
+ private ItemCardCompactBinding binding;
+
+ @SuppressWarnings("WeakerAccess")
+ public CompactCardViewHolder(@NonNull ItemCardCompactBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ /**
+ * Removes all {@link OnClickListener} and {@link OnLongClickListener}
+ */
+ public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @ColorInt int mainColor) {
+ super.bind(fullCard, account, boardRemoteId, hasEditPermission, optionsMenu, optionsItemsSelectedListener, counterMaxValue, mainColor);
+
+ List<Label> labels = fullCard.getLabels();
+ if (labels != null && labels.size() > 0) {
+ binding.labels.updateLabels(labels);
+ binding.labels.setVisibility(View.VISIBLE);
+ } else {
+ binding.labels.removeAllViews();
+ binding.labels.setVisibility(View.GONE);
+ }
+ }
+
+ public void bindCardClickListener(@Nullable OnClickListener l) {
+ binding.card.setOnClickListener(l);
+ }
+
+ public void bindCardLongClickListener(@Nullable OnLongClickListener l) {
+ binding.card.setOnLongClickListener(l);
+ }
+
+ public MaterialCardView getDraggable() {
+ return binding.card;
+ }
+
+ @Override
+ protected TextView getCardDueDate() {
+ return binding.cardDueDate;
+ }
+
+ @Override
+ protected ImageView getNotSyncedYet() {
+ return binding.notSyncedYet;
+ }
+
+ @Override
+ protected TextView getCardTitle() {
+ return binding.cardTitle;
+ }
+
+ @Override
+ protected View getCardMenu() {
+ return binding.cardMenu;
+ }
+
+ @Override
+ protected MaterialCardView getCard() {
+ return binding.card;
+ }
+} \ No newline at end of file
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
new file mode 100644
index 000000000..2f9e132c9
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardOnlyTitleViewHolder.java
@@ -0,0 +1,61 @@
+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;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.google.android.material.card.MaterialCardView;
+
+import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultOnlyTitleBinding;
+
+public class DefaultCardOnlyTitleViewHolder extends AbstractCardViewHolder {
+ private ItemCardDefaultOnlyTitleBinding binding;
+
+ @SuppressWarnings("WeakerAccess")
+ public DefaultCardOnlyTitleViewHolder(@NonNull ItemCardDefaultOnlyTitleBinding binding) {
+ super(binding.getRoot());
+ this.binding = binding;
+ }
+
+ public void bindCardClickListener(@Nullable OnClickListener l) {
+ binding.card.setOnClickListener(l);
+ }
+
+ public void bindCardLongClickListener(@Nullable OnLongClickListener l) {
+ binding.card.setOnLongClickListener(l);
+ }
+
+ public MaterialCardView getDraggable() {
+ return binding.card;
+ }
+
+ @Override
+ protected TextView getCardDueDate() {
+ return binding.cardDueDate;
+ }
+
+ @Override
+ protected ImageView getNotSyncedYet() {
+ return binding.notSyncedYet;
+ }
+
+ @Override
+ protected TextView getCardTitle() {
+ return binding.cardTitle;
+ }
+
+ @Override
+ protected View getCardMenu() {
+ return binding.cardMenu;
+ }
+
+ @Override
+ protected MaterialCardView getCard() {
+ return binding.card;
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardViewHolder.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java
index 279d38360..5e82061d1 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/CardViewHolder.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/DefaultCardViewHolder.java
@@ -1,19 +1,16 @@
package it.niedermann.nextcloud.deck.ui.card;
import android.content.Context;
-import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
+import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.MenuRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import androidx.appcompat.widget.PopupMenu;
-import androidx.core.graphics.drawable.DrawableCompat;
-import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.card.MaterialCardView;
@@ -22,21 +19,18 @@ import org.jetbrains.annotations.Contract;
import java.util.List;
import it.niedermann.nextcloud.deck.R;
-import it.niedermann.nextcloud.deck.databinding.ItemCardBinding;
+import it.niedermann.nextcloud.deck.databinding.ItemCardDefaultBinding;
import it.niedermann.nextcloud.deck.model.Account;
import it.niedermann.nextcloud.deck.model.Card;
import it.niedermann.nextcloud.deck.model.Label;
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.util.DateUtil;
-import it.niedermann.nextcloud.deck.util.ViewUtil;
-public class CardViewHolder extends RecyclerView.ViewHolder {
- private ItemCardBinding binding;
+public class DefaultCardViewHolder extends AbstractCardViewHolder {
+ private ItemCardDefaultBinding binding;
@SuppressWarnings("WeakerAccess")
- public CardViewHolder(@NonNull ItemCardBinding binding) {
+ public DefaultCardViewHolder(@NonNull ItemCardDefaultBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
@@ -45,12 +39,9 @@ public class CardViewHolder extends RecyclerView.ViewHolder {
* Removes all {@link OnClickListener} and {@link OnLongClickListener}
*/
public void bind(@NonNull FullCard fullCard, @NonNull Account account, @Nullable Long boardRemoteId, boolean hasEditPermission, @MenuRes int optionsMenu, @NonNull CardOptionsItemSelectedListener optionsItemsSelectedListener, @NonNull String counterMaxValue, @ColorInt int mainColor) {
- final Context context = itemView.getContext();
+ super.bind(fullCard, account, boardRemoteId, hasEditPermission, optionsMenu, optionsItemsSelectedListener, counterMaxValue, mainColor);
- bindCardClickListener(null);
- bindCardLongClickListener(null);
- binding.cardMenu.setVisibility(hasEditPermission ? View.VISIBLE : View.GONE);
- binding.cardTitle.setText(fullCard.getCard().getTitle().trim());
+ final Context context = itemView.getContext();
if (fullCard.getAssignedUsers() != null && fullCard.getAssignedUsers().size() > 0) {
binding.overlappingAvatars.setAvatars(account, fullCard.getAssignedUsers());
@@ -59,16 +50,6 @@ public class CardViewHolder extends RecyclerView.ViewHolder {
binding.overlappingAvatars.setVisibility(View.GONE);
}
- DrawableCompat.setTint(binding.notSyncedYet.getDrawable(), mainColor);
- binding.notSyncedYet.setVisibility(DBStatus.LOCAL_EDITED.equals(fullCard.getStatusEnum()) ? View.VISIBLE : View.GONE);
-
- if (fullCard.getCard().getDueDate() != null) {
- setupDueDate(binding.cardDueDate, fullCard.getCard());
- binding.cardDueDate.setVisibility(View.VISIBLE);
- } else {
- binding.cardDueDate.setVisibility(View.GONE);
- }
-
final int attachmentsCount = fullCard.getAttachments().size();
if (attachmentsCount == 0) {
@@ -104,23 +85,31 @@ public class CardViewHolder extends RecyclerView.ViewHolder {
} else {
binding.cardCountTasks.setVisibility(View.GONE);
}
+ }
- binding.cardMenu.setOnClickListener(view -> {
- final PopupMenu popup = new PopupMenu(context, view);
- popup.inflate(optionsMenu);
- final Menu menu = popup.getMenu();
- if (containsUser(fullCard.getAssignedUsers(), account.getUserName())) {
- menu.removeItem(menu.findItem(R.id.action_card_assign).getItemId());
- } else {
- menu.removeItem(menu.findItem(R.id.action_card_unassign).getItemId());
- }
- if (boardRemoteId == null || fullCard.getCard().getId() == null) {
- menu.removeItem(R.id.share_link);
- }
+ @Override
+ protected TextView getCardDueDate() {
+ return binding.cardDueDate;
+ }
+
+ @Override
+ protected ImageView getNotSyncedYet() {
+ return binding.notSyncedYet;
+ }
+
+ @Override
+ protected TextView getCardTitle() {
+ return binding.cardTitle;
+ }
- popup.setOnMenuItemClickListener(item -> optionsItemsSelectedListener.onCardOptionsItemSelected(item, fullCard));
- popup.show();
- });
+ @Override
+ protected View getCardMenu() {
+ return binding.cardMenu;
+ }
+
+ @Override
+ protected MaterialCardView getCard() {
+ return binding.card;
}
public void bindCardClickListener(@Nullable OnClickListener l) {
@@ -135,11 +124,6 @@ public class CardViewHolder extends RecyclerView.ViewHolder {
return binding.card;
}
- private static void setupDueDate(@NonNull TextView cardDueDate, @NonNull Card card) {
- final Context context = cardDueDate.getContext();
- cardDueDate.setText(DateUtil.getRelativeDateTimeString(context, card.getDueDate().getTime()));
- ViewUtil.themeDueDate(context, cardDueDate, card.getDueDate());
- }
private static void setupCounter(@NonNull TextView textView, @NonNull String counterMaxValue, int count) {
if (count > 99) {
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentDeletedListener.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentDeletedListener.java
index c236fa4c5..2d3ece255 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentDeletedListener.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/attachments/AttachmentDeletedListener.java
@@ -3,5 +3,5 @@ package it.niedermann.nextcloud.deck.ui.card.attachments;
import it.niedermann.nextcloud.deck.model.Attachment;
public interface AttachmentDeletedListener {
- void onAttachmentDeleted(Attachment attachment);
- } \ No newline at end of file
+ void onAttachmentDeleted(Attachment attachment);
+} \ No newline at end of file
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 c291c5bdf..23f6377a5 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
@@ -1,7 +1,6 @@
package it.niedermann.nextcloud.deck.ui.card.attachments;
import android.Manifest;
-import android.app.Activity;
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
@@ -16,6 +15,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.app.SharedElementCallback;
+import androidx.core.content.PermissionChecker;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.GridLayoutManager;
@@ -43,6 +43,8 @@ import it.niedermann.nextcloud.deck.ui.branding.BrandedSnackbar;
import it.niedermann.nextcloud.deck.ui.card.EditCardViewModel;
import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
+import static android.app.Activity.RESULT_OK;
+import static androidx.core.content.PermissionChecker.checkSelfPermission;
import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce;
import static it.niedermann.nextcloud.deck.ui.branding.BrandingUtil.applyBrandToFAB;
import static it.niedermann.nextcloud.deck.ui.card.attachments.CardAttachmentAdapter.VIEW_TYPE_DEFAULT;
@@ -55,8 +57,8 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
private FragmentCardEditTabAttachmentsBinding binding;
private EditCardViewModel viewModel;
- private static final int REQUEST_CODE_ADD_ATTACHMENT = 1;
- private static final int REQUEST_PERMISSION = 2;
+ private static final int REQUEST_CODE_ADD_FILE = 1;
+ private static final int REQUEST_CODE_ADD_FILE_PERMISSION = 2;
private SyncManager syncManager;
private CardAttachmentAdapter adapter;
@@ -123,14 +125,11 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
updateEmptyContentView();
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && viewModel.canEdit()) {
+ if (viewModel.canEdit()) {
binding.fab.setOnClickListener(v -> {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
- REQUEST_PERMISSION);
- } else {
- startFilePickerIntent();
- }
+ startActivityForResult(new Intent(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("*/*"), REQUEST_CODE_ADD_FILE);
});
binding.fab.show();
binding.attachmentsList.addOnScrollListener(new RecyclerView.OnScrollListener() {
@@ -150,110 +149,124 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
}
@RequiresApi(api = Build.VERSION_CODES.KITKAT)
- private void startFilePickerIntent() {
- Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
- intent.addCategory(Intent.CATEGORY_OPENABLE);
- intent.setType("*/*");
- startActivityForResult(intent, REQUEST_CODE_ADD_ATTACHMENT);
+ public void pickFile() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(requireActivity(), Manifest.permission.READ_CONTACTS) != PermissionChecker.PERMISSION_GRANTED) {
+ requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ REQUEST_CODE_ADD_FILE_PERMISSION);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("*/*");
+ startActivityForResult(intent, REQUEST_CODE_ADD_FILE);
+ }
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (requestCode == REQUEST_CODE_ADD_ATTACHMENT && resultCode == Activity.RESULT_OK) {
- if (data == null) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Intent data is null"), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
- }
- final Uri sourceUri = data.getData();
- if (sourceUri == null) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("sourceUri is null"), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
- }
- if (!ContentResolver.SCHEME_CONTENT.equals(sourceUri.getScheme())) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme: " + sourceUri.getScheme()), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
- }
-
- DeckLog.verbose("--- found content URL " + sourceUri.getPath());
- File fileToUpload;
+ switch (requestCode) {
+ case REQUEST_CODE_ADD_FILE: {
+ if (resultCode == RESULT_OK) {
+ if (data == null) {
+ ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Intent data is null"), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ return;
+ }
+ final Uri sourceUri = data.getData();
+ if (sourceUri == null) {
+ ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("sourceUri is null"), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ return;
+ }
+ if (!ContentResolver.SCHEME_CONTENT.equals(sourceUri.getScheme())) {
+ ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme: " + sourceUri.getScheme()), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ return;
+ }
- try {
- DeckLog.verbose("---- so, now copy & upload: " + sourceUri.getPath());
- fileToUpload = copyContentUriToTempFile(requireContext(), sourceUri, viewModel.getAccount().getId(), viewModel.getFullCard().getCard().getLocalId());
- } catch (IllegalArgumentException | IOException e) {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Could not copy content URI to temporary file", e), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
- return;
- }
+ DeckLog.verbose("--- found content URL " + sourceUri.getPath());
+ File fileToUpload;
- for (Attachment existingAttachment : viewModel.getFullCard().getAttachments()) {
- final String existingPath = existingAttachment.getLocalPath();
- if (existingPath != null && existingPath.equals(fileToUpload.getAbsolutePath())) {
- BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
- return;
- }
- }
+ try {
+ DeckLog.verbose("---- so, now copy & upload: " + sourceUri.getPath());
+ fileToUpload = copyContentUriToTempFile(requireContext(), sourceUri, viewModel.getAccount().getId(), viewModel.getFullCard().getCard().getLocalId());
+ } catch (IllegalArgumentException | IOException e) {
+ ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Could not copy content URI to temporary file", e), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ return;
+ }
- final Date now = new Date();
- final Attachment a = new Attachment();
- a.setMimetype(requireContext().getContentResolver().getType(sourceUri));
- a.setData(fileToUpload.getName());
- a.setFilename(fileToUpload.getName());
- a.setBasename(fileToUpload.getName());
- a.setFilesize(fileToUpload.length());
- a.setLocalPath(fileToUpload.getAbsolutePath());
- a.setLastModifiedLocal(now);
- a.setStatusEnum(DBStatus.LOCAL_EDITED);
- a.setCreatedAt(now);
- viewModel.getFullCard().getAttachments().add(a);
- adapter.addAttachment(a);
- if (!viewModel.isCreateMode()) {
- WrappedLiveData<Attachment> liveData = syncManager.addAttachmentToCard(viewModel.getAccount().getId(), viewModel.getFullCard().getLocalId(), a.getMimetype(), fileToUpload);
- observeOnce(liveData, getViewLifecycleOwner(), (next) -> {
- if (liveData.hasError()) {
- Throwable t = liveData.getError();
- if (t instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) t).getStatusCode() == HTTP_CONFLICT) {
- // https://github.com/stefan-niedermann/nextcloud-deck/issues/534
- viewModel.getFullCard().getAttachments().remove(a);
- adapter.removeAttachment(a);
+ for (Attachment existingAttachment : viewModel.getFullCard().getAttachments()) {
+ final String existingPath = existingAttachment.getLocalPath();
+ if (existingPath != null && existingPath.equals(fileToUpload.getAbsolutePath())) {
BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
- } else {
- ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme", t), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ return;
}
- } else {
- viewModel.getFullCard().getAttachments().remove(a);
- adapter.removeAttachment(a);
- viewModel.getFullCard().getAttachments().add(next);
- adapter.addAttachment(next);
}
- });
+
+ final Date now = new Date();
+ final Attachment a = new Attachment();
+ a.setMimetype(requireContext().getContentResolver().getType(sourceUri));
+ a.setData(fileToUpload.getName());
+ a.setFilename(fileToUpload.getName());
+ a.setBasename(fileToUpload.getName());
+ a.setFilesize(fileToUpload.length());
+ a.setLocalPath(fileToUpload.getAbsolutePath());
+ a.setLastModifiedLocal(now);
+ a.setStatusEnum(DBStatus.LOCAL_EDITED);
+ a.setCreatedAt(now);
+ viewModel.getFullCard().getAttachments().add(a);
+ adapter.addAttachment(a);
+ if (!viewModel.isCreateMode()) {
+ WrappedLiveData<Attachment> liveData = syncManager.addAttachmentToCard(viewModel.getAccount().getId(), viewModel.getFullCard().getLocalId(), a.getMimetype(), fileToUpload);
+ observeOnce(liveData, getViewLifecycleOwner(), (next) -> {
+ if (liveData.hasError()) {
+ Throwable t = liveData.getError();
+ if (t instanceof NextcloudHttpRequestFailedException && ((NextcloudHttpRequestFailedException) t).getStatusCode() == HTTP_CONFLICT) {
+ // https://github.com/stefan-niedermann/nextcloud-deck/issues/534
+ viewModel.getFullCard().getAttachments().remove(a);
+ adapter.removeAttachment(a);
+ BrandedSnackbar.make(binding.coordinatorLayout, R.string.attachment_already_exists, Snackbar.LENGTH_LONG).show();
+ } else {
+ ExceptionDialogFragment.newInstance(new UploadAttachmentFailedException("Unknown URI scheme", t), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ } else {
+ viewModel.getFullCard().getAttachments().remove(a);
+ adapter.removeAttachment(a);
+ viewModel.getFullCard().getAttachments().add(next);
+ adapter.addAttachment(next);
+ }
+ });
+ }
+ updateEmptyContentView();
+ }
+ break;
+ }
+ default: {
+ super.onActivityResult(requestCode, resultCode, data);
}
- updateEmptyContentView();
}
-
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
- if (requestCode == REQUEST_PERMISSION) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- startFilePickerIntent();
- }
- } else {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+ switch (requestCode) {
+ case REQUEST_CODE_ADD_FILE_PERMISSION:
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ pickFile();
+ }
+ break;
+ default:
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
- public static Fragment newInstance() {
- return new CardAttachmentsFragment();
- }
-
@Override
public void onAttachmentDeleted(Attachment attachment) {
adapter.removeAttachment(attachment);
viewModel.getFullCard().getAttachments().remove(attachment);
if (!viewModel.isCreateMode() && attachment.getLocalId() != null) {
- syncManager.deleteAttachmentOfCard(viewModel.getAccount().getId(), viewModel.getFullCard().getLocalId(), attachment.getLocalId());
+ final WrappedLiveData<Void> deleteLiveData = syncManager.deleteAttachmentOfCard(viewModel.getAccount().getId(), viewModel.getFullCard().getLocalId(), attachment.getLocalId());
+ observeOnce(deleteLiveData, this, (next) -> {
+ if (deleteLiveData.hasError()) {
+ ExceptionDialogFragment.newInstance(deleteLiveData.getError(), viewModel.getAccount()).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ }
+ });
}
updateEmptyContentView();
}
@@ -263,7 +276,6 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
this.clickedItemPosition = position;
}
-
private void updateEmptyContentView() {
if (this.adapter == null || this.adapter.getItemCount() == 0) {
this.binding.emptyContentView.setVisibility(View.VISIBLE);
@@ -278,4 +290,8 @@ public class CardAttachmentsFragment extends BrandedFragment implements Attachme
public void applyBrand(int mainColor) {
applyBrandToFAB(mainColor, binding.fab);
}
+
+ public static Fragment newInstance() {
+ return new CardAttachmentsFragment();
+ }
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java
new file mode 100644
index 000000000..97a011398
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardDialogFragment.java
@@ -0,0 +1,124 @@
+package it.niedermann.nextcloud.deck.ui.movecard;
+
+import android.content.Context;
+import android.content.res.ColorStateList;
+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.DialogFragment;
+
+import it.niedermann.nextcloud.deck.DeckLog;
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.databinding.DialogMoveCardBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.model.Board;
+import it.niedermann.nextcloud.deck.model.Stack;
+import it.niedermann.nextcloud.deck.model.full.FullStack;
+import it.niedermann.nextcloud.deck.ui.branding.BrandedDialogFragment;
+import it.niedermann.nextcloud.deck.ui.branding.BrandingUtil;
+import it.niedermann.nextcloud.deck.ui.pickstack.PickStackFragment;
+import it.niedermann.nextcloud.deck.ui.pickstack.PickStackListener;
+
+import static android.view.View.GONE;
+import static android.view.View.VISIBLE;
+
+public class MoveCardDialogFragment extends BrandedDialogFragment implements PickStackListener {
+
+ private static final String KEY_ORIGIN_ACCOUNT_ID = "account_id";
+ private static final String KEY_ORIGIN_BOARD_LOCAL_ID = "board_local_id";
+ private static final String KEY_ORIGIN_CARD_LOCAL_ID = "card_local_id";
+ private Long originAccountId;
+ private Long originBoardLocalId;
+ private Long originCardLocalId;
+
+ private DialogMoveCardBinding binding;
+ private PickStackFragment fragment;
+ private MoveCardListener moveCardListener;
+
+ private Account selectedAccount;
+ private Board selectedBoard;
+ private FullStack selectedStack;
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (getParentFragment() instanceof MoveCardListener) {
+ this.moveCardListener = (MoveCardListener) getParentFragment();
+ } else if (context instanceof MoveCardListener) {
+ this.moveCardListener = (MoveCardListener) context;
+ } else {
+ throw new IllegalArgumentException("Caller must implement " + MoveCardListener.class.getSimpleName());
+ }
+
+ final Bundle args = requireArguments();
+ originAccountId = args.getLong(KEY_ORIGIN_ACCOUNT_ID, -1L);
+ if (originAccountId < 0) {
+ throw new IllegalArgumentException("Missing " + KEY_ORIGIN_ACCOUNT_ID);
+ }
+ originCardLocalId = args.getLong(KEY_ORIGIN_CARD_LOCAL_ID, -1L);
+ if (originCardLocalId < 0) {
+ throw new IllegalArgumentException("Missing " + KEY_ORIGIN_CARD_LOCAL_ID);
+ }
+ originBoardLocalId = args.getLong(KEY_ORIGIN_BOARD_LOCAL_ID, -1L);
+ if (originBoardLocalId < 0) {
+ throw new IllegalArgumentException("Missing " + KEY_ORIGIN_BOARD_LOCAL_ID);
+ }
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ binding = DialogMoveCardBinding.inflate(inflater);
+ binding.submit.setOnClickListener((v) -> {
+ DeckLog.verbose("[Move card] Attempt to move to " + Stack.class.getSimpleName() + " #" + selectedStack.getLocalId());
+ this.moveCardListener.move(originAccountId, originCardLocalId, selectedAccount.getId(), selectedBoard.getLocalId(), selectedStack.getLocalId());
+ dismiss();
+ });
+ binding.cancel.setOnClickListener((v) -> dismiss());
+ return binding.getRoot();
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ fragment = new PickStackFragment();
+ getChildFragmentManager()
+ .beginTransaction()
+ .add(R.id.fragment_container, fragment)
+ .commit();
+ }
+
+ @Override
+ public void onStackPicked(@NonNull Account account, @Nullable Board board, @Nullable FullStack fullStack) {
+ this.selectedAccount = account;
+ this.selectedBoard = board;
+ this.selectedStack = fullStack;
+ if (board == null || fullStack == null) {
+ binding.submit.setEnabled(false);
+ binding.moveWarning.setVisibility(GONE);
+ } else {
+ binding.submit.setEnabled(true);
+ binding.moveWarning.setVisibility(board.getLocalId().equals(originBoardLocalId) ? GONE : VISIBLE);
+ }
+ }
+
+ @Override
+ public void applyBrand(int mainColor) {
+ final ColorStateList mainColorStateList = ColorStateList.valueOf(BrandingUtil.getSecondaryForegroundColorDependingOnTheme(requireContext(), mainColor));
+ binding.cancel.setTextColor(mainColorStateList);
+ binding.submit.setTextColor(mainColorStateList);
+ }
+
+ public static DialogFragment newInstance(long originAccountId, long originBoardLocalId, Long originCardLocalId) {
+ final DialogFragment dialogFragment = new MoveCardDialogFragment();
+ final Bundle args = new Bundle();
+ args.putLong(KEY_ORIGIN_ACCOUNT_ID, originAccountId);
+ args.putLong(KEY_ORIGIN_BOARD_LOCAL_ID, originBoardLocalId);
+ args.putLong(KEY_ORIGIN_CARD_LOCAL_ID, originCardLocalId);
+ dialogFragment.setArguments(args);
+ return dialogFragment;
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardListener.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardListener.java
new file mode 100644
index 000000000..f6f7a7a1f
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/movecard/MoveCardListener.java
@@ -0,0 +1,5 @@
+package it.niedermann.nextcloud.deck.ui.movecard;
+
+public interface MoveCardListener {
+ void move(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId);
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackFragment.java
new file mode 100644
index 000000000..6ebbed99b
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackFragment.java
@@ -0,0 +1,195 @@
+package it.niedermann.nextcloud.deck.ui.pickstack;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.Observer;
+
+import java.util.List;
+
+import it.niedermann.nextcloud.deck.DeckLog;
+import it.niedermann.nextcloud.deck.databinding.FragmentPickStackBinding;
+import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.model.Board;
+import it.niedermann.nextcloud.deck.model.full.FullStack;
+import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+import it.niedermann.nextcloud.deck.ui.ImportAccountActivity;
+import it.niedermann.nextcloud.deck.ui.preparecreate.AccountAdapter;
+import it.niedermann.nextcloud.deck.ui.preparecreate.BoardAdapter;
+import it.niedermann.nextcloud.deck.ui.preparecreate.SelectedListener;
+import it.niedermann.nextcloud.deck.ui.preparecreate.StackAdapter;
+
+import static androidx.lifecycle.Transformations.switchMap;
+import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentAccountId;
+import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentBoardId;
+import static it.niedermann.nextcloud.deck.DeckApplication.readCurrentStackId;
+
+public class PickStackFragment extends Fragment {
+
+ private FragmentPickStackBinding binding;
+
+ private SyncManager syncManager;
+
+ private PickStackListener pickStackListener;
+
+ private long lastAccountId;
+ private long lastBoardId;
+ private long lastStackId;
+
+ private ArrayAdapter<Account> accountAdapter;
+ private ArrayAdapter<Board> boardAdapter;
+ private ArrayAdapter<FullStack> stackAdapter;
+
+ @Nullable
+ private LiveData<List<Board>> boardsLiveData;
+ @NonNull
+ private Observer<List<Board>> boardsObserver = (boards) -> {
+ boardAdapter.clear();
+ boardAdapter.addAll(boards);
+ binding.boardSelect.setEnabled(true);
+
+ if (boards.size() > 0) {
+ binding.boardSelect.setEnabled(true);
+
+ Board boardToSelect = null;
+ for (Board board : boards) {
+ if (board.getLocalId() == lastBoardId) {
+ boardToSelect = board;
+ break;
+ }
+ }
+ if(boardToSelect == null) {
+ boardToSelect = boards.get(0);
+ }
+ binding.boardSelect.setSelection(boardAdapter.getPosition(boardToSelect));
+ } else {
+ binding.boardSelect.setEnabled(false);
+ pickStackListener.onStackPicked((Account) binding.accountSelect.getSelectedItem(), null, null);
+ }
+ };
+
+ @Nullable
+ private LiveData<List<FullStack>> stacksLiveData;
+ @NonNull
+ private Observer<List<FullStack>> stacksObserver = (fullStacks) -> {
+ stackAdapter.clear();
+ stackAdapter.addAll(fullStacks);
+
+ if (fullStacks.size() > 0) {
+ binding.stackSelect.setEnabled(true);
+
+ FullStack fullStackToSelect = null;
+ for (FullStack fullStack : fullStacks) {
+ if (fullStack.getLocalId() == lastStackId) {
+ fullStackToSelect = fullStack;
+ break;
+ }
+ }
+ if (fullStackToSelect == null) {
+ fullStackToSelect = fullStacks.get(0);
+ }
+ binding.stackSelect.setSelection(stackAdapter.getPosition(fullStackToSelect));
+ } else {
+ binding.stackSelect.setEnabled(false);
+ pickStackListener.onStackPicked((Account) binding.accountSelect.getSelectedItem(), (Board) binding.boardSelect.getSelectedItem(), null);
+ }
+ };
+
+ @Override
+ public void onAttach(@NonNull Context context) {
+ super.onAttach(context);
+ if (getParentFragment() instanceof PickStackListener) {
+ this.pickStackListener = (PickStackListener) getParentFragment();
+ } else if (context instanceof PickStackListener) {
+ this.pickStackListener = (PickStackListener) context;
+ } else {
+ throw new IllegalArgumentException("Caller must implement " + PickStackListener.class.getSimpleName());
+ }
+ DeckLog.error("PICKSTACK: onAttach successful");
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ binding = FragmentPickStackBinding.inflate(getLayoutInflater());
+
+ accountAdapter = new AccountAdapter(requireContext());
+ binding.accountSelect.setAdapter(accountAdapter);
+ binding.accountSelect.setEnabled(false);
+ boardAdapter = new BoardAdapter(requireContext());
+ binding.boardSelect.setAdapter(boardAdapter);
+ binding.stackSelect.setEnabled(false);
+ stackAdapter = new StackAdapter(requireContext());
+ binding.stackSelect.setAdapter(stackAdapter);
+ binding.stackSelect.setEnabled(false);
+
+ syncManager = new SyncManager(requireContext());
+
+ switchMap(syncManager.hasAccounts(), hasAccounts -> {
+ if (hasAccounts) {
+ return syncManager.readAccounts();
+ } else {
+ startActivityForResult(new Intent(requireActivity(), ImportAccountActivity.class), ImportAccountActivity.REQUEST_CODE_IMPORT_ACCOUNT);
+ return null;
+ }
+ }).observe(getViewLifecycleOwner(), (List<Account> accounts) -> {
+ if (accounts == null || accounts.size() == 0) {
+ throw new IllegalStateException("hasAccounts() returns true, but readAccounts() returns null or has no entry");
+ }
+
+ lastAccountId = readCurrentAccountId(requireContext());
+ lastBoardId = readCurrentBoardId(requireContext(), lastAccountId);
+ lastStackId = readCurrentStackId(requireContext(), lastAccountId, lastBoardId);
+
+ accountAdapter.clear();
+ accountAdapter.addAll(accounts);
+ binding.accountSelect.setEnabled(true);
+
+ for (Account account : accounts) {
+ if (account.getId() == lastAccountId) {
+ binding.accountSelect.setSelection(accountAdapter.getPosition(account));
+ break;
+ }
+ }
+ });
+
+ binding.accountSelect.setOnItemSelectedListener((SelectedListener) (parent, view, position, id) -> {
+ updateLiveDataSource(boardsLiveData, boardsObserver, syncManager.getBoardsWithEditPermission(parent.getSelectedItemId()));
+ });
+
+ binding.boardSelect.setOnItemSelectedListener((SelectedListener) (parent, view, position, id) -> {
+ updateLiveDataSource(stacksLiveData, stacksObserver, syncManager.getStacksForBoard(binding.accountSelect.getSelectedItemId(), parent.getSelectedItemId()));
+ });
+
+ binding.stackSelect.setOnItemSelectedListener((SelectedListener) (parent, view, position, id) -> {
+ pickStackListener.onStackPicked((Account) binding.accountSelect.getSelectedItem(), (Board) binding.boardSelect.getSelectedItem(), (FullStack) parent.getSelectedItem());
+ });
+
+ return binding.getRoot();
+ }
+
+ /**
+ * Updates the source of the given liveData and de- and reregisters the given observer.
+ */
+ private <T> void updateLiveDataSource(@Nullable LiveData<T> liveData, Observer<T> observer, LiveData<T> newSource) {
+ if (liveData != null) {
+ liveData.removeObserver(observer);
+ }
+ liveData = newSource;
+ liveData.observe(getViewLifecycleOwner(), observer);
+ }
+
+ public static PickStackFragment newInstance() {
+ return new PickStackFragment();
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackListener.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackListener.java
new file mode 100644
index 000000000..0abb4dac9
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/pickstack/PickStackListener.java
@@ -0,0 +1,12 @@
+package it.niedermann.nextcloud.deck.ui.pickstack;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import it.niedermann.nextcloud.deck.model.Account;
+import it.niedermann.nextcloud.deck.model.Board;
+import it.niedermann.nextcloud.deck.model.full.FullStack;
+
+public interface PickStackListener {
+ void onStackPicked(@NonNull Account account, @Nullable Board board, @Nullable FullStack fullStack);
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java
index ea8d90f22..04429f225 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/settings/SettingsFragment.java
@@ -23,6 +23,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Brande
private BrandedSwitchPreference wifiOnlyPref;
private BrandedSwitchPreference themePref;
private BrandedSwitchPreference brandingPref;
+ private BrandedSwitchPreference compactPref;
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
@@ -67,6 +68,8 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Brande
DeckLog.error("Could not find preference with key: \"" + getString(R.string.pref_key_dark_theme) + "\"");
}
+ compactPref = findPreference(getString(R.string.pref_key_compact));
+
final ListPreference backgroundSyncPref = findPreference(getString(R.string.pref_key_background_sync));
if (backgroundSyncPref != null) {
backgroundSyncPref.setOnPreferenceChangeListener((Preference preference, Object newValue) -> {
@@ -92,5 +95,6 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Brande
wifiOnlyPref.applyBrand(mainColor);
themePref.applyBrand(mainColor);
brandingPref.applyBrand(mainColor);
+ compactPref.applyBrand(mainColor);
}
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java
index 726a74184..72c5ffc84 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/stack/StackFragment.java
@@ -20,15 +20,22 @@ import java.util.List;
import it.niedermann.android.crosstabdnd.DragAndDropTab;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.databinding.FragmentStackBinding;
+import it.niedermann.nextcloud.deck.model.Card;
+import it.niedermann.nextcloud.deck.model.Stack;
import it.niedermann.nextcloud.deck.model.full.FullCard;
import it.niedermann.nextcloud.deck.persistence.sync.SyncManager;
+import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.WrappedLiveData;
import it.niedermann.nextcloud.deck.ui.MainViewModel;
import it.niedermann.nextcloud.deck.ui.branding.BrandedFragment;
import it.niedermann.nextcloud.deck.ui.card.CardAdapter;
import it.niedermann.nextcloud.deck.ui.card.SelectCardListener;
+import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment;
import it.niedermann.nextcloud.deck.ui.filter.FilterViewModel;
+import it.niedermann.nextcloud.deck.ui.movecard.MoveCardListener;
-public class StackFragment extends BrandedFragment implements DragAndDropTab<CardAdapter> {
+import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce;
+
+public class StackFragment extends BrandedFragment implements DragAndDropTab<CardAdapter>, MoveCardListener {
private static final String KEY_STACK_ID = "stackId";
@@ -153,4 +160,17 @@ public class StackFragment extends BrandedFragment implements DragAndDropTab<Car
return fragment;
}
+
+ @Override
+ public void move(long originAccountId, long originCardLocalId, long targetAccountId, long targetBoardLocalId, long targetStackLocalId) {
+ WrappedLiveData<Void> liveData = syncManager.moveCard(originAccountId, originCardLocalId, targetAccountId, targetBoardLocalId, targetStackLocalId);
+ observeOnce(liveData, requireActivity(), (next) -> {
+ if (liveData.hasError()) {
+ ExceptionDialogFragment.newInstance(liveData.getError(), null).show(getChildFragmentManager(), ExceptionDialogFragment.class.getSimpleName());
+ } else {
+ DeckLog.log("Moved " + Card.class.getSimpleName() + " \"" + originCardLocalId + "\" to " + Stack.class.getSimpleName() + " \"" + targetStackLocalId + "\"");
+ }
+ });
+ }
+
} \ No newline at end of file
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
new file mode 100644
index 000000000..cf872f406
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/CompactLabelChip.java
@@ -0,0 +1,22 @@
+package it.niedermann.nextcloud.deck.ui.view.labelchip;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Px;
+
+import it.niedermann.nextcloud.deck.R;
+import it.niedermann.nextcloud.deck.model.Label;
+
+import static it.niedermann.nextcloud.deck.util.DimensionUtil.dpToPx;
+
+@SuppressLint("ViewConstructor")
+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(dpToPx(context, R.dimen.compact_label_height));
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/DefaultLabelChip.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/DefaultLabelChip.java
new file mode 100644
index 000000000..80e44d7e0
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/DefaultLabelChip.java
@@ -0,0 +1,21 @@
+package it.niedermann.nextcloud.deck.ui.view.labelchip;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Px;
+
+import it.niedermann.nextcloud.deck.model.Label;
+
+import static android.text.TextUtils.TruncateAt.MIDDLE;
+
+@SuppressLint("ViewConstructor")
+public class DefaultLabelChip extends LabelChip {
+
+ public DefaultLabelChip(@NonNull Context context, @NonNull Label label, @Px int gutter) {
+ super(context, label, gutter);
+ setText(label.getTitle());
+ setEllipsize(MIDDLE);
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/LabelChip.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/LabelChip.java
index db4e123d3..ba3331457 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/LabelChip.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labelchip/LabelChip.java
@@ -1,4 +1,4 @@
-package it.niedermann.nextcloud.deck.ui.view;
+package it.niedermann.nextcloud.deck.ui.view.labelchip;
import android.annotation.SuppressLint;
import android.content.Context;
@@ -16,22 +16,20 @@ import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.model.Label;
import it.niedermann.nextcloud.deck.util.ColorUtil;
-import static android.text.TextUtils.TruncateAt.MIDDLE;
-
@SuppressLint("ViewConstructor")
public class LabelChip extends Chip {
private final Label label;
+ protected final FlexboxLayout.LayoutParams params = new FlexboxLayout.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT
+ );
+
public LabelChip(@NonNull Context context, @NonNull Label label, @Px int gutter) {
super(context);
this.label = label;
- FlexboxLayout.LayoutParams params = new FlexboxLayout.LayoutParams(
- ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
- );
-
params.setMargins(0, 0, gutter, 0);
setLayoutParams(params);
setEnsureMinTouchTargetSize(false);
@@ -43,9 +41,6 @@ public class LabelChip extends Chip {
setTextEndPadding(gutter);
setChipEndPadding(gutter);
- setText(label.getTitle());
- setEllipsize(MIDDLE);
-
try {
int labelColor = Color.parseColor("#" + label.getColor());
ColorStateList c = ColorStateList.valueOf(labelColor);
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/CompactLabelLayout.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/CompactLabelLayout.java
new file mode 100644
index 000000000..1c5e35d97
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/CompactLabelLayout.java
@@ -0,0 +1,22 @@
+package it.niedermann.nextcloud.deck.ui.view.labellayout;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+
+import it.niedermann.nextcloud.deck.model.Label;
+import it.niedermann.nextcloud.deck.ui.view.labelchip.CompactLabelChip;
+import it.niedermann.nextcloud.deck.ui.view.labelchip.LabelChip;
+
+public class CompactLabelLayout extends LabelLayout {
+
+ public CompactLabelLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected LabelChip createLabelChip(@NonNull Label label) {
+ return new CompactLabelChip(getContext(), label, gutter);
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/DefaultLabelLayout.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/DefaultLabelLayout.java
new file mode 100644
index 000000000..f2d6d0752
--- /dev/null
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/DefaultLabelLayout.java
@@ -0,0 +1,21 @@
+package it.niedermann.nextcloud.deck.ui.view.labellayout;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+import androidx.annotation.NonNull;
+
+import it.niedermann.nextcloud.deck.model.Label;
+import it.niedermann.nextcloud.deck.ui.view.labelchip.DefaultLabelChip;
+
+public class DefaultLabelLayout extends LabelLayout {
+
+ public DefaultLabelLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected DefaultLabelChip createLabelChip(@NonNull Label label) {
+ return new DefaultLabelChip(getContext(), label, gutter);
+ }
+}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/LabelLayout.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java
index 814c63ce1..9a1d60021 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/LabelLayout.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/view/labellayout/LabelLayout.java
@@ -1,4 +1,4 @@
-package it.niedermann.nextcloud.deck.ui.view;
+package it.niedermann.nextcloud.deck.ui.view.labellayout;
import android.content.Context;
import android.util.AttributeSet;
@@ -14,14 +14,16 @@ import java.util.List;
import it.niedermann.nextcloud.deck.DeckLog;
import it.niedermann.nextcloud.deck.R;
import it.niedermann.nextcloud.deck.model.Label;
+import it.niedermann.nextcloud.deck.ui.view.labelchip.LabelChip;
import static it.niedermann.nextcloud.deck.util.DimensionUtil.dpToPx;
-public class LabelLayout extends FlexboxLayout {
+public abstract class LabelLayout extends FlexboxLayout {
@Px
- private int gutter;
- private List<LabelChip> chipList = new LinkedList<>();
+ final protected int gutter;
+ @NonNull
+ final private List<LabelChip> chipList = new LinkedList<>();
public LabelLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -82,9 +84,11 @@ public class LabelLayout extends FlexboxLayout {
continue labelList;
}
}
- LabelChip chip = new LabelChip(getContext(), label, gutter);
+ final LabelChip chip = createLabelChip(label);
addView(chip);
chipList.add(chip);
}
}
+
+ protected abstract LabelChip createLabelChip(@NonNull Label label);
}
diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/util/DrawerMenuUtil.java b/app/src/main/java/it/niedermann/nextcloud/deck/util/DrawerMenuUtil.java
index 7457c0df7..3ee5bbe74 100644
--- a/app/src/main/java/it/niedermann/nextcloud/deck/util/DrawerMenuUtil.java
+++ b/app/src/main/java/it/niedermann/nextcloud/deck/util/DrawerMenuUtil.java
@@ -63,6 +63,9 @@ public class DrawerMenuUtil {
case R.id.manage_labels:
ManageLabelsDialogFragment.newInstance(board.getLocalId()).show(context.getSupportFragmentManager(), editBoard);
return true;
+ case R.id.clone_board:
+ context.onClone(board);
+ return true;
case R.id.archive_board:
context.onArchive(board);
return true;
diff --git a/app/src/main/res/drawable/ic_baseline_compact_24.xml b/app/src/main/res/drawable/ic_baseline_compact_24.xml
new file mode 100644
index 000000000..d395a7338
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_compact_24.xml
@@ -0,0 +1,5 @@
+<vector 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="M8,19h3v4h2v-4h3l-4,-4 -4,4zM16,5h-3L13,1h-2v4L8,5l4,4 4,-4zM4,11v2h16v-2L4,11z"/>
+</vector>
diff --git a/app/src/main/res/layout/activity_archived.xml b/app/src/main/res/layout/activity_archived.xml
index 6dd7024dc..d5e9646a8 100644
--- a/app/src/main/res/layout/activity_archived.xml
+++ b/app/src/main/res/layout/activity_archived.xml
@@ -35,5 +35,5 @@
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- tools:listitem="@layout/item_card" />
+ tools:listitem="@layout/item_card_default" />
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_move_card.xml b/app/src/main/res/layout/dialog_move_card.xml
new file mode 100644
index 000000000..e94f63b74
--- /dev/null
+++ b/app/src/main/res/layout/dialog_move_card.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout 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="wrap_content"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="@dimen/spacer_1x">
+
+ <ScrollView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <FrameLayout
+ android:id="@+id/fragment_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </ScrollView>
+
+ <TextView
+ android:id="@+id/move_warning"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/spacer_2x"
+ android:drawableStart="@drawable/ic_warning_white_24dp"
+ android:drawablePadding="@dimen/spacer_3x"
+ android:paddingStart="@dimen/spacer_3x"
+ android:paddingEnd="@dimen/spacer_1x"
+ android:text="@string/move_warning"
+ android:textColor="@color/danger"
+ android:visibility="gone"
+ app:drawableTint="@color/danger"
+ tools:visibility="visible" />
+
+ <com.google.android.flexbox.FlexboxLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/spacer_1x"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_1x"
+ android:paddingEnd="@dimen/spacer_1x"
+ app:justifyContent="space_between">
+
+ <Button
+ android:id="@+id/cancel"
+ style="@style/Widget.MaterialComponents.Button.TextButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="start"
+ android:layout_marginEnd="@dimen/spacer_1x"
+ android:text="@android:string/cancel"
+ android:textColor="@color/defaultBrand" />
+
+ <Button
+ android:id="@+id/submit"
+ style="@style/Widget.MaterialComponents.Button.TextButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:text="@string/action_card_move"
+ android:textColor="@color/defaultBrand" />
+ </com.google.android.flexbox.FlexboxLayout>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_pick_stack.xml b/app/src/main/res/layout/fragment_pick_stack.xml
new file mode 100644
index 000000000..9769969ff
--- /dev/null
+++ b/app/src/main/res/layout/fragment_pick_stack.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <androidx.appcompat.widget.AppCompatSpinner
+ android:id="@+id/account_select"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/choose_account"
+ tools:listitem="@layout/item_prepare_create_account" />
+
+ <androidx.appcompat.widget.AppCompatSpinner
+ android:id="@+id/board_select"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/choose_board"
+ tools:listitem="@layout/item_board" />
+
+ <androidx.appcompat.widget.AppCompatSpinner
+ android:id="@+id/stack_select"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:prompt="@string/choose_list"
+ tools:listitem="@layout/item_board" />
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_stack.xml b/app/src/main/res/layout/fragment_stack.xml
index d4cbd2ee8..e0fb35c8a 100644
--- a/app/src/main/res/layout/fragment_stack.xml
+++ b/app/src/main/res/layout/fragment_stack.xml
@@ -24,5 +24,5 @@
android:scrollbarStyle="outsideOverlay"
android:scrollbars="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
- tools:listitem="@layout/item_card" />
+ tools:listitem="@layout/item_card_default" />
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_card_compact.xml b/app/src/main/res/layout/item_card_compact.xml
new file mode 100644
index 000000000..457aa0d25
--- /dev/null
+++ b/app/src/main/res/layout/item_card_compact.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.card.MaterialCardView 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/card"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/spacer_2x"
+ android:layout_marginTop="@dimen/spacer_1x"
+ android:layout_marginEnd="@dimen/spacer_2x"
+ android:layout_marginBottom="@dimen/spacer_1x"
+ android:focusable="true"
+ app:cardBackgroundColor="@color/bg_card">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/spacer_1x"
+ android:paddingBottom="@dimen/spacer_1x">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_2x"
+ android:paddingEnd="@dimen/spacer_1x">
+
+ <TextView
+ android:id="@+id/card_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4sp"
+ android:layout_weight="1"
+ android:textColor="?attr/colorAccent"
+ android:textSize="18sp"
+ tools:ignore="RtlSymmetry"
+ tools:text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l" />
+
+ <ImageView
+ android:id="@+id/not_synced_yet"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8sp"
+ android:contentDescription="@string/not_synced_yet"
+ android:visibility="gone"
+ app:srcCompat="@drawable/ic_sync_blue_24dp"
+ tools:visibility="visible" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_1hx"
+ tools:ignore="RtlSymmetry">
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/card_due_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/due_tomorrow_background"
+ android:drawablePadding="@dimen/spacer_1hx"
+ android:gravity="center"
+ android:padding="@dimen/spacer_1hx"
+ android:textColor="@color/fg_secondary"
+ app:drawableStartCompat="@drawable/calendar_blank_grey600_24dp"
+ tools:text="tomorrow" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/card_menu"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/label_menu"
+ android:padding="@dimen/spacer_1hx"
+ android:tint="?attr/colorAccent"
+ app:srcCompat="@drawable/ic_menu" />
+ </LinearLayout>
+
+ <it.niedermann.nextcloud.deck.ui.view.labellayout.CompactLabelLayout
+ android:id="@+id/labels"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/spacer_1x"
+ android:animateLayoutChanges="true"
+ android:paddingStart="@dimen/spacer_2x"
+ android:paddingEnd="@dimen/spacer_2x"
+ app:flexWrap="nowrap"
+ tools:layout_height="@dimen/avatar_size" />
+
+ </LinearLayout>
+</com.google.android.material.card.MaterialCardView> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_card.xml b/app/src/main/res/layout/item_card_default.xml
index c0457f5e4..01970ef57 100644
--- a/app/src/main/res/layout/item_card.xml
+++ b/app/src/main/res/layout/item_card_default.xml
@@ -69,7 +69,7 @@
</LinearLayout>
</LinearLayout>
- <it.niedermann.nextcloud.deck.ui.view.LabelLayout
+ <it.niedermann.nextcloud.deck.ui.view.labellayout.DefaultLabelLayout
android:id="@+id/labels"
android:layout_width="match_parent"
android:layout_height="wrap_content"
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
new file mode 100644
index 000000000..7d32e7f44
--- /dev/null
+++ b/app/src/main/res/layout/item_card_default_only_title.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.google.android.material.card.MaterialCardView 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/card"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@dimen/spacer_2x"
+ android:layout_marginTop="@dimen/spacer_1x"
+ android:layout_marginEnd="@dimen/spacer_2x"
+ android:layout_marginBottom="@dimen/spacer_1x"
+ android:focusable="true"
+ app:cardBackgroundColor="@color/bg_card">
+
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_2x"
+ android:paddingTop="@dimen/spacer_1x"
+ android:paddingEnd="@dimen/spacer_1x"
+ android:paddingBottom="@dimen/spacer_1x">
+
+ <TextView
+ android:id="@+id/card_title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4sp"
+ android:layout_weight="1"
+ android:textColor="?attr/colorAccent"
+ android:textSize="18sp"
+ tools:ignore="RtlSymmetry"
+ tools:text="Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut l" />
+
+ <ImageView
+ android:id="@+id/not_synced_yet"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8sp"
+ android:contentDescription="@string/not_synced_yet"
+ android:visibility="gone"
+ app:srcCompat="@drawable/ic_sync_blue_24dp"
+ tools:visibility="visible" />
+
+ <LinearLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingStart="@dimen/spacer_1hx"
+ tools:ignore="RtlSymmetry">
+
+ <androidx.appcompat.widget.AppCompatTextView
+ android:id="@+id/card_due_date"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/due_tomorrow_background"
+ android:drawablePadding="@dimen/spacer_1hx"
+ android:gravity="center"
+ android:padding="@dimen/spacer_1hx"
+ android:textColor="@color/fg_secondary"
+ app:drawableStartCompat="@drawable/calendar_blank_grey600_24dp"
+ tools:text="tomorrow" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:id="@+id/card_menu"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackgroundBorderless"
+ android:contentDescription="@string/label_menu"
+ android:padding="@dimen/spacer_1hx"
+ android:tint="?attr/colorAccent"
+ app:srcCompat="@drawable/ic_menu" />
+ </LinearLayout>
+</com.google.android.material.card.MaterialCardView> \ No newline at end of file
diff --git a/app/src/main/res/menu/navigation_context_menu.xml b/app/src/main/res/menu/navigation_context_menu.xml
index d56f07595..ece2b917e 100644
--- a/app/src/main/res/menu/navigation_context_menu.xml
+++ b/app/src/main/res/menu/navigation_context_menu.xml
@@ -12,6 +12,11 @@
android:title="@string/manage_tags"
app:showAsAction="never" />
<item
+ android:id="@+id/clone_board"
+ android:orderInCategory="10"
+ android:title="@string/clone_board"
+ app:showAsAction="never" />
+ <item
android:id="@+id/archive_board"
android:orderInCategory="20"
android:title="@string/archive_board"
diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml
index 3bc6b422e..06d1fb151 100644
--- a/app/src/main/res/values-cs-rCZ/strings.xml
+++ b/app/src/main/res/values-cs-rCZ/strings.xml
@@ -133,6 +133,7 @@
<string name="delete_board_message">Toto tabuli nadobro smaže, včetně všech seznamů a karet.</string>
<string name="settings_theme_title">Tmavý motiv vzhledu</string>
<string name="settings_branding_title">Opatření vlastním logem</string>
+ <string name="settings_compact_title">Kompaktní režim 🆕</string>
<string name="settings_background_sync">Synchronizace na pozadí</string>
<string name="pref_value_wifi_and_mobile">Synchr. přes Wi-Fi a mobilní data</string>
<string name="pref_value_wifi_only">Synchr. pouze přes Wi-Fi</string>
diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml
index 69f7a1886..b0e4128b8 100644
--- a/app/src/main/res/values-de/strings.xml
+++ b/app/src/main/res/values-de/strings.xml
@@ -129,6 +129,7 @@
<string name="delete_board_message">Dies wird dieses Board endgültig inklusive aller Listen und Karten löschen.</string>
<string name="settings_theme_title">Dunkles Design</string>
<string name="settings_branding_title">Branding</string>
+ <string name="settings_compact_title">Kompakt-Modus 🆕</string>
<string name="settings_background_sync">Hintergrundsynchronisierung</string>
<string name="pref_value_wifi_and_mobile">Über WLAN und mobile Daten synchronisieren</string>
<string name="pref_value_wifi_only">Nur über WLAN synchronisieren</string>
diff --git a/app/src/main/res/values-el/strings.xml b/app/src/main/res/values-el/strings.xml
index 5ef209258..cb1f66037 100644
--- a/app/src/main/res/values-el/strings.xml
+++ b/app/src/main/res/values-el/strings.xml
@@ -129,6 +129,7 @@
<string name="delete_board_message">Μόνιμη διαγραφή πίνακα με όλες τις λίστες και κάρτες.</string>
<string name="settings_theme_title">Σκούρο θέμα</string>
<string name="settings_branding_title">Επωνυμία</string>
+ <string name="settings_compact_title">Συμπαγής προβολή 🆕</string>
<string name="settings_background_sync">Συγχρονισμός στο παρασκήνιο</string>
<string name="pref_value_wifi_and_mobile">Συγχρονισμός σε Wi-Fi και δεδομένα κινητού</string>
<string name="pref_value_wifi_only">Συγχρονισμός μόνο σε Wi-Fi</string>
@@ -217,6 +218,7 @@
<string name="error_dialog_title">Ωχ όχι - Τώρα τί; 🙁</string>
<string name="error_dialog_tip_token_mismatch_retry">Δοκιμάστε τερματισμό της εφαρμογή και επανεκκίνηση. Ίσως υπάρχει λάθος σύνδεση.</string>
<string name="error_dialog_tip_token_mismatch_clear_storage">Εάν το πρόβλημα παραμένει, προσπαθήστε να εκκαθαρίσετε τον χώρο αποθήκευσης και των δύο εφαρμογών: του Nextcloud και του Nextcloud Deck για επίλυση.</string>
+ <string name="error_dialog_tip_database_upgrade_failed">Η αναβάθμιση της βάσης δεδομένων απέτυχε. Παρακαλούμε αναφέρετε το πρόβλημα και εκκαθαρίστε τον χώρο αποθήκευσης για να χρησιμοποιήσετε την εφαρμογή κανονικά.</string>
<string name="error_dialog_tip_clear_storage">Μπορείτε να εκκαθαρίσετε τον χώρο αποθήκευσης από τις πληροφορίες εφαρμογής και επιλέγοντας Αποθηκευτικός χώρος → Εκκαθάριση χώρου αποθήκευσης.</string>
<string name="error_dialog_tip_files_outdated">Η εφαρμογή Nextcloud δεν είναι ενημερωμένη. Παρακαλούμε επισκεφθείτε το Play Store ή το F-Groid για λήψη της τελευταίας έκδοσης.</string>
<string name="error_dialog_tip_files_force_stop">Κάτι φαίνεται να πάει στραβά με την εφαρμογή Nextcloud. Προσπαθήστε να τερματίσετε την εφαρμογή Nextcloud και την εφαρμογή Nextcloud Deck.</string>
@@ -232,6 +234,7 @@
<string name="error_dialog_version_not_parsable">Δεν μπορέσαμε να προσδιορίσουμε την έκδοση της εφαρμογής Deck του διακομιστή σας. Βεβαιωθείτε ότι είναι εγκατεστημένο και ενεργοποιημένο.</string>
<string name="error_dialog_capabilities_not_parsable">Δεν ήταν δυνατός ο έλεγχος των δυνατοτήτων του διακομιστή σας. Βεβαιωθείτε ότι ο διακομιστής σας λειτουργεί σωστά και οι εφαρμογές έχουν πρόσβαση στο Nextcloud.</string>
<string name="error_dialog_attachment_upload_failed">Δεν ήταν δυνατή η μεταφόρτωση ενός συνημμένου. Προσπαθήστε να το διαμοιράσετε με άλλο τρόπο και ενημερώστε μας σχετικά με το σφάλμα.</string>
+ <string name="error_dialog_tip_disable_battery_optimizations">Παρακαλούμε απενεργοποιήστε όλες τις βελτιστοποιήσεις μπαταρίας για το Nextcloud και την εφαρμογή Deck.</string>
<string name="error_action_open_deck_info">Πληροφορίες εφαρμογής</string>
<string name="error_action_open_network">Ρυθμίσεις δικτύου</string>
<string name="error_action_server_logs">Αρχεία καταγραφής διακομιστή</string>
@@ -253,4 +256,5 @@
<item quantity="other">%1$d σφάλματα κατά την μεταφόρτωση</item>
</plurals>
<string name="simple_report">Αναφορά</string>
- </resources>
+ <string name="error_action_open_battery_settings">Ρυθμίσεις μπαταρίας</string>
+</resources>
diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml
index cf92736ed..9bd01d6dd 100644
--- a/app/src/main/res/values-es/strings.xml
+++ b/app/src/main/res/values-es/strings.xml
@@ -217,7 +217,6 @@
<string name="error_dialog_title">Oh no - ¿Ahora qué? 🙁</string>
<string name="error_dialog_tip_token_mismatch_retry">Por favor, intenta forzar el cierre de la aplicación y vuelve a abrirla. Es posible que haya habido un problema de conexión con la aplicación Nextcloud.</string>
<string name="error_dialog_tip_token_mismatch_clear_storage">Si el problema persiste, intenta limpiar borrar el almacenamiento de ambas apps, Nextcloud y Nextcloud Deck para solucionar este problema.</string>
- <string name="error_dialog_tip_database_upgrade_failed">La actualización de la base de datos falló. Por favor, informe del problema y limpie el almacenamiento para usar la aplicación de manera normal.</string>
<string name="error_dialog_tip_clear_storage">Puedes limpiar el almacenamiento abriendo la información de la app y seleccionando Almacenamiento → Eliminar datos.</string>
<string name="error_dialog_tip_files_outdated">Su aplicación Nextcloud parece que está desactualizada. Por favor visite la Play Store o F-Droid para conseguir la versión más reciente.</string>
<string name="error_dialog_tip_files_force_stop">Algo parece ir mal en tu app de Nextcloud. Por favor, intenta forzar el cierre de ambas, Nextcloud y Nextcloud Deck.</string>
diff --git a/app/src/main/res/values-eu/strings.xml b/app/src/main/res/values-eu/strings.xml
index 2f6ccb012..e704e9fcd 100644
--- a/app/src/main/res/values-eu/strings.xml
+++ b/app/src/main/res/values-eu/strings.xml
@@ -148,7 +148,7 @@
<string name="hours_6">6 ordu</string>
<string name="action_card_move">Mugitu txartela</string>
<string name="action_card_move_title">Mugitu %1$s</string>
- <string name="please_add_an_account_first">Mesedez, gehitu kontu bat lehenengo</string>
+ <string name="please_add_an_account_first">Gehitu kontu bat lehenengo</string>
<string name="title_is_mandatory">Izenburua jartzea nahitaezkoa da</string>
<string name="provide_at_least_a_title_or_description">Jarri gutxienez izenburu edo deskribapen bat</string>
<string name="welcome_text">Ongi etorri %1$s(e)ra</string>
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 89fa234ee..6c6d1792e 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -20,7 +20,7 @@
<string name="simple_error">Erreur</string>
<string name="simple_exception">Exception</string>
<string name="simple_close">fermer</string>
- <string name="simple_open">Ouvert</string>
+ <string name="simple_open">Ouvrir</string>
<string name="simple_switch">Permuter</string>
<string name="simple_filter">Filtre</string>
<string name="simple_overdue">En retard</string>
@@ -129,6 +129,7 @@
<string name="delete_board_message">Cette action supprimera définitivement ce tableau incluant ses listes et ses cartes.</string>
<string name="settings_theme_title">Thème sombre</string>
<string name="settings_branding_title">Marque</string>
+ <string name="settings_compact_title">Mode compact</string>
<string name="settings_background_sync">Synchronisation en tâche de fond</string>
<string name="pref_value_wifi_and_mobile">Synchroniser en Wifi et données mobiles</string>
<string name="pref_value_wifi_only">Synchroniser uniquement en Wi-Fi</string>
@@ -217,6 +218,7 @@
<string name="error_dialog_title">Oh non ! - Et maintenant ? 🙁</string>
<string name="error_dialog_tip_token_mismatch_retry">Essayez de forcer la fermeture de l\'application puis redémarrer la. Il y avait peut-être une mauvaise connexion à Nextcloud.</string>
<string name="error_dialog_tip_token_mismatch_clear_storage">Si le problème persiste, essayez d\'effacer les données d\'application des deux applications: Nextcloud et Nextcloud Deck pour résoudre ce problème.</string>
+ <string name="error_dialog_tip_database_upgrade_failed">La mise à jour de la base de données a échoué. Veuillez envoyer l\'erreur et supprimer les données pour utiliser l\'application</string>
<string name="error_dialog_tip_clear_storage">Vous pouvez nettoyer l\'espace de stockage en ouvrant les paramètres de l\'application et en sélectionnant Stockage → Effacer le stockage.</string>
<string name="error_dialog_tip_files_outdated">Votre application Nextcloud semble ancienne. Veuillez visiter le Play Store ou F-Droid pour installer la dernière version.</string>
<string name="error_dialog_tip_files_force_stop">Quelque chose semble ne pas fonctionner avec votre application Nextcloud. Essayer de forcer l\'arrêt des applications Nextcloud et Nextcloud Notes.</string>
diff --git a/app/src/main/res/values-gl/strings.xml b/app/src/main/res/values-gl/strings.xml
index 6091b2f0b..e4a279121 100644
--- a/app/src/main/res/values-gl/strings.xml
+++ b/app/src/main/res/values-gl/strings.xml
@@ -114,8 +114,8 @@
<string name="do_you_want_to_save_your_changes">Confirma que quere gardar os cambios?</string>
<string name="do_you_want_to_archive_all_cards_of_the_list">Quere arquivar todas as tarxetas de %1$s?</string>
<plurals name="do_you_want_to_delete_the_current_list">
- <item quantity="one">Isto eliminará permanentemente %1$d tarxeta desta lista.</item>
- <item quantity="other">Isto eliminará permanentemente as %1$d tarxetas desta lista.</item>
+ <item quantity="one">Isto eliminará de xeito permanente %1$d tarxeta desta lista.</item>
+ <item quantity="other">Isto eliminará de xeito permanente as %1$d tarxetas desta lista.</item>
</plurals>
<plurals name="do_you_want_to_delete_the_label">
<item quantity="one">Isto retirará a etiqueta de %1$d tarxeta.</item>
@@ -129,6 +129,7 @@
<string name="delete_board_message">Isto eliminará este taboleiro de xeito permanente incluíndo todas as listas e tarxetas.</string>
<string name="settings_theme_title">Tema escuro</string>
<string name="settings_branding_title">Xestión da marca</string>
+ <string name="settings_compact_title">Modo compacto 🆕</string>
<string name="settings_background_sync">Sincronización do traballo en segundo plano</string>
<string name="pref_value_wifi_and_mobile">Sincronización con wifi e con datos móbiles</string>
<string name="pref_value_wifi_only">Sincronizar só con wifi</string>
diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml
index d9bde9148..b3cee302e 100644
--- a/app/src/main/res/values-it/strings.xml
+++ b/app/src/main/res/values-it/strings.xml
@@ -129,6 +129,7 @@
<string name="delete_board_message">Questo eliminerà definitivamente questa lavagna inclusi tutti gli elenchi e le schede.</string>
<string name="settings_theme_title">Tema scuro</string>
<string name="settings_branding_title">Marchio</string>
+ <string name="settings_compact_title">Modalità compatta 🆕</string>
<string name="settings_background_sync">Sincronizzazione in background</string>
<string name="pref_value_wifi_and_mobile">Sincronizza su Wi-FI e dati mobili</string>
<string name="pref_value_wifi_only">Sincronizza solo con Wi-Fi</string>
diff --git a/app/src/main/res/values-pl/strings.xml b/app/src/main/res/values-pl/strings.xml
index 4fd76266f..1dc85ba54 100644
--- a/app/src/main/res/values-pl/strings.xml
+++ b/app/src/main/res/values-pl/strings.xml
@@ -133,6 +133,7 @@
<string name="delete_board_message">Spowoduje to trwałe usunięcie tej tablicy, w tym wszystkich list i kart.</string>
<string name="settings_theme_title">Ciemny motyw</string>
<string name="settings_branding_title">Motyw serwera</string>
+ <string name="settings_compact_title">Tryb kompaktowy 🆕</string>
<string name="settings_background_sync">Synchronizacja w tle</string>
<string name="pref_value_wifi_and_mobile">Synchronizacja w sieci Wi-Fi i komórkowej transmisji danych</string>
<string name="pref_value_wifi_only">Synchronizuj tylko przez Wi-Fi</string>
@@ -221,6 +222,7 @@
<string name="error_dialog_title">O nie - co teraz? 🙁</string>
<string name="error_dialog_tip_token_mismatch_retry">Spróbuj wymusić zamknięcie aplikacji i uruchomić ją ponownie. Być może połączenie z aplikacją Nextcloud było nieprawidłowe.</string>
<string name="error_dialog_tip_token_mismatch_clear_storage">Jeśli problem będzie się powtarzać, spróbuj wyczyścić pamięć obu aplikacji: Nextcloud i Nextcloud Deck, aby rozwiązać ten problem.</string>
+ <string name="error_dialog_tip_database_upgrade_failed">Aktualizacja bazy danych nie powiodła się. Zgłoś problem oraz wyczyść pamięć, aby ponownie korzystać z aplikacji.</string>
<string name="error_dialog_tip_clear_storage">Możesz wyczyścić pamięć, otwierając informacje o aplikacji i wybierając Pamięć → Wyczyść pamięć podręczną.</string>
<string name="error_dialog_tip_files_outdated">Twoja aplikacja Nextcloud wydaje się, że jest nieaktualna. Odwiedź Sklep Play lub F-Droid, aby pobrać najnowszą wersję.</string>
<string name="error_dialog_tip_files_force_stop">Wydaje się, że coś jest nie tak z Twoją aplikacją Nextcloud. Spróbuj wymusić zatrzymanie aplikacji Nextcloud i Nextcloud Deck.</string>
diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml
index 8eb21ff3b..8d8822f9e 100644
--- a/app/src/main/res/values-pt-rBR/strings.xml
+++ b/app/src/main/res/values-pt-rBR/strings.xml
@@ -129,6 +129,7 @@
<string name="delete_board_message">Isso excluirá permanentemente este painel incluindo listas e cartões.</string>
<string name="settings_theme_title">Tema escuro</string>
<string name="settings_branding_title">Marcação</string>
+ <string name="settings_compact_title">Modo compacto</string>
<string name="settings_background_sync">Sincronização em segundo plano</string>
<string name="pref_value_wifi_and_mobile">Sincronizar com Wi-Fi e dados móveis</string>
<string name="pref_value_wifi_only">Sincronizar apenas com Wi-Fi</string>
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index bb95d8196..a338d3505 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -221,7 +221,6 @@
<string name="error_dialog_title">О нет, что теперь? 🙁</string>
<string name="error_dialog_tip_token_mismatch_retry">Попробуйте принудительно закрыть приложение и запустить его снова. Это могло быть связано с некорректным подключением к приложению Nextcloud.</string>
<string name="error_dialog_tip_token_mismatch_clear_storage">Если проблема повторяется, попробуйте для её решения очистить хранилище приложений Nextcloud и Nextcloud Карточки.</string>
- <string name="error_dialog_tip_database_upgrade_failed">Сбой обновления базы данных. Пожалуйста сообщите о проблеме и очистите хранилище, чтобы корректно использовать приложение.</string>
<string name="error_dialog_tip_clear_storage">Для очистки хранилища на откройте приложение «Настройки» и выберите Приложения → Nextcloud / Nextcloud Deck → Хранилище → Очистить хранилище</string>
<string name="error_dialog_tip_files_outdated">Ваше приложение Nextcloud устарело. Установите новую версию с Play Store или F-Droid.</string>
<string name="error_dialog_tip_files_force_stop">Похоже, что с приложением Nextcloud ведёт себя неожиданным образом. Попробуйте принудительно остановить приложения Nextcloud и Nextcloud Карточки. </string>
diff --git a/app/src/main/res/values-sk-rSK/strings.xml b/app/src/main/res/values-sk-rSK/strings.xml
index 0898dfb00..51b963d38 100644
--- a/app/src/main/res/values-sk-rSK/strings.xml
+++ b/app/src/main/res/values-sk-rSK/strings.xml
@@ -221,7 +221,6 @@
<string name="error_dialog_title">Ale nie - Čo teraz? 🙁</string>
<string name="error_dialog_tip_token_mismatch_retry">Skúste vyinútiť zatvorenie aplikácie a znova ju reštartovať. Možno došlo k chybnému pripojeniu k aplikácii Nextcloud.</string>
<string name="error_dialog_tip_token_mismatch_clear_storage">Ak problém pretrváva, pokúste sa tento problém vyriešiť vymazaním úložiska oboch aplikácií: Nextcloud a Nextcloud Deck.</string>
- <string name="error_dialog_tip_database_upgrade_failed">Aktualizácia databázy zlyhala. Nahláste problém a vymažte ukladací priestor, aby ste aplikáciu mohli normálne používať.</string>
<string name="error_dialog_tip_clear_storage">Úložisko môžete vyčistiť otvorením informácii o aplikácii a výberom Úložisko → Vyčistiť úložisko.</string>
<string name="error_dialog_tip_files_outdated">Váš Nextcloud vyzerá byť zastaralý. Navštívte Play Store alebo F-Droid pre získanie najnovšej verzie.</string>
<string name="error_dialog_tip_files_force_stop">Zdá sa, že s Nextcloudom niečo nie je v poriadku. Pokúste sa vynútiť zastavenie aplikácie Nextcloud aj aplikácie Nextcloud Deck.</string>
diff --git a/app/src/main/res/values-sr/strings.xml b/app/src/main/res/values-sr/strings.xml
index ad6075d5b..0af202087 100644
--- a/app/src/main/res/values-sr/strings.xml
+++ b/app/src/main/res/values-sr/strings.xml
@@ -112,6 +112,7 @@
<string name="not_synced_yet">Још није синхронизовано</string>
<string name="no_lists_yet">Нема још спискова</string>
<string name="do_you_want_to_save_your_changes">Да ли желите да сачувате измене?</string>
+ <string name="do_you_want_to_archive_all_cards_of_the_list">Да ли желите да архивирате све картице са %1$s?</string>
<plurals name="do_you_want_to_delete_the_current_list">
<item quantity="one">Овим ћете заувек обрисати %1$d картицу са овог списка.</item>
<item quantity="few">Овим ћете заувек обрисати %1$d картице са овог списка.</item>
@@ -240,7 +241,19 @@
<string name="info_box_maintenance_mode">Сервер у режиму одржавања</string>
<string name="info_box_version_not_supported">Серверска верзија %1$s није подржана, ажурирајте на %2$s</string>
<string name="share_link">Веза дељења</string>
+ <string name="archive_cards">Архивирај картице</string>
<string name="manage_accounts">Управљање налозима</string>
+ <string name="manage_list">Управљај списком</string>
<string name="simple_reply">Одговори</string>
+ <string name="error_while_uploading_attachment">Грешка приликом отпремања прилога: %1$s</string>
+ <string name="append_text_to_description">Придодај на опис</string>
+ <string name="add_text_as_comment">Додај као коментар</string>
+ <string name="progress_count">%1$d од %2$d</string>
+ <plurals name="progress_error_count">
+ <item quantity="one">%1$d грешка приликом отпремања.</item>
+ <item quantity="few">%1$d грешке приликом отпремања.</item>
+ <item quantity="other">%1$d грешака приликом отпремања.</item>
+ </plurals>
<string name="simple_report">Пријави</string>
- </resources>
+ <string name="error_action_open_battery_settings">Подешавања батерије</string>
+</resources>
diff --git a/app/src/main/res/values-tr/strings.xml b/app/src/main/res/values-tr/strings.xml
index a34bb2f8a..7b35c3683 100644
--- a/app/src/main/res/values-tr/strings.xml
+++ b/app/src/main/res/values-tr/strings.xml
@@ -129,6 +129,7 @@
<string name="delete_board_message">Bu işlem içindeki tüm liste ve kartlarla birlikte bu panoyu silecek.</string>
<string name="settings_theme_title">Koyu tema</string>
<string name="settings_branding_title">Markalama</string>
+ <string name="settings_compact_title">Sıkışık kip 🆕</string>
<string name="settings_background_sync">Arka planda eşitleme</string>
<string name="pref_value_wifi_and_mobile">Wi-Fi ve mobil veri ile eşitlensin</string>
<string name="pref_value_wifi_only">Yalnız Wi-Fi ile eşitlensin</string>
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 5ecd246df..54bff5fee 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,6 +4,8 @@
<dimen name="spacer_2x">16dp</dimen>
<dimen name="spacer_3x">24dp</dimen>
+ <dimen name="compact_label_height">6dp</dimen>
+
<!-- Drawer header -->
<dimen name="drawer_header_height">100dp</dimen>
<dimen name="drawer_header_logo_size">42dp</dimen>
diff --git a/app/src/main/res/values/setup.xml b/app/src/main/res/values/setup.xml
index 247bb0697..c790a5868 100644
--- a/app/src/main/res/values/setup.xml
+++ b/app/src/main/res/values/setup.xml
@@ -8,6 +8,7 @@
<string name="pref_key_wifi_only" translatable="false">wifiOnly</string>
<string name="pref_key_dark_theme" translatable="false">darkTheme</string>
<string name="pref_key_branding" translatable="false">branding</string>
+ <string name="pref_key_compact" translatable="false">compact</string>
<string name="pref_key_background_sync" translatable="false">backgroundSync</string>
<string name="pref_value_background_sync_off">off</string>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8e4d9ec60..678921cf2 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -154,6 +154,7 @@
<string name="delete_board_message">This will permanently delete this board including all lists and cards.</string>
<string name="settings_theme_title">Dark theme</string>
<string name="settings_branding_title">Branding</string>
+ <string name="settings_compact_title">Compact mode 🆕</string>
<string name="settings_background_sync">Background synchronization</string>
<string name="pref_value_wifi_and_mobile">Sync on Wi-Fi and mobile data</string>
<string name="pref_value_wifi_only">Sync only on Wi-Fi</string>
@@ -282,6 +283,10 @@
</plurals>
<string name="simple_report">Report</string>
<string name="error_action_open_battery_settings">Battery settings</string>
+ <string name="move_warning">Neither comments nor attachments can be transferred when moving the card to another board.</string>
+ <string name="clone_board">Clone board</string>
+ <string name="cloning_board">Cloning %1$s…</string>
+ <string name="successfully_cloned_board">Successfully cloned %1$s</string>
<string name="widget_stack_title">Stack</string>
<string name="widget_stack_header_icon">Widget header icon</string>
diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml
index 14943fff6..6d67622bd 100644
--- a/app/src/main/res/xml/settings.xml
+++ b/app/src/main/res/xml/settings.xml
@@ -29,6 +29,12 @@
android:icon="@drawable/ic_format_paint_grey600_24dp"
android:key="@string/pref_key_branding"
android:title="@string/settings_branding_title"
- app:defaultValue="true" />
+ app:defaultValue="false" />
+
+ <it.niedermann.nextcloud.deck.ui.branding.BrandedSwitchPreference
+ android:icon="@drawable/ic_baseline_compact_24"
+ android:key="@string/pref_key_compact"
+ android:title="@string/settings_compact_title"
+ app:defaultValue="false" />
</it.niedermann.nextcloud.deck.ui.branding.BrandedPreferenceCategory>
</PreferenceScreen>