Skip to content

Commit

Permalink
refactor(custom-study): arguments & add docs
Browse files Browse the repository at this point in the history
* move arg keys to companion object

* renames:
`DID` => `ARG_DID`
`ID` => `ARG_SUB_DIALOG_ID`, `"id"` => `"subDialogId"`

I don't feel these are important variables to have at the start
of the file

----

refactor(custom-study): make `selectedSubDialog` a property

previously: `getOption()`

----

refactor: introduce `dialogDeckId`

----

docs: CustomStudyDialog
  • Loading branch information
david-allison committed Dec 9, 2024
1 parent 6e4f8da commit f84b6b8
Showing 1 changed file with 73 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import com.ichi2.libanki.Consts.DynPriority
import com.ichi2.libanki.Deck
import com.ichi2.libanki.DeckId
import com.ichi2.utils.BundleUtils.getNullableInt
import com.ichi2.utils.KotlinCleanup
import com.ichi2.utils.cancelable
import com.ichi2.utils.customView
import com.ichi2.utils.listItems
Expand All @@ -69,32 +70,69 @@ import timber.log.Timber
import java.util.Locale

/**
* Key for the ordinal of the [ContextMenuOption] to display. May be not set.
* Implements custom studying either by:
* 1. modifying the limits of the current selected deck for when the user has reached the daily deck limits and wishes to study more
* 2. creating a filtered "Custom study deck" where a user can study outside the typical schedule
*
*
* ## UI
* [CustomStudyDialog] represents either:
* * A [main menu][buildContextMenu], offering [methods of custom study][ContextMenuOption]:
* * An [input dialog][buildInputDialog] to input additional constraints for a [ContextMenuOption]
* * Example: changing the number of new cards
*
* #### Not Implemented
* Anki Desktop contains the following items which are not yet implemented
* * Study by card state or tags
* * New cards only
* * Due cards only
* * All review cards in random order
* * All cards in random order (don't reschedule)
*
* ## Nomenclature
* Filtered decks were previously known as 'dynamic' decks, and before that: 'cram' decks
*
* ## Links
* * [https://docs.ankiweb.net/filtered-decks.html#custom-study](https://docs.ankiweb.net/filtered-decks.html#custom-study)
* * [com.ichi2.libanki.sched.Scheduler.customStudyDefaults]
* * [sched.proto: CustomStudyDefaultsResponse](https://github.com/search?q=repo%3Aankitects%2Fanki+CustomStudyDefaultsResponse+language%3A%22Protocol+Buffer%22&type=code&l=Protocol+Buffer)
* * [com.ichi2.libanki.sched.Scheduler.customStudy]
* * [sched.proto: CustomStudyRequest](https://github.com/search?q=repo%3Aankitects%2Fanki+CustomStudyRequest+language%3A%22Protocol+Buffer%22&type=code&l=Protocol+Buffer)
* * [https://github.com/ankitects/anki/blob/main/qt/aqt/customstudy.py](https://github.com/ankitects/anki/blob/main/qt/aqt/customstudy.py)
*/
private const val ID = "id"

/**
* Key for the id of the deck this dialog deals with
*/
private const val DID = "did"

