diff options
author | Stefan Niedermann <info@niedermann.it> | 2020-05-14 13:45:37 +0300 |
---|---|---|
committer | Stefan Niedermann <info@niedermann.it> | 2020-05-14 13:45:37 +0300 |
commit | 700cf960612d8ea157e65bb5c0ca053b5fa3a5f3 (patch) | |
tree | f9fb67829438af08b11befc73c21bf219f459179 /app/src/main/java/it/niedermann/nextcloud | |
parent | 6f2580db3ca95e27533eebd6f3b8cd17271f1120 (diff) | |
parent | 06710dbac904ce33ae6d7b4c84499159d181b120 (diff) |
Merge branch 'master' into 454-replay-to-comments
# Conflicts:
# app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DataBaseAdapter.java
# app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java
Diffstat (limited to 'app/src/main/java/it/niedermann/nextcloud')
19 files changed, 550 insertions, 44 deletions
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 4a31ad54f..1b6d517a6 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 @@ -344,7 +344,9 @@ public class JsonToEntityParser { JsonObject deck = caps.getAsJsonObject("deck"); if (deck.has("version")) { version = deck.get("version").getAsString(); - + if (version == null || version.trim().length() < 1) { + throw new IllegalArgumentException("capabilities endpoint returned an invalid version string: \""+version+"\""); + } } } if (caps.has("theming")) { @@ -353,6 +355,9 @@ public class JsonToEntityParser { capabilities.setTextColor(theming.get("color-text").getAsString()); } } + if (version == null || version.trim().length() < 1) { + throw new IllegalArgumentException("capabilities endpoint returned no version string at all!"); + } capabilities.setDeckVersion(Version.of(version)); } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/full/FullSingleCardWidgetModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/full/FullSingleCardWidgetModel.java new file mode 100644 index 000000000..c9786f4d2 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/full/FullSingleCardWidgetModel.java @@ -0,0 +1,44 @@ +package it.niedermann.nextcloud.deck.model.full; + +import androidx.room.Embedded; +import androidx.room.Ignore; +import androidx.room.Relation; + +import it.niedermann.nextcloud.deck.model.Account; +import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; + +public class FullSingleCardWidgetModel { + + @Embedded + private SingleCardWidgetModel model; + + @Relation(parentColumn = "accountId", entityColumn = "id") + private Account account; + + @Ignore + private FullCard fullCard; + + public SingleCardWidgetModel getModel() { + return model; + } + + public void setModel(SingleCardWidgetModel model) { + this.model = model; + } + + public Account getAccount() { + return account; + } + + public void setAccount(Account account) { + this.account = account; + } + + public FullCard getFullCard() { + return fullCard; + } + + public void setFullCard(FullCard fullCard) { + this.fullCard = fullCard; + } +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java index 5e7169472..701cdd335 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/ocs/Version.java @@ -80,7 +80,7 @@ public class Version implements Comparable<Version> { if (matcher.find()) { return Integer.parseInt(matcher.group()); } - return 0; + throw new IllegalArgumentException("could not extract a number from following string: \""+containsNumbers+"\""); } @NonNull diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/model/widget/singlecard/SingleCardWidgetModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/model/widget/singlecard/SingleCardWidgetModel.java new file mode 100644 index 000000000..fcaab380b --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/model/widget/singlecard/SingleCardWidgetModel.java @@ -0,0 +1,86 @@ +package it.niedermann.nextcloud.deck.model.widget.singlecard; + +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.Ignore; +import androidx.room.Index; +import androidx.room.PrimaryKey; + +import it.niedermann.nextcloud.deck.model.Account; +import it.niedermann.nextcloud.deck.model.Board; +import it.niedermann.nextcloud.deck.model.Card; + +@Entity( + indices = { + @Index("cardId") + }, + foreignKeys = { + @ForeignKey( + entity = Account.class, + parentColumns = "id", + childColumns = "accountId", onDelete = ForeignKey.CASCADE + ), + @ForeignKey( + entity = Board.class, + parentColumns = "localId", + childColumns = "boardId", onDelete = ForeignKey.CASCADE + ), + @ForeignKey( + entity = Card.class, + parentColumns = "localId", + childColumns = "cardId", onDelete = ForeignKey.CASCADE + ) + } +) +public class SingleCardWidgetModel { + @Ignore + private static final long serialVersionUID = 0; + + @PrimaryKey() + private Integer widgetId; + private Long accountId; + private Long boardId; + private Long cardId; + + public Integer getWidgetId() { + return widgetId; + } + + public void setWidgetId(Integer widgetId) { + this.widgetId = widgetId; + } + + public Long getAccountId() { + return accountId; + } + + public void setAccountId(Long accountId) { + this.accountId = accountId; + } + + public Long getBoardId() { + return boardId; + } + + public void setBoardId(Long boardId) { + this.boardId = boardId; + } + + public Long getCardId() { + return cardId; + } + + public void setCardId(Long cardId) { + this.cardId = cardId; + } + + @Override + public String toString() { + return "SingleCardWidget{" + + "widgetId=" + widgetId + + ", accountId=" + accountId + + ", boardId=" + boardId + + ", cardId=" + cardId + + '}'; + } +} 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 5722f0f1e..29f7725b8 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 @@ -5,6 +5,7 @@ import android.database.sqlite.SQLiteConstraintException; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.WorkerThread; import androidx.core.util.Pair; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -16,6 +17,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.NoSuchElementException; import java.util.concurrent.CountDownLatch; import it.niedermann.nextcloud.deck.DeckLog; @@ -35,6 +37,7 @@ import it.niedermann.nextcloud.deck.model.User; import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.model.full.FullCard; +import it.niedermann.nextcloud.deck.model.full.FullSingleCardWidgetModel; import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.model.internal.FilterInformation; import it.niedermann.nextcloud.deck.model.ocs.Capabilities; @@ -1281,6 +1284,30 @@ public class SyncManager { return liveData; } + // ------------------- + // Widgets + // ------------------- + + /** + * Can be called from a configuration screen or a picker. + * Creates a new entry in the database, if row with given widgetId does not yet exist. + */ + public void addOrUpdateSingleCardWidget(int widgetId, long accountId, long boardId, long localCardId) { + doAsync(() -> dataBaseAdapter.createSingleCardWidget(widgetId, accountId, boardId, localCardId)); + } + + @WorkerThread + public FullSingleCardWidgetModel getSingleCardWidgetModelDirectly(int widgetId) throws NoSuchElementException { + final FullSingleCardWidgetModel model = dataBaseAdapter.getFullSingleCardWidgetModel(widgetId); + if (model == null) { + throw new NoSuchElementException(); + } + return model; + } + + public void deleteSingleCardWidgetModel(int widgetId) { + doAsync(() -> dataBaseAdapter.deleteSingleCardWidget(widgetId)); + } private static class BooleanResultHolder { public boolean result = true; 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 821d910ab..37d2660e5 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 @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.model.AccessControl; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Attachment; @@ -27,6 +28,7 @@ import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.model.enums.EDueType; import it.niedermann.nextcloud.deck.model.full.FullBoard; import it.niedermann.nextcloud.deck.model.full.FullCard; +import it.niedermann.nextcloud.deck.model.full.FullSingleCardWidgetModel; import it.niedermann.nextcloud.deck.model.full.FullStack; import it.niedermann.nextcloud.deck.model.interfaces.AbstractRemoteEntity; import it.niedermann.nextcloud.deck.model.interfaces.IRemoteEntity; @@ -34,9 +36,11 @@ import it.niedermann.nextcloud.deck.model.internal.FilterInformation; import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; +import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; import it.niedermann.nextcloud.deck.model.ocs.comment.full.FullDeckComment; 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.widget.singlecard.SingleCardWidget; import static androidx.lifecycle.Transformations.distinctUntilChanged; @@ -174,7 +178,7 @@ public class DataBaseAdapter { return LiveDataHelper.interceptLiveData(db.getCardDao().getFullCardsForStack(accountId, localStackId), this::filterRelationsForCard); } - List<Object> args = new ArrayList(); + List<Object> args = new ArrayList<>(); StringBuilder query = new StringBuilder("SELECT * FROM card c " + "WHERE accountId = ? AND stackId = ? "); args.add(accountId); @@ -488,6 +492,10 @@ public class DataBaseAdapter { public void updateCard(Card card, boolean setStatus) { markAsEditedIfNeeded(card, setStatus); db.getCardDao().update(card); + if (db.getSingleCardWidgetModelDao().containsCardLocalId(card.getLocalId())) { + DeckLog.info("Notifying widget about card changes for \"" + card.getTitle() + "\""); + SingleCardWidget.notifyDatasetChanged(context); + } } public long createAccessControl(long accountId, AccessControl entity) { @@ -829,4 +837,32 @@ public class DataBaseAdapter { public Long getLocalCommentIdForRemoteIdDirectly(long accountId, Long remoteCommentId) { return db.getCommentDao().getLocalCommentIdForRemoteIdDirectly(accountId, remoteCommentId); } + + + // ------------------- + // Widgets + // ------------------- + + public long createSingleCardWidget(int widgetId, long accountId, long boardLocalId, long cardLocalId) { + SingleCardWidgetModel model = new SingleCardWidgetModel(); + model.setWidgetId(widgetId); + model.setAccountId(accountId); + model.setBoardId(boardLocalId); + model.setCardId(cardLocalId); + return db.getSingleCardWidgetModelDao().insert(model); + } + + public FullSingleCardWidgetModel getFullSingleCardWidgetModel(int widgetId) { + FullSingleCardWidgetModel model = db.getSingleCardWidgetModelDao().getFullCardByRemoteIdDirectly(widgetId); + if (model != null) { + model.setFullCard(db.getCardDao().getFullCardByLocalIdDirectly(model.getAccount().getId(), model.getModel().getCardId())); + } + return model; + } + + public void deleteSingleCardWidget(int widgetId) { + SingleCardWidgetModel model = new SingleCardWidgetModel(); + model.setWidgetId(widgetId); + db.getSingleCardWidgetModelDao().delete(model); + } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java index 3e2b6dec4..a87a60cd2 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/DeckDatabase.java @@ -31,6 +31,7 @@ import it.niedermann.nextcloud.deck.model.enums.DBStatus; import it.niedermann.nextcloud.deck.model.ocs.Activity; import it.niedermann.nextcloud.deck.model.ocs.comment.DeckComment; import it.niedermann.nextcloud.deck.model.ocs.comment.Mention; +import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.AccessControlDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.AccountDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.ActivityDao; @@ -46,6 +47,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.JoinCardWit import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.LabelDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.MentionDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.PermissionDao; +import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.SingleCardWidgetModelDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.StackDao; import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.UserDao; @@ -68,6 +70,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao.UserDao; Activity.class, DeckComment.class, Mention.class, + SingleCardWidgetModel.class, }, exportSchema = false, version = 12 @@ -149,6 +152,14 @@ public abstract class DeckDatabase extends RoomDatabase { private static final Migration MIGRATION_11_12 = new Migration(11, 12) { @Override public void migrate(SupportSQLiteDatabase database) { + database.execSQL("CREATE TABLE `SingleCardWidgetModel` (`widgetId` INTEGER PRIMARY KEY, `accountId` INTEGER, `boardId` INTEGER, `cardId` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE, FOREIGN KEY(`boardId`) REFERENCES `Board`(`localId`) ON UPDATE NO ACTION ON DELETE CASCADE, FOREIGN KEY(`cardId`) REFERENCES `Card`(`localId`) ON UPDATE NO ACTION ON DELETE CASCADE )"); + database.execSQL("CREATE INDEX `index_SingleCardWidgetModel_cardId` ON `SingleCardWidgetModel` (`cardId`)"); + } + }; + + private static final Migration MIGRATION_12_13 = new Migration(11, 12) { + @Override + public void migrate(SupportSQLiteDatabase database) { database.execSQL("ALTER TABLE `DeckComment` ADD `parentId` INTEGER REFERENCES DeckComment(localId) ON DELETE CASCADE"); database.execSQL("CREATE INDEX `idx_comment_parentID` ON DeckComment(parentId)"); } @@ -180,12 +191,14 @@ public abstract class DeckDatabase extends RoomDatabase { .addMigrations(MIGRATION_9_10) .addMigrations(MIGRATION_10_11) .addMigrations(MIGRATION_11_12) + .addMigrations(MIGRATION_12_13) .fallbackToDestructiveMigration() .addCallback(ON_CREATE_CALLBACK) .build(); } public abstract AccountDao getAccountDao(); + public abstract AccessControlDao getAccessControlDao(); public abstract BoardDao getBoardDao(); @@ -217,4 +230,6 @@ public abstract class DeckDatabase extends RoomDatabase { public abstract CommentDao getCommentDao(); public abstract MentionDao getMentionDao(); + + public abstract SingleCardWidgetModelDao getSingleCardWidgetModelDao(); }
\ No newline at end of file diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/CardDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/CardDao.java index e58f2c819..d63f98b6a 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/CardDao.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/CardDao.java @@ -33,7 +33,7 @@ public interface CardDao extends GenericDao<Card> { FullCard getFullCardByLocalIdDirectly(final long accountId, final long localId); @Transaction // v not deleted! - @Query("SELECT * FROM card WHERE accountId = :accountId AND stackId = :localStackId and status<>3 order by `order`, createdAt asc") + @Query("SELECT * FROM card WHERE accountId = :accountId AND archived = 0 AND stackId = :localStackId and status<>3 order by `order`, createdAt asc") LiveData<List<FullCard>> getFullCardsForStack(final long accountId, final long localStackId); @Transaction // v not deleted! diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/SingleCardWidgetModelDao.java b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/SingleCardWidgetModelDao.java new file mode 100644 index 000000000..0c2a485c1 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/persistence/sync/adapters/db/dao/SingleCardWidgetModelDao.java @@ -0,0 +1,20 @@ +package it.niedermann.nextcloud.deck.persistence.sync.adapters.db.dao; + +import androidx.room.Dao; +import androidx.room.Query; +import androidx.room.Transaction; + +import it.niedermann.nextcloud.deck.model.full.FullSingleCardWidgetModel; +import it.niedermann.nextcloud.deck.model.widget.singlecard.SingleCardWidgetModel; + +@Dao +public interface SingleCardWidgetModelDao extends GenericDao<SingleCardWidgetModel> { + + @Transaction + @Query("SELECT * FROM singlecardwidgetmodel WHERE widgetId = :widgetId") + FullSingleCardWidgetModel getFullCardByRemoteIdDirectly(final int widgetId); + + @Transaction + @Query("SELECT EXISTS (SELECT 1 FROM singlecardwidgetmodel WHERE cardId = :cardLocalId)") + boolean containsCardLocalId(final Long cardLocalId); +} 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 f4d744f11..6d2623a91 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 @@ -364,8 +364,12 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener protected void onStop() { // Clean up zombie fragments in case of system initiated process death. // See linked issues in https://github.com/stefan-niedermann/nextcloud-deck/issues/478 - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); + try { + for (Fragment fragment : getSupportFragmentManager().getFragments()) { + getSupportFragmentManager().beginTransaction().remove(fragment).commit(); + } + } catch (IllegalStateException e) { + DeckLog.warn("onSAveInstanceState has already been called."); } super.onStop(); } @@ -411,8 +415,12 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener public void onUpdateStack(long localStackId, String stackName) { observeOnce(syncManager.getStack(mainViewModel.getCurrentAccount().getId(), localStackId), MainActivity.this, fullStack -> { fullStack.getStack().setTitle(stackName); - // TODO error handling - syncManager.updateStack(fullStack); + final WrappedLiveData<FullStack> archiveLiveData = syncManager.updateStack(fullStack); + observeOnce(archiveLiveData, this, (v) -> { + if (archiveLiveData.hasError()) { + ExceptionDialogFragment.newInstance(archiveLiveData.getError(), mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()); + } + }); }); } @@ -974,9 +982,13 @@ public class MainActivity extends BrandedActivity implements DeleteStackListener public void onStackDeleted(Long stackLocalId) { long stackId = stackAdapter.getItem(binding.viewPager.getCurrentItem()).getLocalId(); observeOnce(syncManager.getStack(mainViewModel.getCurrentAccount().getId(), stackId), MainActivity.this, fullStack -> { - DeckLog.log("Delete stack #" + fullStack.getLocalId() + ": " + fullStack.getStack().getTitle()); - // TODO error handling - syncManager.deleteStack(fullStack.getStack()); + DeckLog.info("Delete stack #" + fullStack.getLocalId() + ": " + fullStack.getStack().getTitle()); + final WrappedLiveData<Void> deleteStackLiveData = syncManager.deleteStack(fullStack.getStack()); + observeOnce(deleteStackLiveData, this, (v) -> { + if (deleteStackLiveData.hasError()) { + ExceptionDialogFragment.newInstance(deleteStackLiveData.getError(), mainViewModel.getCurrentAccount()).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()); + } + }); }); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java index e8dd9b8dd..00500ae97 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/archivedcards/ArchivedCardsActvitiy.java @@ -55,7 +55,7 @@ public class ArchivedCardsActvitiy extends BrandedActivity { syncManager = new SyncManager(this); - adapter = new ArchivedCardsAdapter(this, account, boardId, false, syncManager, this); + adapter = new ArchivedCardsAdapter(this, getSupportFragmentManager(), account, boardId, false, syncManager, this); binding.recyclerView.setAdapter(adapter); syncManager.getArchivedFullCardsForBoard(account.getId(), boardId).observe(this, (fullCards) -> { 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 955749dd0..80502ef38 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 @@ -7,6 +7,7 @@ import android.view.View; import android.widget.PopupMenu; import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; import androidx.lifecycle.LifecycleOwner; import org.jetbrains.annotations.NotNull; @@ -21,8 +22,8 @@ import it.niedermann.nextcloud.deck.ui.card.ItemCardViewHolder; public class ArchivedCardsAdapter extends CardAdapter { @SuppressWarnings("WeakerAccess") - public ArchivedCardsAdapter(@NonNull Context context, @NonNull Account account, long boardId, boolean canEdit, @NonNull SyncManager syncManager, @NonNull LifecycleOwner lifecycleOwner) { - super(context, account, boardId, 0L, canEdit, syncManager, lifecycleOwner, null); + public ArchivedCardsAdapter(@NonNull Context context, @NonNull FragmentManager fragmentManager, @NonNull Account account, long boardId, boolean canEdit, @NonNull SyncManager syncManager, @NonNull LifecycleOwner lifecycleOwner) { + super(context, fragmentManager, account, boardId, 0L, canEdit, syncManager, lifecycleOwner, null); } @Override 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 f9b8a46de..15902bd14 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 @@ -14,6 +14,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.drawable.DrawableCompat; +import androidx.fragment.app.FragmentManager; import androidx.lifecycle.LifecycleOwner; import androidx.recyclerview.widget.RecyclerView; @@ -39,16 +40,21 @@ 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.BrandedActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; +import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.util.DateUtil; import it.niedermann.nextcloud.deck.util.ViewUtil; +import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; + public class CardAdapter extends RecyclerView.Adapter<ItemCardViewHolder> implements DragAndDropAdapter<FullCard>, Branded { protected final SyncManager syncManager; + private final FragmentManager fragmentManager; private final Account account; private final long boardId; private final long stackId; @@ -64,8 +70,9 @@ public class CardAdapter extends RecyclerView.Adapter<ItemCardViewHolder> implem private int mainColor; - public CardAdapter(@NonNull Context context, @NonNull Account account, long boardId, long stackId, boolean canEdit, @NonNull SyncManager syncManager, @NonNull LifecycleOwner lifecycleOwner, @Nullable SelectCardListener selectCardListener) { + public CardAdapter(@NonNull Context context, @NonNull FragmentManager fragmentManager, @NonNull Account account, long boardId, long stackId, boolean canEdit, @NonNull SyncManager syncManager, @NonNull LifecycleOwner lifecycleOwner, @Nullable SelectCardListener selectCardListener) { this.context = context; + this.fragmentManager = fragmentManager; this.lifecycleOwner = lifecycleOwner; this.account = account; this.boardId = boardId; @@ -296,13 +303,21 @@ public class CardAdapter extends RecyclerView.Adapter<ItemCardViewHolder> implem return true; } case R.id.action_card_archive: { - // TODO error handling - syncManager.archiveCard(fullCard); + final WrappedLiveData<FullCard> archiveLiveData = syncManager.archiveCard(fullCard); + observeOnce(archiveLiveData, lifecycleOwner, (v) -> { + if (archiveLiveData.hasError()) { + ExceptionDialogFragment.newInstance(archiveLiveData.getError(), account).show(fragmentManager, ExceptionDialogFragment.class.getSimpleName()); + } + }); return true; } case R.id.action_card_delete: { - // TODO error handling - syncManager.deleteCard(fullCard.getCard()); + final WrappedLiveData<Void> deleteLiveData = syncManager.deleteCard(fullCard.getCard()); + observeOnce(deleteLiveData, lifecycleOwner, (v) -> { + if (deleteLiveData.hasError()) { + ExceptionDialogFragment.newInstance(deleteLiveData.getError(), account).show(fragmentManager, ExceptionDialogFragment.class.getSimpleName()); + } + }); return true; } } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java index f800ae546..8cb13a865 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditActivity.java @@ -20,6 +20,7 @@ import androidx.lifecycle.ViewModelProvider; import com.google.android.material.tabs.TabLayout; import com.google.android.material.tabs.TabLayoutMediator; +import it.niedermann.nextcloud.deck.DeckLog; import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityEditBinding; import it.niedermann.nextcloud.deck.model.Account; @@ -29,6 +30,7 @@ import it.niedermann.nextcloud.deck.ui.branding.BrandedAlertDialogBuilder; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; import it.niedermann.nextcloud.deck.util.CardUtil; +import static android.graphics.Color.parseColor; import static it.niedermann.nextcloud.deck.persistence.sync.adapters.db.util.LiveDataHelper.observeOnce; public class EditActivity extends BrandedActivity { @@ -72,14 +74,26 @@ public class EditActivity extends BrandedActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Thread.currentThread().setUncaughtExceptionHandler(new ExceptionHandler(this)); - binding = ActivityEditBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); - setSupportActionBar(binding.toolbar); + viewModel = new ViewModelProvider(this).get(EditCardViewModel.class); + syncManager = new SyncManager(this); + + loadDataFromIntent(); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + setIntent(intent); + loadDataFromIntent(); + applyBrand(parseColor(viewModel.getAccount().getColor()), parseColor(viewModel.getAccount().getTextColor())); + } + + private void loadDataFromIntent() { final Bundle args = getIntent().getExtras(); if (args == null || !args.containsKey(BUNDLE_KEY_ACCOUNT) || !args.containsKey(BUNDLE_KEY_BOARD_ID)) { @@ -88,9 +102,6 @@ public class EditActivity extends BrandedActivity { long cardId = args.getLong(BUNDLE_KEY_CARD_ID); - viewModel = new ViewModelProvider(this).get(EditCardViewModel.class); - syncManager = new SyncManager(this); - if (cardId == 0L) { viewModel.setCreateMode(true); if (!args.containsKey(BUNDLE_KEY_STACK_ID)) { @@ -102,13 +113,16 @@ public class EditActivity extends BrandedActivity { if (account == null) { throw new IllegalArgumentException(BUNDLE_KEY_ACCOUNT + " must not be null."); } + viewModel.setAccount(account); + final long boardId = args.getLong(BUNDLE_KEY_BOARD_ID); observeOnce(syncManager.getFullBoardById(account.getId(), boardId), EditActivity.this, (fullBoard -> { viewModel.setCanEdit(fullBoard.getBoard().isPermissionEdit()); invalidateOptionsMenu(); if (viewModel.isCreateMode()) { - viewModel.initializeNewCard(account, boardId, args.getLong(BUNDLE_KEY_STACK_ID), account.getServerDeckVersionAsObject().isSupported(this)); + viewModel.initializeNewCard(boardId, args.getLong(BUNDLE_KEY_STACK_ID), account.getServerDeckVersionAsObject().isSupported(this)); + invalidateOptionsMenu(); String title = args.getString(BUNDLE_KEY_TITLE); if (!TextUtils.isEmpty(title)) { if (title.length() > viewModel.getAccount().getServerDeckVersionAsObject().getCardTitleMaxLength()) { @@ -121,9 +135,18 @@ public class EditActivity extends BrandedActivity { setupTitle(); } else { observeOnce(syncManager.getCardByLocalId(account.getId(), cardId), EditActivity.this, (fullCard) -> { - viewModel.initializeExistingCard(account, boardId, fullCard, account.getServerDeckVersionAsObject().isSupported(this)); - setupViewPager(); - setupTitle(); + if (fullCard == null) { + new BrandedAlertDialogBuilder(this) + .setTitle(R.string.card_not_found) + .setMessage(R.string.card_not_found_message) + .setPositiveButton(R.string.simple_close, (a, b) -> super.finish()) + .show(); + } else { + viewModel.initializeExistingCard(boardId, fullCard, account.getServerDeckVersionAsObject().isSupported(this)); + invalidateOptionsMenu(); + setupViewPager(); + setupTitle(); + } }); } })); @@ -264,8 +287,12 @@ public class EditActivity extends BrandedActivity { protected void onStop() { // Clean up zombie fragments in case of system initiated process death. // See linked issues in https://github.com/stefan-niedermann/nextcloud-deck/issues/478 - for (Fragment fragment : getSupportFragmentManager().getFragments()) { - getSupportFragmentManager().beginTransaction().remove(fragment).commit(); + try { + for (Fragment fragment : getSupportFragmentManager().getFragments()) { + getSupportFragmentManager().beginTransaction().remove(fragment).commit(); + } + } catch (IllegalStateException e) { + DeckLog.warn("onSAveInstanceState has already been called."); } super.onStop(); } diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java index 66ec0d5d8..490c1d57d 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/card/EditCardViewModel.java @@ -25,27 +25,23 @@ public class EditCardViewModel extends ViewModel { /** * Stores a deep copy of the given fullCard to be able to compare the state at every time in #{@link EditCardViewModel#hasChanges()} * - * @param account Must not be null * @param boardId Local ID, expecting a positive long value * @param fullCard The card that is currently edited */ - public void initializeExistingCard(@NonNull Account account, long boardId, @NonNull FullCard fullCard, boolean isSupportedVersion) { - this.account = account; + public void initializeExistingCard(long boardId, @NonNull FullCard fullCard, boolean isSupportedVersion) { this.boardId = boardId; this.fullCard = fullCard; this.originalCard = new FullCard(this.fullCard); this.isSupportedVersion = isSupportedVersion; - hasCommentsAbility = account.getServerDeckVersionAsObject().supportsComments(); } /** * Stores a deep copy of the given fullCard to be able to compare the state at every time in #{@link EditCardViewModel#hasChanges()} * - * @param account Must not be null * @param boardId Local ID, expecting a positive long value * @param stackId Local ID, expecting a positive long value where the card should be created */ - public void initializeNewCard(@NonNull Account account, long boardId, long stackId, boolean isSupportedVersion) { + public void initializeNewCard(long boardId, long stackId, boolean isSupportedVersion) { final FullCard fullCard = new FullCard(); fullCard.setLabels(new ArrayList<>()); fullCard.setAssignedUsers(new ArrayList<>()); @@ -53,7 +49,12 @@ public class EditCardViewModel extends ViewModel { final Card card = new Card(); card.setStackId(stackId); fullCard.setCard(card); - initializeExistingCard(account, boardId, fullCard, isSupportedVersion); + initializeExistingCard(boardId, fullCard, isSupportedVersion); + } + + public void setAccount(@NonNull Account account) { + this.account = account; + hasCommentsAbility = account.getServerDeckVersionAsObject().supportsComments(); } public boolean hasChanges() { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java index 65a0d726d..b2e9cb012 100644 --- a/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/preparecreate/PrepareCreateActivity.java @@ -5,7 +5,6 @@ import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.widget.ArrayAdapter; -import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -16,7 +15,6 @@ import java.util.List; import it.niedermann.nextcloud.deck.Application; import it.niedermann.nextcloud.deck.DeckLog; -import it.niedermann.nextcloud.deck.R; import it.niedermann.nextcloud.deck.databinding.ActivityPrepareCreateBinding; import it.niedermann.nextcloud.deck.model.Account; import it.niedermann.nextcloud.deck.model.Board; @@ -25,6 +23,7 @@ import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; import it.niedermann.nextcloud.deck.ui.ImportAccountActivity; import it.niedermann.nextcloud.deck.ui.branding.BrandedActivity; import it.niedermann.nextcloud.deck.ui.card.EditActivity; +import it.niedermann.nextcloud.deck.ui.exception.ExceptionDialogFragment; import it.niedermann.nextcloud.deck.ui.exception.ExceptionHandler; import static android.graphics.Color.parseColor; @@ -189,9 +188,7 @@ public class PrepareCreateActivity extends BrandedActivity { finish(); } else { - // TODO Use snackbar for better error handling - DeckLog.error("Selected account at position " + binding.accountSelect.getSelectedItemPosition() + " is null."); - Toast.makeText(this, R.string.error, Toast.LENGTH_LONG).show(); + ExceptionDialogFragment.newInstance(new IllegalStateException("Selected account at position " + binding.accountSelect.getSelectedItemPosition() + " is null."), null).show(getSupportFragmentManager(), ExceptionDialogFragment.class.getSimpleName()); } } 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 def22c3f7..7b43d130c 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 @@ -66,7 +66,7 @@ public class StackFragment extends BrandedFragment implements DragAndDropTab<Car syncManager = new SyncManager(activity); - adapter = new CardAdapter(requireContext(), mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardLocalId(), stackId, mainViewModel.currentBoardHasEditPermission(), syncManager, this, (requireActivity() instanceof SelectCardListener) ? (SelectCardListener) requireActivity() : null); + adapter = new CardAdapter(requireContext(), getChildFragmentManager(), mainViewModel.getCurrentAccount(), mainViewModel.getCurrentBoardLocalId(), stackId, mainViewModel.currentBoardHasEditPermission(), syncManager, this, (requireActivity() instanceof SelectCardListener) ? (SelectCardListener) requireActivity() : null); binding.recyclerView.setAdapter(adapter); if (onScrollListener != null) { diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java new file mode 100644 index 000000000..e87e9c955 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SelectCardForWidgetActivity.java @@ -0,0 +1,67 @@ +package it.niedermann.nextcloud.deck.ui.widget.singlecard; + +import android.appwidget.AppWidgetManager; +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.View; + +import androidx.annotation.NonNull; + +import it.niedermann.nextcloud.deck.R; +import it.niedermann.nextcloud.deck.model.Board; +import it.niedermann.nextcloud.deck.model.full.FullCard; +import it.niedermann.nextcloud.deck.ui.MainActivity; +import it.niedermann.nextcloud.deck.ui.card.SelectCardListener; + +public class SelectCardForWidgetActivity extends MainActivity implements SelectCardListener { + + private int appWidgetId; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + final Intent intent = getIntent(); + if (intent == null) { + finish(); + return; + } + final Bundle args = intent.getExtras(); + if (args == null) { + finish(); + return; + } + appWidgetId = args.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { + finish(); + } + } + + @Override + public void onCardSelected(FullCard fullCard) { + syncManager.addOrUpdateSingleCardWidget(appWidgetId, mainViewModel.getCurrentAccount().getId(), mainViewModel.getCurrentBoardLocalId(), fullCard.getLocalId()); + final Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, + getApplicationContext(), SingleCardWidget.class) + .putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); + setResult(RESULT_OK, updateIntent); + getApplicationContext().sendBroadcast(updateIntent); + finish(); + } + + @Override + protected void setCurrentBoard(@NonNull Board board) { + super.setCurrentBoard(board); + binding.addStackButton.setVisibility(View.GONE); + binding.fab.setVisibility(View.GONE); + binding.toolbar.setTitle(R.string.simple_select); + } + + @Override + protected void showFabIfEditPermissionGranted() { /* Silence is gold */ } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return true; + } + +} diff --git a/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java new file mode 100644 index 000000000..783f98e00 --- /dev/null +++ b/app/src/main/java/it/niedermann/nextcloud/deck/ui/widget/singlecard/SingleCardWidget.java @@ -0,0 +1,153 @@ +package it.niedermann.nextcloud.deck.ui.widget.singlecard; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.view.View; +import android.widget.RemoteViews; + +import androidx.annotation.DrawableRes; +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; + +import java.util.NoSuchElementException; + +import it.niedermann.nextcloud.deck.R; +import it.niedermann.nextcloud.deck.model.Card; +import it.niedermann.nextcloud.deck.model.full.FullSingleCardWidgetModel; +import it.niedermann.nextcloud.deck.persistence.sync.SyncManager; +import it.niedermann.nextcloud.deck.ui.card.EditActivity; +import it.niedermann.nextcloud.deck.util.DateUtil; + +public class SingleCardWidget extends AppWidgetProvider { + + void updateAppWidget(Context context, AppWidgetManager awm, int[] appWidgetIds) { + final SyncManager syncManager = new SyncManager(context); + + for (int appWidgetId : appWidgetIds) { + new Thread(() -> { + try { + final FullSingleCardWidgetModel fullModel = syncManager.getSingleCardWidgetModelDirectly(appWidgetId); + + final Intent intent = EditActivity.createEditCardIntent(context, fullModel.getAccount(), fullModel.getModel().getBoardId(), fullModel.getFullCard().getLocalId()); + final PendingIntent pendingIntent = PendingIntent.getActivity(context, appWidgetId, intent, PendingIntent.FLAG_UPDATE_CURRENT); + final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_single_card); + + views.setOnClickPendingIntent(R.id.widget_card, pendingIntent); + + views.setTextViewText(R.id.title, fullModel.getFullCard().getCard().getTitle()); + views.setTextViewText(R.id.description, fullModel.getFullCard().getCard().getDescription()); + + if (fullModel.getFullCard().getCard().getDueDate() != null) { + views.setTextViewText(R.id.card_due_date, DateUtil.getRelativeDateTimeString(context, fullModel.getFullCard().getCard().getDueDate().getTime())); + // TODO Use multiple views for background colors and only set the necessary to View.VISIBLE + // https://stackoverflow.com/a/3376537 + // Because otherwise using Reflection is the only way + views.setViewVisibility(R.id.card_due_date, View.VISIBLE); + views.setViewVisibility(R.id.card_due_date_image, View.VISIBLE); + setImageDrawable(views, R.id.card_due_date_image, R.drawable.calendar_blank_grey600_24dp); + } else { + views.setViewVisibility(R.id.card_due_date, View.GONE); + views.setViewVisibility(R.id.card_due_date_image, View.GONE); + } + + + final String counterMaxValue = context.getString(R.string.counter_max_value); + + final int attachmentsCount = fullModel.getFullCard().getAttachments().size(); + if (attachmentsCount == 0) { + views.setViewVisibility(R.id.card_count_attachments, View.GONE); + views.setViewVisibility(R.id.card_count_attachments_image, View.GONE); + } else { + views.setViewVisibility(R.id.card_count_attachments, View.VISIBLE); + views.setViewVisibility(R.id.card_count_attachments_image, View.VISIBLE); + setupCounter(views, R.id.card_count_attachments, attachmentsCount, counterMaxValue); + setImageDrawable(views, R.id.card_count_attachments_image, R.drawable.ic_check_grey600_24dp); + } + + final int commentsCount = fullModel.getFullCard().getCommentCount(); + if (commentsCount == 0) { + views.setViewVisibility(R.id.card_count_comments, View.GONE); + views.setViewVisibility(R.id.card_count_comments_image, View.GONE); + } else { + views.setViewVisibility(R.id.card_count_comments, View.VISIBLE); + views.setViewVisibility(R.id.card_count_comments_image, View.VISIBLE); + setupCounter(views, R.id.card_count_comments, commentsCount, counterMaxValue); + setImageDrawable(views, R.id.card_count_comments_image, R.drawable.ic_comment_white_24dp); + } + + final Card.TaskStatus taskStatus = fullModel.getFullCard().getCard().getTaskStatus(); + if (taskStatus.taskCount > 0) { + views.setViewVisibility(R.id.card_count_tasks, View.VISIBLE); + views.setViewVisibility(R.id.card_count_tasks_image, View.VISIBLE); + views.setTextViewText(R.id.card_count_tasks, context.getResources().getString(R.string.task_count, String.valueOf(taskStatus.doneCount), String.valueOf(taskStatus.taskCount))); + setImageDrawable(views, R.id.card_count_tasks_image, R.drawable.ic_attach_file_grey600_24dp); + } else { + views.setViewVisibility(R.id.card_count_tasks, View.GONE); + views.setViewVisibility(R.id.card_count_tasks_image, View.GONE); + } + + awm.updateAppWidget(appWidgetId, views); + } catch (NoSuchElementException e) { + // onUpdate has been triggered before the user finished configuring the widget + } + }).start(); + } + } + + private static void setupCounter(@NonNull RemoteViews views, @IdRes int textViewId, int count, String counterMaxValue) { + if (count > 99) { + views.setTextViewText(textViewId, counterMaxValue); + } else if (count > 1 || Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + views.setTextViewText(textViewId, String.valueOf(count)); + } else if (count == 1) { + views.setTextViewText(textViewId, ""); + } + } + + private static void setImageDrawable(@NonNull RemoteViews views, @IdRes int imageView, @DrawableRes int image) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + views.setImageViewResource(imageView, image); + } + } + + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + super.onUpdate(context, appWidgetManager, appWidgetIds); + updateAppWidget(context, appWidgetManager, appWidgetIds); + } + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + AppWidgetManager awm = AppWidgetManager.getInstance(context); + + updateAppWidget(context, AppWidgetManager.getInstance(context), + (awm.getAppWidgetIds(new ComponentName(context, SingleCardWidget.class)))); + } + + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + final SyncManager syncManager = new SyncManager(context); + + for (int appWidgetId : appWidgetIds) { + syncManager.deleteSingleCardWidgetModel(appWidgetId); + } + + super.onDeleted(context, appWidgetIds); + } + + + /** + * Updates UI data of all {@link SingleCardWidget} instances + */ + public static void notifyDatasetChanged(Context context) { + Intent intent = new Intent(context, SingleCardWidget.class); + intent.setAction("android.appwidget.action.APPWIDGET_UPDATE"); + context.sendBroadcast(intent); + } +} |