diff --git a/AnkiDroid/build.gradle b/AnkiDroid/build.gradle
index d22c131cb026..44f4b2d57429 100644
--- a/AnkiDroid/build.gradle
+++ b/AnkiDroid/build.gradle
@@ -298,6 +298,7 @@ dependencies {
implementation 'com.drakeet.drawer:drawer:1.0.3'
implementation 'uk.co.samuelwall:material-tap-target-prompt:3.3.2'
implementation 'com.github.mrudultora:Colorpicker:1.2.0'
+ implementation "androidx.work:work-runtime-ktx:2.7.1"
// Cannot use debugImplementation since classes need to be imported in AnkiDroidApp
// and there's no no-op version for release build. Usage has been disabled for release
@@ -328,11 +329,5 @@ dependencies {
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test:rules:1.4.0'
-
- // GSON
- implementation 'com.google.code.gson:gson:2.9.0'
-
- // Work Manager
- implementation "androidx.work:work-runtime-ktx:2.7.1"
}
apply from: "./kotlinMigration.gradle"
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.java b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.java
index a36719d2caeb..ae05edd4be76 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.java
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.java
@@ -426,9 +426,9 @@ public static String getWebViewErrorMessage() {
* This function setups the work manager which run periodically.
**/
private void setupDeckMetaDataWorker() {
- Timber.tag("META").e("Setting up deck meta data worker...");
+ Timber.d("Setting up deck meta data worker...");
- PeriodicWorkRequest deckMetaDataWorker = new PeriodicWorkRequest.Builder(DeckMetaDataWorker.class, 2, TimeUnit.MINUTES)
+ PeriodicWorkRequest deckMetaDataWorker = new PeriodicWorkRequest.Builder(DeckMetaDataWorker.class, 30, TimeUnit.MINUTES)
.addTag(DeckMetaDataWorker.DECK_META_WORKER)
.build();
@@ -438,6 +438,10 @@ private void setupDeckMetaDataWorker() {
ExistingPeriodicWorkPolicy.REPLACE,
deckMetaDataWorker
);
+
+ // Does the required work in setting up new Worker.
+ String time = CollectionHelper.getInstance().getTimeSafe(this).getCurrentDate().toString();
+ DeckMetaDataWorker.Companion.setupNewWorker(this, time);
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckMetaDataPreference.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckMetaDataPreference.kt
index b10cea04c7b5..7cdbaa2e5633 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckMetaDataPreference.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckMetaDataPreference.kt
@@ -18,33 +18,93 @@ package com.ichi2.anki
import android.content.Context
import android.content.SharedPreferences
-import com.google.gson.Gson
+import androidx.core.content.edit
import com.ichi2.anki.worker.DeckMetaDataWorker
+import com.ichi2.utils.JSONException
+import com.ichi2.utils.JSONObject
import timber.log.Timber
+import java.lang.NumberFormatException
class DeckMetaDataPreference(context: Context) {
private val sharedPreferences: SharedPreferences =
context.getSharedPreferences("DeckMetaData", Context.MODE_PRIVATE)
+ /**
+ * Use this to store data in shared preference.
+ * @param key The Key of value. Used in fetching the data.
+ * @param value Value that needs to be stored (VALUE MUST BE STRING).
+ * */
fun putString(key: String, value: String) {
- sharedPreferences.edit()
- .putString(key, value)
- .apply()
+ sharedPreferences.edit {
+ putString(key, value)
+ apply()
+ }
}
+ /**
+ * Use this to fetch string from shared preference.
+ * @prams The Key of deck whose data you want to fetch.
+ * @return Value that needs to be fetched (VALUE WILL BE STRING).
+ * */
fun getString(key: String, default: String): String {
return sharedPreferences.getString(key, default)!!
}
+ /**
+ * Use this to store int in shared preference.
+ * @param key The Key of value. Created while storing the data.
+ * @param value Value that needs to be stored (VALUE MUST BE INTEGER).
+ * */
+ fun putInt(key: String, value: Int) {
+ sharedPreferences.edit {
+ putInt(key, value)
+ apply()
+ }
+ }
+
+ /**
+ * Use this to fetch integer from shared preference.
+ * @prams The Key of deck whose data you want to fetch.
+ * @return Value that needs to be fetched (VALUE WILL BE INTEGER).
+ * */
+ fun getInt(key: String, default: Int): Int {
+ return sharedPreferences.getInt(key, default)
+ }
+
+ /**
+ * Use this to store meta data in shared preference.
+ * @param key The Key of deck whose data you want to fetch. (deck id is the for deck)
+ * @param value Object of Metadata Model
+ * */
fun setMetaData(key: String, value: DeckMetaDataWorker.Meta) {
- val json: String = Gson().toJson(value)
- sharedPreferences.edit()
- .putString(key, json)
- .apply()
- Timber.tag("META").e(json)
+ val jsonObject = JSONObject()
+
+ try {
+ jsonObject.put("did", value.did)
+ jsonObject.put("deckName", value.deckName)
+ jsonObject.put("new", value.new)
+ jsonObject.put("lrn", value.lrn)
+ jsonObject.put("rev", value.rev)
+ jsonObject.put("eta", value.eta)
+ } catch (e: JSONException) {
+ Timber.d(e)
+ return
+ }
+
+ val json = jsonObject.toString()
+
+ sharedPreferences.edit {
+ putString(key, json)
+ apply()
+ }
}
+ /**
+ * Use this to fetch meta data from shared preference if Deck ID (did) is known.
+ * @prams The Key of deck whose data you want to fetch. (deck id is the for deck)
+ * @return Object of Metadata Model.
+ * */
fun getMetaData(key: String): DeckMetaDataWorker.Meta? {
val jsonData = sharedPreferences
.getString(key, null)
@@ -52,7 +112,35 @@ class DeckMetaDataPreference(context: Context) {
return if (jsonData == null) {
null
} else {
- Gson().fromJson(jsonData, DeckMetaDataWorker.Meta::class.java)
+ val jsonObject = JSONObject(jsonData)
+ return DeckMetaDataWorker.Meta(
+ jsonObject.getLong("did"),
+ jsonObject.getString("deckName"),
+ jsonObject.getInt("new"),
+ jsonObject.getInt("lrn"),
+ jsonObject.getInt("rev"),
+ jsonObject.getInt("eta"),
+ )
+ }
+ }
+
+ /**
+ * Use this to fetch all meta data from shared preference.
+ * @return List of all Meta Data Object.
+ * */
+ fun getAllMetaData(): List {
+ val listMeta = mutableListOf()
+ val dids = sharedPreferences.all.keys.filter { data -> data.isLong() }
+ dids.forEach {
+ listMeta.add(getMetaData(it)!!)
}
+ return listMeta
+ }
+
+ private fun String.isLong(): Boolean = try {
+ this.toLong()
+ true
+ } catch (e: NumberFormatException) {
+ false
}
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/worker/DeckMetaDataWorker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/worker/DeckMetaDataWorker.kt
index fb4a53bca815..a7d593c1ace1 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/worker/DeckMetaDataWorker.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/worker/DeckMetaDataWorker.kt
@@ -21,7 +21,9 @@ import androidx.work.Worker
import androidx.work.WorkerParameters
import com.ichi2.anki.CollectionHelper
import com.ichi2.anki.DeckMetaDataPreference
+import com.ichi2.libanki.Collection
import com.ichi2.libanki.sched.Counts
+import com.ichi2.libanki.sched.DeckDueTreeNode
import timber.log.Timber
/**
@@ -31,34 +33,42 @@ import timber.log.Timber
* */
class DeckMetaDataWorker(val context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) {
- private lateinit var colHelper: CollectionHelper
+ // Lambda to check the collection is not null.
+ private val mCollection: (context: Context) -> Collection? = {
+ val collection = CollectionHelper.getInstance()
+ if (collection.getColSafe(it) != null) {
+ collection.getCol(it)
+ } else {
+ null
+ }
+ }
override fun doWork(): Result {
- colHelper = CollectionHelper.getInstance()
+ val date = CollectionHelper.getInstance().getTimeSafe(context).currentDate
- Timber.tag("META").e("Deck Meta Data Worker started at: ${colHelper.getTimeSafe(context).currentDate}")
+ Timber.d("Deck Meta Data Worker started at: $date")
// Update the data of Deck Metadata from HERE
- val col = colHelper.getCol(context)
- val deckList = col.sched.deckDueList()
+ val sched = mCollection(context)?.sched ?: return Result.failure()
+ val deckList: List = sched.deckDueList()
deckList.forEach {
- val new = it?.newCount ?: -1
- val lrn = it?.lrnCount ?: -1
- val rev = it?.revCount ?: -1
+ val new = it.newCount
+ val lrn = it.lrnCount
+ val rev = it.revCount
val data = Meta(
- it?.did ?: -1,
- it?.fullDeckName ?: "Deck Name",
+ it.did,
+ it.fullDeckName,
new,
lrn,
rev,
- col.sched.eta(Counts(new, lrn, rev), false)
+ sched.eta(Counts(new, lrn, rev), false)
)
storeInPreference(data)
}
- updateLastFetch(colHelper.getTimeSafe(context).toString())
+ updateLastFetch(date.toString())
return Result.success() // Done work successfully...
}
@@ -66,6 +76,8 @@ class DeckMetaDataWorker(val context: Context, workerParameters: WorkerParameter
private fun updateLastFetch(time: String) {
val metaPreference = DeckMetaDataPreference(context)
metaPreference.putString(LAST_FETCH_TIME, time)
+ val prevData = metaPreference.getInt(TIMES_FETCHED, 0)
+ metaPreference.putInt(TIMES_FETCHED, prevData + 1)
}
/**
@@ -88,5 +100,14 @@ class DeckMetaDataWorker(val context: Context, workerParameters: WorkerParameter
companion object {
const val DECK_META_WORKER = "DeckMetaData"
const val LAST_FETCH_TIME = "LAST_FETCH"
+ const val TIMES_FETCHED = "TIMES_FETCHED"
+ const val WORKER_CREATED = "WORKER_CREATED"
+
+ fun setupNewWorker(context: Context, time: String) {
+ Timber.d("Setting up Preference for new Worker.")
+ val metaDataPreference = DeckMetaDataPreference(context)
+ metaDataPreference.putString(WORKER_CREATED, time)
+ metaDataPreference.putInt(TIMES_FETCHED, 0)
+ }
}
}
diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractSched.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractSched.kt
index ca6e2cc19614..da25a91dd3ee 100644
--- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractSched.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractSched.kt
@@ -181,7 +181,7 @@ abstract class AbstractSched {
/**
* @return [deckname, did, rev, lrn, new]
*/
- abstract fun deckDueList(): List
+ abstract fun deckDueList(): List
/**
* @param cancelListener A task that is potentially cancelled