class CustomStudyDialog(private val collection: Collection, private val customStudyListener: CustomStudyListener?) : AnalyticsDialogFragment(), TagsDialogListener {
@KotlinCleanup("remove 'collection' parameter and use withCol { }")
@KotlinCleanup("remove 'customStudyListener' parameter and use FragmentResult")
class CustomStudyDialog(
private val collection: Collection,
private val customStudyListener: CustomStudyListener?
) : AnalyticsDialogFragment(), TagsDialogListener {

interface CustomStudyListener {
fun onCreateCustomStudySession()
fun onExtendStudyLimits()
}

/** ID of the [Deck] which this dialog was created for */
private val dialogDeckId: DeckId
get() = requireArguments().getLong(ARG_DID)

/**
* `null` initially when the main view is shown
* otherwise, the [ContextMenuOption] representing the current sub-dialog
*/
private val selectedSubDialog: ContextMenuOption?
get() = requireArguments().getNullableInt(ARG_SUB_DIALOG_ID)?.let { ContextMenuOption.entries[it] }

fun withArguments(
did: DeckId,
contextMenuAttribute: ContextMenuOption? = null
): CustomStudyDialog {
val args = this.arguments ?: Bundle()
args.apply {
if (contextMenuAttribute != null) {
putInt(ID, contextMenuAttribute.ordinal)
putInt(ARG_SUB_DIALOG_ID, contextMenuAttribute.ordinal)
}
putLong(DID, did)
putLong(ARG_DID, did)
}
this.arguments = args
return this
Expand All @@ -107,11 +145,11 @@ class CustomStudyDialog(private val collection: Collection, private val customSt

override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
super.onCreate(savedInstanceState)
val option = getOption()
val option = selectedSubDialog
return if (option == null) {
Timber.i("Showing Custom Study main menu")
// Select the specified deck
collection.decks.select(requireArguments().getLong(DID))
collection.decks.select(dialogDeckId)
buildContextMenu()
} else {
Timber.i("Showing Custom Study dialog: $option")
Expand All @@ -134,13 +172,11 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
* number, it is necessary to collect a list of tags. This case handles the creation
* of that Dialog.
*/
val currentDeck = requireArguments().getLong(DID)

val dialogFragment = TagsDialog().withArguments(
context = requireContext(),
type = TagsDialog.DialogType.CUSTOM_STUDY_TAGS,
checkedTags = ArrayList(),
allTags = ArrayList(collection.tags.byDeck(currentDeck))
allTags = ArrayList(collection.tags.byDeck(dialogDeckId))
)
requireActivity().showDialogFragment(dialogFragment)
}
Expand All @@ -152,10 +188,7 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
STUDY_PREVIEW -> {
// User asked for a standard custom study option
val d = CustomStudyDialog(collection, customStudyListener)
.withArguments(
requireArguments().getLong(DID),
listIds[index]
)
.withArguments(dialogDeckId, listIds[index])
requireActivity().showDialogFragment(d)
}
}
Expand Down Expand Up @@ -188,8 +221,6 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
if (contextMenuOption == STUDY_NEW || contextMenuOption == STUDY_REV) {
editText.inputType = EditorInfo.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_FLAG_SIGNED
}
// deck id
val did = requireArguments().getLong(DID)
// Set material dialog parameters
val dialog = AlertDialog.Builder(requireActivity())
.customView(view = v, paddingLeft = 64, paddingRight = 64, paddingTop = 32, paddingBottom = 32)
Expand All @@ -205,15 +236,15 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
when (contextMenuOption) {
STUDY_NEW -> {
requireActivity().sharedPrefs().edit { putInt("extendNew", n) }
val deck = collection.decks.get(did)!!
val deck = collection.decks.get(dialogDeckId)!!
deck.put("extendNew", n)
collection.decks.save(deck)
collection.sched.extendLimits(n, 0)
onLimitsExtended()
}
STUDY_REV -> {
requireActivity().sharedPrefs().edit { putInt("extendRev", n) }
val deck = collection.decks.get(did)!!
val deck = collection.decks.get(dialogDeckId)!!
deck.put("extendRev", n)
collection.decks.save(deck)
collection.sched.extendLimits(0, n)
Expand Down Expand Up @@ -331,15 +362,10 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
}
}

/**
* The ContextMenuOption saved in [ID]. It may be unset for the standard study dialog.
*/
private fun getOption() = requireArguments().getNullableInt(ID)?. let { ContextMenuOption.entries[it] }

private val text1: String
get() {
val res = resources
return when (getOption()) {
return when (selectedSubDialog) {
STUDY_NEW -> res.getString(R.string.custom_study_new_total_new, collection.sched.totalNewForCurrentDeck())
STUDY_REV -> res.getString(R.string.custom_study_rev_total_rev, collection.sched.totalRevForCurrentDeck())
STUDY_FORGOT,
Expand All @@ -353,7 +379,7 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
private val text2: String
get() {
val res = resources
return when (getOption()) {
return when (selectedSubDialog) {
STUDY_NEW -> res.getString(R.string.custom_study_new_extend)
STUDY_REV -> res.getString(R.string.custom_study_rev_extend)
STUDY_FORGOT -> res.getString(R.string.custom_study_forgotten)
Expand All @@ -367,7 +393,7 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
private val defaultValue: String
get() {
val prefs = requireActivity().sharedPrefs()
return when (getOption()) {
return when (selectedSubDialog) {
STUDY_NEW -> prefs.getInt("extendNew", 10).toString()
STUDY_REV -> prefs.getInt("extendRev", 50).toString()
STUDY_FORGOT -> prefs.getInt("forgottenDays", 1).toString()
Expand All @@ -387,10 +413,9 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
*/
private fun createCustomStudySession(delays: JSONArray, terms: Array<Any>, resched: Boolean) {
val dyn: Deck
val did = requireArguments().getLong(DID)

val decks = collection.decks
val deckToStudyName = decks.name(did)
val deckToStudyName = decks.name(dialogDeckId)
val customStudyDeck = resources.getString(R.string.custom_study_deck_name)
val cur = decks.byName(customStudyDeck)
if (cur != null) {
Expand Down Expand Up @@ -473,4 +498,18 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
STUDY_PREVIEW(R.string.custom_study_preview_new),
STUDY_TAGS(R.string.custom_study_limit_tags)
}

companion object {
/**
* (required) Key for the [DeckId] this dialog deals with.
* @see CustomStudyDialog.dialogDeckId
*/
private const val ARG_DID = "did"

/**
* (optional) Key for the ordinal of the [ContextMenuOption] to display.
* @see CustomStudyDialog.selectedSubDialog
*/
private const val ARG_SUB_DIALOG_ID = "subDialogId"
}
}

0 comments on commit f84b6b8

Please sign in to comment.