Skip to content

Commit

Permalink
migrated AnkiStatsTaskHandler.createReviewSummaryStatistics() to susp…
Browse files Browse the repository at this point in the history
…end function
  • Loading branch information
thedroiddiv committed Jun 21, 2022
1 parent d95ac1d commit 8610238
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 70 deletions.
1 change: 1 addition & 0 deletions AnkiDroid/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ dependencies {
testImplementation 'androidx.test.ext:junit:1.1.3'
testImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
testImplementation "io.mockk:mockk:1.12.4"
testImplementation 'org.apache.commons:commons-exec:1.3' // obtaining the OS

Expand Down
13 changes: 11 additions & 2 deletions AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import androidx.core.content.ContextCompat
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand Down Expand Up @@ -106,6 +107,8 @@ import com.ichi2.ui.BadgeDrawableBuilder
import com.ichi2.utils.*
import com.ichi2.utils.Permissions.hasStorageAccessPermission
import com.ichi2.widget.WidgetStatus
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.File
import kotlin.math.abs
Expand Down Expand Up @@ -1943,7 +1946,7 @@ open class DeckPicker : NavigationDrawerActivity(), StudyOptionsListener, SyncEr
return UpdateDeckListListener(this)
}

private class UpdateDeckListListener<T : AbstractDeckTreeNode>(deckPicker: DeckPicker?) : TaskListenerWithContext<DeckPicker, Void, List<TreeNode<T>>?>(deckPicker) {
private class UpdateDeckListListener<T : AbstractDeckTreeNode>(private val deckPicker: DeckPicker?) : TaskListenerWithContext<DeckPicker, Void, List<TreeNode<T>>?>(deckPicker) {
override fun actualOnPreExecute(context: DeckPicker) {
if (!context.colIsOpen()) {
context.showProgressBar()
Expand All @@ -1966,7 +1969,13 @@ open class DeckPicker : NavigationDrawerActivity(), StudyOptionsListener, SyncEr
context.mDueTree = result.map { x -> x.unsafeCastToType(AbstractDeckTreeNode::class.java) }
context.renderPage()
// Update the mini statistics bar as well
AnkiStatsTaskHandler.createReviewSummaryStatistics(context.col, context.mReviewSummaryTextView)
deckPicker?.lifecycleScope?.launch(
CoroutineExceptionHandler { _, throwable ->
Timber.w(throwable)
}
) {
AnkiStatsTaskHandler.createReviewSummaryStatistics(context.col, context.mReviewSummaryTextView)
}
Timber.d("Startup - Deck List UI Completed")
}
}
Expand Down
100 changes: 37 additions & 63 deletions AnkiDroid/src/main/java/com/ichi2/anki/stats/AnkiStatsTaskHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
package com.ichi2.anki.stats

import android.R
import android.util.Pair
import android.view.View
import android.webkit.WebView
import android.widget.ProgressBar
Expand All @@ -33,10 +32,7 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.lang.ref.WeakReference
import java.net.URLEncoder
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import kotlin.math.roundToInt

class AnkiStatsTaskHandler private constructor(
Expand Down Expand Up @@ -112,64 +108,10 @@ class AnkiStatsTaskHandler private constructor(
}
}

@Suppress("deprecation") // #7108: AsyncTask
class DeckPreviewStatistics : android.os.AsyncTask<Pair<Collection, TextView?>?, Void?, String?>() {
private var mTextView: WeakReference<TextView>? = null
private var mIsRunning = true
override fun doInBackground(vararg params: Pair<Collection, TextView?>?): String? {
// make sure only one task of CreateChartTask is running, first to run should get sLock
// only necessary on lower APIs because after honeycomb only one thread is used for all asynctasks
sLock.lock()
return try {
val collection = params[0]!!.first
val textView = params[0]!!.second
mTextView = WeakReference(textView)
if (!mIsRunning || collection == null || collection.dbClosed) {
Timber.d("Quitting DeckPreviewStatistics before execution")
return null
} else {
Timber.d("Starting DeckPreviewStatistics")
}

// eventually put this in Stats (in desktop it is not though)
var cards: Int
var minutes: Int
val query = "select sum(case when ease > 0 then 1 else 0 end), " + /* cards, excludes rescheduled cards https://github.com/ankidroid/Anki-Android/issues/8592 */
"sum(time)/1000 from revlog where id > " + (collection.sched.dayCutoff - Stats.SECONDS_PER_DAY) * 1000
Timber.d("DeckPreviewStatistics query: %s", query)
collection.db
.query(query).use { cur ->
cur.moveToFirst()
cards = cur.getInt(0)
minutes = (cur.getInt(1) / 60.0).roundToInt()
}
val res = textView!!.resources
val span = res.getQuantityString(com.ichi2.anki.R.plurals.in_minutes, minutes, minutes)
res.getQuantityString(com.ichi2.anki.R.plurals.studied_cards_today, cards, cards, span)
} finally {
sLock.unlock()
}
}

override fun onCancelled() {
mIsRunning = false
}

override fun onPostExecute(todayStatString: String?) {
val textView = mTextView!!.get()
if (todayStatString != null && mIsRunning && textView != null) {
textView.text = todayStatString
textView.visibility = View.VISIBLE
textView.invalidate()
}
}
}

companion object {
@JvmStatic
var instance: AnkiStatsTaskHandler? = null
private set
private val sLock: Lock = ReentrantLock()
private val mutex = Mutex()
@JvmStatic
@Synchronized
Expand All @@ -184,12 +126,44 @@ class AnkiStatsTaskHandler private constructor(
return instance
}

@Suppress("deprecation") // #7108: AsyncTask
@JvmStatic
fun createReviewSummaryStatistics(col: Collection, view: TextView): DeckPreviewStatistics {
val deckPreviewStatistics = DeckPreviewStatistics()
deckPreviewStatistics.execute(Pair(col, view))
return deckPreviewStatistics
suspend fun createReviewSummaryStatistics(
col: Collection,
view: TextView,
mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
defaultDispatcher: CoroutineDispatcher = Dispatchers.Main
) = mutex.withLock {
withContext(defaultDispatcher) {
val todayStatString = if (!isActive || col.dbClosed) {
Timber.d("Quitting DeckPreviewStatistics before execution")
null
} else {
Timber.d("Starting DeckPreviewStatistics")
// eventually put this in Stats (in desktop it is not though)
var cards: Int
var minutes: Int
/* cards, excludes rescheduled cards https://github.com/ankidroid/Anki-Android/issues/8592 */
val query = "select sum(case when ease > 0 then 1 else 0 end), " +
"sum(time)/1000 from revlog where id > " + (col.sched.dayCutoff - Stats.SECONDS_PER_DAY) * 1000
Timber.d("DeckPreviewStatistics query: %s", query)
col.db
.query(query).use { cur ->
cur.moveToFirst()
cards = cur.getInt(0)
minutes = (cur.getInt(1) / 60.0).roundToInt()
}
val res = view.resources
val span = res.getQuantityString(com.ichi2.anki.R.plurals.in_minutes, minutes, minutes)
res.getQuantityString(com.ichi2.anki.R.plurals.studied_cards_today, cards, cards, span)
}
todayStatString?.let {
withContext(mainDispatcher) {
view.text = it
view.visibility = View.VISIBLE
view.invalidate()
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ import com.ichi2.anki.RobolectricTest
import com.ichi2.anki.stats.AnkiStatsTaskHandler.Companion.createReviewSummaryStatistics
import com.ichi2.annotations.NeedsTest
import com.ichi2.libanki.Collection
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -30,6 +34,7 @@ import org.mockito.MockitoAnnotations
import org.mockito.kotlin.whenever
import java.util.concurrent.ExecutionException

@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class AnkiStatsTaskHandlerTest : RobolectricTest() {
@Mock
Expand All @@ -38,6 +43,8 @@ class AnkiStatsTaskHandlerTest : RobolectricTest() {
@Mock
private lateinit var mView: TextView

private val testDispatcher = StandardTestDispatcher()

@Before
override fun setUp() {
super.setUp()
Expand All @@ -49,12 +56,10 @@ class AnkiStatsTaskHandlerTest : RobolectricTest() {
@Test
@Throws(ExecutionException::class, InterruptedException::class)
@NeedsTest("explain this test")
@Suppress("deprecation") // #7108: AsyncTask
fun testCreateReviewSummaryStatistics() {
fun testCreateReviewSummaryStatistics() = runTest(testDispatcher) {
verify(mCol, atMost(0))!!.db
val result = createReviewSummaryStatistics(mCol, mView)
result.get()
advanceRobolectricLooper()
createReviewSummaryStatistics(mCol, mView, testDispatcher, testDispatcher)
advanceUntilIdle()
verify(mCol, atLeast(0))!!.db
verify(mCol, atLeast(1))!!.dbClosed
}
Expand Down
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ buildscript {
ext.ankidroid_backend_version = '0.1.11'
ext.hamcrest_version = '2.2'
ext.junit_version = '5.8.2'
ext.coroutines_version = '1.6.2'

repositories {
google()
Expand Down

0 comments on commit 8610238

Please sign in to comment.