From a811f84117dbb2642e14ac6793285389eed34183 Mon Sep 17 00:00:00 2001 From: Prateek Singh Date: Sun, 15 Aug 2021 01:38:55 +0530 Subject: [PATCH] Decoupled Notification and Widget --- .../src/main/java/com/ichi2/anki/MetaDB.java | 18 +++-- .../anki/services/NotificationService.java | 5 +- .../src/main/java/com/ichi2/libanki/Deck.java | 45 ++++++++++- .../java/com/ichi2/libanki/DecksMetaData.java | 63 ++++++++++++++++ .../java/com/ichi2/widget/WidgetStatus.java | 75 +++---------------- 5 files changed, 132 insertions(+), 74 deletions(-) create mode 100644 AnkiDroid/src/main/java/com/ichi2/libanki/DecksMetaData.java diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/MetaDB.java b/AnkiDroid/src/main/java/com/ichi2/anki/MetaDB.java index 07422ea6b79f..82c7e306deb7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/MetaDB.java +++ b/AnkiDroid/src/main/java/com/ichi2/anki/MetaDB.java @@ -438,12 +438,12 @@ public static void storeLookupDictionary(Context context, long did, int dictiona } } - /** * Return the current status of the widget. - * - * @return [due, eta] - */ + * @return [due, eta]
+ * Widget Status table will be removed from database in future. + * */ + @Deprecated public static int[] getWidgetSmallStatus(Context context) { openDBIfClosed(context); Cursor cursor = null; @@ -463,7 +463,10 @@ public static int[] getWidgetSmallStatus(Context context) { return new int[]{0, 0}; } - + /** + * Widget Status table will be removed from database in future. + * */ + @Deprecated public static int getNotificationStatus(Context context) { openDBIfClosed(context); Cursor cursor = null; @@ -483,7 +486,10 @@ public static int getNotificationStatus(Context context) { return due; } - + /** + * Widget Status table will be removed from database in future. + * */ + @Deprecated public static void storeSmallWidgetStatus(Context context, Pair status) { openDBIfClosed(context); try { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.java b/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.java index 5b60ec60f617..f7a1dfa01d41 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.java +++ b/AnkiDroid/src/main/java/com/ichi2/anki/services/NotificationService.java @@ -30,6 +30,7 @@ import com.ichi2.anki.Preferences; import com.ichi2.anki.R; import com.ichi2.compat.CompatHelper; +import com.ichi2.libanki.DecksMetaData; import com.ichi2.widget.WidgetStatus; import timber.log.Timber; @@ -50,7 +51,7 @@ public void onReceive(Context context, Intent intent) { SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(context); int minCardsDue = Integer.parseInt(preferences.getString(MINIMUM_CARDS_DUE_FOR_NOTIFICATION, Integer.toString(Preferences.PENDING_NOTIFICATIONS_ONLY))); - int dueCardsCount = WidgetStatus.fetchDue(context); + int dueCardsCount = new DecksMetaData(context).getTotalDueCards().first; if (dueCardsCount >= minCardsDue) { // Build basic notification String cardsDueText = context.getResources() @@ -86,4 +87,4 @@ public void onReceive(Context context, Intent intent) { manager.cancel(WIDGET_NOTIFY_ID); } } -} +} \ No newline at end of file diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Deck.java b/AnkiDroid/src/main/java/com/ichi2/libanki/Deck.java index a55d6c97dc8e..53e537a54923 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Deck.java +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Deck.java @@ -62,4 +62,47 @@ public boolean isDyn() { public boolean isStd() { return getInt("dyn") == Consts.DECK_STD; } -} + + /** + * Time when deck is last modified. + * @return time in epoch + * */ + public Long lastModified() { + return getLong("mod"); + } + + /** + * Retrieve all the learning card from the deck. + * @return Integer Array of size 2. index 0 is number of days that have passed between the collection was created
+ * index 1 number of cards seen today in this deck minus the number of new cards + * */ + public String[] lrnCards() { + return new String[] { getJSONArray("lrnToday").get(0).toString(), getJSONArray("lrnToday").get(1).toString()}; + } + + /** + * Retrieve all the new card from the deck. + * @return Integer Array of size 2. index 0 is number of days that have passed between the collection was created
+ * index 1 number of cards seen today in this deck minus the number of new cards + * */ + public String[] newCards() { + return new String[] { getJSONArray("newToday").get(0).toString(), getJSONArray("newToday").get(1).toString()}; + } + + /** + * Retrieve all the revision card from the deck. + * @return Integer Array of size 2. index 0 is number of days that have passed between the collection was created
+ * index 1 number of cards seen today in this deck minus the number of new cards + * */ + public String[] revCards() { + return new String[] {getJSONArray("revToday").get(0).toString(), getJSONArray("revToday").get(1).toString()}; + } + + /** + * Name of the deck. + * @return deck name + * */ + public String deckName() { + return getString("name"); + } +} \ No newline at end of file diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/DecksMetaData.java b/AnkiDroid/src/main/java/com/ichi2/libanki/DecksMetaData.java new file mode 100644 index 000000000000..73397e9d43d7 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/DecksMetaData.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2021 Prateek Singh + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.ichi2.libanki; + +import android.content.Context; +import android.util.Log; +import android.util.Pair; +import android.widget.Toast; + +import com.ichi2.anki.CollectionHelper; +import com.ichi2.libanki.sched.Counts; +import com.ichi2.libanki.sched.DeckDueTreeNode; + +import java.util.Arrays; +import java.util.List; + +public class DecksMetaData { + + private final Decks mDecks; + private final Collection mCollection; + + public DecksMetaData(Context context) { + mCollection = CollectionHelper.getInstance().getCol(context); + mDecks = mCollection.getDecks(); + } + + /** + * Total number of due cards and their expected time to compete. + * @return Pair of Total Due Cards and Expected time to complete.
+ * first is due cards.
+ * second is Expected time. + */ + public Pair getTotalDueCards() { + Counts total = new Counts(); + // Ensure queues are reset if we cross over to the next day. + mCollection.getSched()._checkDay(); + + // Only count the top-level decks in the total + List nodes = mCollection.getSched().deckDueTree(); + for (DeckDueTreeNode node : nodes) { + total.addNew(node.getNewCount()); + total.addLrn(node.getLrnCount()); + total.addRev(node.getRevCount()); + } + int eta = mCollection.getSched().eta(total, false); + Log.d("AABB", "getAllDeckNames: " + total.count() + " " + eta); + return new Pair<>(total.count(), eta); + } +} \ No newline at end of file diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/WidgetStatus.java b/AnkiDroid/src/main/java/com/ichi2/widget/WidgetStatus.java index 04f8f7252b48..e9849ecf236b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/WidgetStatus.java +++ b/AnkiDroid/src/main/java/com/ichi2/widget/WidgetStatus.java @@ -15,27 +15,15 @@ package com.ichi2.widget; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; import android.util.Pair; -import androidx.localbroadcastmanager.content.LocalBroadcastManager; - import com.ichi2.anki.AnkiDroidApp; -import com.ichi2.anki.CollectionHelper; -import com.ichi2.anki.MetaDB; -import com.ichi2.anki.services.NotificationService; import com.ichi2.async.BaseAsyncTask; -import com.ichi2.libanki.Collection; -import com.ichi2.libanki.sched.Counts; -import com.ichi2.libanki.sched.DeckDueTreeNode; - -import java.util.List; +import com.ichi2.libanki.DecksMetaData; import timber.log.Timber; -import static com.ichi2.anki.Preferences.MINIMUM_CARDS_DUE_FOR_NOTIFICATION; - /** * The status of the widget. */ @@ -61,9 +49,8 @@ private WidgetStatus() { public static void update(Context context) { SharedPreferences preferences = AnkiDroidApp.getSharedPrefs(context); sSmallWidgetEnabled = preferences.getBoolean("widgetSmallEnabled", false); - boolean notificationEnabled = Integer.parseInt(preferences.getString(MINIMUM_CARDS_DUE_FOR_NOTIFICATION, "1000001")) < 1000000; boolean canExecuteTask = ((sUpdateDeckStatusAsyncTask == null) || (sUpdateDeckStatusAsyncTask.getStatus() == android.os.AsyncTask.Status.FINISHED)); - if ((sSmallWidgetEnabled || notificationEnabled) && canExecuteTask) { + if (sSmallWidgetEnabled && canExecuteTask) { Timber.d("WidgetStatus.update(): updating"); sUpdateDeckStatusAsyncTask = new UpdateDeckStatusAsyncTask(); sUpdateDeckStatusAsyncTask.execute(context); @@ -73,68 +60,26 @@ public static void update(Context context) { } - /** Returns the status of each of the decks. */ + /** Returns the card due and eta of all the due decks. */ public static int[] fetchSmall(Context context) { - return MetaDB.getWidgetSmallStatus(context); - } - - - public static int fetchDue(Context context) { - return MetaDB.getNotificationStatus(context); + DecksMetaData metaData = new DecksMetaData(context); + Pair pair = metaData.getTotalDueCards(); + return new int[] {pair.first, pair.second}; } - private static class UpdateDeckStatusAsyncTask extends BaseAsyncTask { - // due, eta - private static Pair sSmallWidgetStatus = new Pair<>(0, 0); - @Override protected Context doInBackground(Context... params) { super.doInBackground(params); Timber.d("WidgetStatus.UpdateDeckStatusAsyncTask.doInBackground()"); Context context = params[0]; - if (!AnkiDroidApp.isSdCardMounted()) { + if (!AnkiDroidApp.isSdCardMounted() && sSmallWidgetEnabled) { return context; } - try { - updateCounts(context); - } catch (Exception e) { - Timber.e(e, "Could not update widget"); - } - return context; - } - - - @Override - protected void onPostExecute(Context context) { - super.onPostExecute(context); - Timber.d("WidgetStatus.UpdateDeckStatusAsyncTask.onPostExecute()"); - MetaDB.storeSmallWidgetStatus(context, sSmallWidgetStatus); - if (sSmallWidgetEnabled) { - new AnkiDroidWidgetSmall.UpdateService().doUpdate(context); - } - Intent intent = new Intent(NotificationService.INTENT_ACTION); - Context appContext = context.getApplicationContext(); - LocalBroadcastManager.getInstance(appContext).sendBroadcast(intent); - } - - - private void updateCounts(Context context) { - Counts total = new Counts(); - Collection col = CollectionHelper.getInstance().getCol(context); - // Ensure queues are reset if we cross over to the next day. - col.getSched()._checkDay(); + new AnkiDroidWidgetSmall.UpdateService().doUpdate(context); - // Only count the top-level decks in the total - List nodes = col.getSched().deckDueTree(); - for (DeckDueTreeNode node : nodes) { - total.addNew(node.getNewCount()); - total.addLrn(node.getLrnCount()); - total.addRev(node.getRevCount()); - } - int eta = col.getSched().eta(total, false); - sSmallWidgetStatus = new Pair<>(total.count(), eta); + return context; } } -} +} \ No newline at end of file