@@ -21,28 +21,26 @@ import android.content.res.Configuration
2121import android.os.Bundle
2222import android.os.Parcelable
2323import android.text.format.DateFormat
24- import android.view.View
25- import android.widget.EditText
26- import android.widget.ImageView
2724import android.widget.LinearLayout
28- import android.widget.TextView
2925import androidx.appcompat.app.AlertDialog
30- import androidx.appcompat.widget.Toolbar
3126import androidx.core.os.BundleCompat
27+ import androidx.core.text.buildSpannedString
28+ import androidx.core.text.color
3229import androidx.core.view.isVisible
3330import androidx.core.widget.doOnTextChanged
3431import androidx.fragment.app.DialogFragment
3532import androidx.fragment.app.setFragmentResult
3633import androidx.fragment.app.setFragmentResultListener
3734import androidx.fragment.app.viewModels
38- import com.google.android.material.button.MaterialButton
35+ import androidx.lifecycle.LiveData
3936import com.google.android.material.checkbox.MaterialCheckBox
40- import com.google.android.material.textfield.TextInputLayout
37+ import com.google.android.material.color.MaterialColors
4138import com.google.android.material.timepicker.MaterialTimePicker
4239import com.google.android.material.timepicker.TimeFormat
4340import com.ichi2.anki.ALL_DECKS_ID
4441import com.ichi2.anki.CollectionManager.withCol
4542import com.ichi2.anki.R
43+ import com.ichi2.anki.databinding.DialogAddEditReminderBinding
4644import com.ichi2.anki.dialogs.ConfirmationDialog
4745import com.ichi2.anki.isDefaultDeckEmpty
4846import com.ichi2.anki.launchCatchingTask
@@ -53,6 +51,7 @@ import com.ichi2.anki.settings.Prefs
5351import com.ichi2.anki.snackbar.showSnackbar
5452import com.ichi2.anki.startDeckSelection
5553import com.ichi2.anki.utils.ext.showDialogFragment
54+ import com.ichi2.ui.FixedTextView
5655import com.ichi2.utils.DisplayUtils.resizeWhenSoftInputShown
5756import com.ichi2.utils.Permissions
5857import com.ichi2.utils.customView
@@ -89,7 +88,7 @@ class AddEditReminderDialog : DialogFragment() {
8988
9089 private val viewModel: AddEditReminderDialogViewModel by viewModels()
9190
92- private lateinit var contentView : View
91+ private lateinit var binding : DialogAddEditReminderBinding
9392
9493 /* *
9594 * The mode of this dialog, retrieved from arguments and set by [getInstance].
@@ -105,12 +104,12 @@ class AddEditReminderDialog : DialogFragment() {
105104
106105 override fun onCreateDialog (savedInstanceState : Bundle ? ): Dialog {
107106 super .onCreateDialog(savedInstanceState)
108- contentView = layoutInflater .inflate(R .layout.add_edit_reminder_dialog, null )
107+ binding = DialogAddEditReminderBinding .inflate(layoutInflater )
109108 Timber .d(" dialog mode: %s" , dialogMode.toString())
110109
111110 val dialogBuilder =
112111 AlertDialog .Builder (requireActivity()).apply {
113- customView(contentView )
112+ customView(binding.root )
114113 positiveButton(R .string.dialog_ok)
115114 neutralButton(R .string.dialog_cancel)
116115
@@ -138,6 +137,7 @@ class AddEditReminderDialog : DialogFragment() {
138137 setUpAdvancedDropdown()
139138 setUpCardThresholdInput()
140139 setUpOnlyNotifyIfNoReviewsCheckbox()
140+ setUpCountCheckboxes()
141141
142142 // For getting the result of the deck selection sub-dialog from ScheduleReminders
143143 // See ScheduleReminders.onDeckSelected for more information
@@ -156,17 +156,15 @@ class AddEditReminderDialog : DialogFragment() {
156156 else -> Consts .DEFAULT_DECK_ID
157157 }
158158 viewModel.setDeckSelected(selectedDeckId)
159- this .dialog?.findViewById<TextView >(R .id.add_edit_reminder_deck_name)?.text =
160- selectedDeck?.getDisplayName(requireContext())
159+ binding.addEditReminderDeckName.text = selectedDeck?.getDisplayName(requireContext())
161160 }
162161
163162 dialog.window?.let { resizeWhenSoftInputShown(it) }
164163 return dialog
165164 }
166165
167166 private fun setUpToolbar () {
168- val toolbar = contentView.findViewById<Toolbar >(R .id.add_edit_reminder_toolbar)
169- toolbar.title =
167+ binding.addEditReminderToolbar.title =
170168 getString(
171169 when (dialogMode) {
172170 is DialogMode .Add -> R .string.add_review_reminder
@@ -176,25 +174,23 @@ class AddEditReminderDialog : DialogFragment() {
176174 }
177175
178176 private fun setUpTimeButton () {
179- val timeButton = contentView.findViewById<MaterialButton >(R .id.add_edit_reminder_time_button)
180- timeButton.setOnClickListener {
177+ binding.addEditReminderTimeButton.setOnClickListener {
181178 Timber .i(" Time button clicked" )
182179 val time = viewModel.time.value ? : ReviewReminderTime .getCurrentTime()
183180 showTimePickerDialog(time.hour, time.minute)
184181 }
185182 viewModel.time.observe(this ) { time ->
186- timeButton .text = time.toFormattedString(requireContext())
183+ binding.addEditReminderTimeButton .text = time.toFormattedString(requireContext())
187184 }
188185 }
189186
190187 private fun setInitialDeckSelection () {
191- val deckName = contentView.findViewById<TextView >(R .id.add_edit_reminder_deck_name)
192- deckName.setOnClickListener { startDeckSelection(all = true , filtered = true ) }
188+ binding.addEditReminderDeckName.setOnClickListener { startDeckSelection(all = true , filtered = true ) }
193189 launchCatchingTask {
194190 Timber .d(" Setting up deck name view" )
195191 val (selectedDeckId, selectedDeckName) = getValidDeckSelection()
196192 Timber .d(" Initial selection of deck %s(id=%d)" , selectedDeckName, selectedDeckId)
197- deckName .text = selectedDeckName
193+ binding.addEditReminderDeckName .text = selectedDeckName
198194 viewModel.setDeckSelected(selectedDeckId)
199195 }
200196 }
@@ -231,34 +227,28 @@ class AddEditReminderDialog : DialogFragment() {
231227 }
232228
233229 private fun setUpAdvancedDropdown () {
234- val advancedDropdown = contentView.findViewById<LinearLayout >(R .id.add_edit_reminder_advanced_dropdown)
235- val advancedDropdownIcon = contentView.findViewById<ImageView >(R .id.add_edit_reminder_advanced_dropdown_icon)
236- val advancedContent = contentView.findViewById<LinearLayout >(R .id.add_edit_reminder_advanced_content)
237-
238- advancedDropdown.setOnClickListener {
230+ binding.addEditReminderAdvancedDropdown.setOnClickListener {
239231 viewModel.toggleAdvancedSettingsOpen()
240232 }
241233 viewModel.advancedSettingsOpen.observe(this ) { advancedSettingsOpen ->
242234 when (advancedSettingsOpen) {
243235 true -> {
244- advancedContent .isVisible = true
245- advancedDropdownIcon .setBackgroundResource(DROPDOWN_EXPANDED_CHEVRON )
236+ binding.addEditReminderAdvancedContent .isVisible = true
237+ binding.addEditReminderAdvancedDropdownIcon .setBackgroundResource(DROPDOWN_EXPANDED_CHEVRON )
246238 }
247239 false -> {
248- advancedContent .isVisible = false
249- advancedDropdownIcon .setBackgroundResource(DROPDOWN_COLLAPSED_CHEVRON )
240+ binding.addEditReminderAdvancedContent .isVisible = false
241+ binding.addEditReminderAdvancedDropdownIcon .setBackgroundResource(DROPDOWN_COLLAPSED_CHEVRON )
250242 }
251243 }
252244 }
253245 }
254246
255247 private fun setUpCardThresholdInput () {
256- val cardThresholdInputWrapper = contentView.findViewById<TextInputLayout >(R .id.add_edit_reminder_card_threshold_input_wrapper)
257- val cardThresholdInput = contentView.findViewById<EditText >(R .id.add_edit_reminder_card_threshold_input)
258- cardThresholdInput.setText(viewModel.cardTriggerThreshold.value.toString())
259- cardThresholdInput.doOnTextChanged { text, _, _, _ ->
248+ binding.addEditReminderCardThresholdInput.setText(viewModel.cardTriggerThreshold.value.toString())
249+ binding.addEditReminderCardThresholdInput.doOnTextChanged { text, _, _, _ ->
260250 val value: Int? = text.toString().toIntOrNull()
261- cardThresholdInputWrapper .error =
251+ binding.addEditReminderCardThresholdInputWrapper .error =
262252 when {
263253 (value == null ) -> " Please enter a whole number of cards"
264254 (value < 0 ) -> " The threshold must be at least 0"
@@ -269,16 +259,79 @@ class AddEditReminderDialog : DialogFragment() {
269259 }
270260
271261 private fun setUpOnlyNotifyIfNoReviewsCheckbox () {
272- val contentSection = contentView.findViewById<LinearLayout >(R .id.add_edit_reminder_only_notify_if_no_reviews_section)
273- val checkbox = contentView.findViewById<MaterialCheckBox >(R .id.add_edit_reminder_only_notify_if_no_reviews_checkbox)
274- contentSection.setOnClickListener {
262+ binding.addEditReminderOnlyNotifyIfNoReviewsSection.setOnClickListener {
275263 viewModel.toggleOnlyNotifyIfNoReviews()
276264 }
277- checkbox .setOnClickListener {
265+ binding.addEditReminderOnlyNotifyIfNoReviewsCheckbox .setOnClickListener {
278266 viewModel.toggleOnlyNotifyIfNoReviews()
279267 }
280268 viewModel.onlyNotifyIfNoReviews.observe(this ) { onlyNotifyIfNoReviews ->
281- checkbox.isChecked = onlyNotifyIfNoReviews
269+ binding.addEditReminderOnlyNotifyIfNoReviewsCheckbox.isChecked = onlyNotifyIfNoReviews
270+ }
271+ }
272+
273+ /* *
274+ * Convenience data class for setting up the checkboxes for whether to count new, learning, and review cards
275+ * when considering the card trigger threshold.
276+ * @see setUpCountCheckboxes
277+ */
278+ private data class CountViewsAndActions (
279+ val section : LinearLayout ,
280+ val textView : FixedTextView ,
281+ val checkbox : MaterialCheckBox ,
282+ val actionOnClick : () -> Unit ,
283+ val state : LiveData <Boolean >,
284+ )
285+
286+ /* *
287+ * Sets up the checkboxes for whether to count new, learning, and review cards when considering the card trigger threshold.
288+ * @see CountViewsAndActions
289+ */
290+ private fun setUpCountCheckboxes () {
291+ val countViewsAndActionsItems =
292+ listOf (
293+ CountViewsAndActions (
294+ section = binding.addEditReminderCountNewSection,
295+ textView = binding.addEditReminderCountNewLabel,
296+ checkbox = binding.addEditReminderCountNewCheckbox,
297+ actionOnClick = viewModel::toggleCountNew,
298+ state = viewModel.countNew,
299+ ),
300+ CountViewsAndActions (
301+ section = binding.addEditReminderCountLrnSection,
302+ textView = binding.addEditReminderCountLrnLabel,
303+ checkbox = binding.addEditReminderCountLrnCheckbox,
304+ actionOnClick = viewModel::toggleCountLrn,
305+ state = viewModel.countLrn,
306+ ),
307+ CountViewsAndActions (
308+ section = binding.addEditReminderCountRevSection,
309+ textView = binding.addEditReminderCountRevLabel,
310+ checkbox = binding.addEditReminderCountRevCheckbox,
311+ actionOnClick = viewModel::toggleCountRev,
312+ state = viewModel.countRev,
313+ ),
314+ )
315+
316+ countViewsAndActionsItems.forEachIndexed { i, item ->
317+ item.section.setOnClickListener { item.actionOnClick() }
318+
319+ // Manually split the string resource so that we can color just the review state part
320+ val (reviewState, colorAttr) = REVIEW_STATE_STRINGS_AND_COLORS .entries.elementAt(i)
321+ val splitString = getString(R .string.review_reminders_include_review_state_for_threshold_do_not_translate).split(" %s" )
322+ item.textView.text =
323+ buildSpannedString {
324+ append(splitString[0 ])
325+ color(MaterialColors .getColor(requireContext(), colorAttr, 0 )) {
326+ append(getString(reviewState))
327+ }
328+ append(splitString[1 ])
329+ }
330+
331+ item.checkbox.setOnClickListener { item.actionOnClick() }
332+ item.state.observe(this ) { value ->
333+ item.checkbox.isChecked = value
334+ }
282335 }
283336 }
284337
@@ -321,9 +374,8 @@ class AddEditReminderDialog : DialogFragment() {
321374 private fun onSubmit () {
322375 Timber .i(" Submitted dialog" )
323376 // Do nothing if numerical fields are invalid
324- val cardThresholdInputWrapper = contentView.findViewById<TextInputLayout >(R .id.add_edit_reminder_card_threshold_input_wrapper)
325- cardThresholdInputWrapper.error?.let {
326- contentView.showSnackbar(R .string.something_wrong)
377+ binding.addEditReminderCardThresholdInputWrapper.error?.let {
378+ binding.root.showSnackbar(R .string.something_wrong)
327379 return
328380 }
329381
@@ -391,6 +443,17 @@ class AddEditReminderDialog : DialogFragment() {
391443 */
392444 private const val TIME_PICKER_TAG = " REMINDER_TIME_PICKER_DIALOG"
393445
446+ /* *
447+ * String resources and colors to display them in for the different review states (new, learning, review).
448+ * Used for styling the advanced options for which card types to count towards the card trigger threshold.
449+ */
450+ private val REVIEW_STATE_STRINGS_AND_COLORS =
451+ mapOf (
452+ R .string.new_review_state_do_not_translate to R .attr.newCountColor,
453+ R .string.learning_review_state_do_not_translate to R .attr.learnCountColor,
454+ R .string.reviewing_review_state_do_not_translate to R .attr.reviewCountColor,
455+ )
456+
394457 /* *
395458 * Creates a new instance of this dialog with the given dialog mode.
396459 */
0 commit comments