Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GSoC'24] Add TemplatePreviewerFragment to CardTemplateEditor #16529

Merged
merged 7 commits into from
Aug 11, 2024

Conversation

SanjaySargam
Copy link
Contributor

@SanjaySargam SanjaySargam commented Jun 2, 2024

Purpose / Description

This feature aims to enhance the CardTemplateEditor by adding a TemplatePreviewerFragment. This will allow users to preview card templates in real-time, improving the usability and efficiency of template editing.

Approach

Steps

  1. Attach fragment to activity
  2. Load fragment on preview button, performing actions and tab change
  3. Remove back button when fragmented
  4. Move menu contents to fragment
  5. Customize UI

How Has This Been Tested?

Emulator

previewer.mp4

Checklist

Please, go through these checks before submitting the PR.

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

@david-allison
Copy link
Member

CardTemplateEditorTest > testDeletePendingAddExistingCardCount FAILED
    java.lang.NullPointerException: null cannot be cast to non-null type androidx.appcompat.app.AlertDialog
        at com.ichi2.anki.RobolectricTest.getAlertDialogText(RobolectricTest.kt:219)
        at com.ichi2.anki.CardTemplateEditorTest.testDeletePendingAddExistingCardCount(CardTemplateEditorTest.kt:467)

@SanjaySargam
Copy link
Contributor Author

image
@david-allison, AlertDialog is showing as expected. Am I missing something to reproduce it?

@david-allison
Copy link
Member

@SanjaySargam run and debug the listed test

@SanjaySargam
Copy link
Contributor Author

SanjaySargam commented Jun 3, 2024

@david-allison I run this test in main branch, still it fails. There might the issue with test case I think

@david-allison
Copy link
Member

CI in main passes. It's likely an issue with this PR

I'm not going to have time to dig deeper today

@lukstbit lukstbit added Needs Author Reply Waiting for a reply from the original author GSoC Pull requests authored by a Google Summer of Code participant [Candidate/Selected], for GSoC mentors labels Jun 3, 2024
@SanjaySargam
Copy link
Contributor Author

@david-allison test fails due to move menu to fragment commit. Will you please review it once? I'm not able to figure out

@david-allison
Copy link
Member

val dialog = ShadowDialog.getLatestDialog() as AlertDialog

getLatestDialog returns null causing the crash

@david-allison
Copy link
Member

david-allison commented Jun 4, 2024

Logs when failing
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/davidallison/.gradle/caches/transforms-4/7abe0f11969f889e91071cf394c241db/transformed/slf4j-timber-3.1-runtime.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/davidallison/.gradle/caches/transforms-4/2942083a1c2f97a1213598d3cb7713d2/transformed/slf4j-timber-3.1/jars/classes.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [com.arcao.slf4j.timber.TimberLoggerFactory]
-- executing test "testDeletePendingAddExistingCardCount"
D/Backend: Opening rust backend with lang=[en-US]
I/Collection: (Re)opening Database: /var/folders/ym/nqynp93d4j74dpzw20sq4wq00000gn/T/robolectric-CardTemplateEditorTest_testDeletePendingAddExistingCardCount12987290836504835394/external-files/Android/data/com.ichi2.anki.debug/AnkiDroid/collection.anki2
I/Themes: Setting theme to LIGHT
I/AnkiDroidApp$onCreate: CardTemplateEditor::onCreate
D/AnkiActivity: AnkiActivity.startLoadingCollection()
D/AnkiActivity: Synchronously calling onCollectionLoaded
I/CardTemplateEditor: CardTemplateEditor:: Card template editor successfully started for model id 1717500700290
D/CardTemplateEditor: Setting starting tab to -1
I/AnkiDroidApp$onCreate: CardTemplateEditor::onStart
I/AnkiDroidApp$onCreate: CardTemplateEditor::onResume
D/UsageAnalytics: sendAnalyticsScreenView(): CardTemplateEditor
D/UsageAnalytics: getOptIn() status: false
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
Invalid ID 0x00000000.
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout$SlidingTabIndicator{4689143b V.ED..... ......ID 0,0-320,48} during layout: running second layout pass
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 0 is not a pending add
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 1 is not a pending add
I/CardTemplateEditor: CardTemplateEditor:: Delete template button pressed
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 1 is not a pending add
D/CardTemplateEditor: Ordinal is not a pending add, so we'll get the current card count for confirmation
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
Invalid ID 0x00000000.
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
D/RobolectricTest: Calling destroy on controller com.ichi2.anki.CardTemplateEditor@4f81cc8f
I/AnkiDroidApp$onCreate: CardTemplateEditor::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
I/RobolectricTest: closeCollection: RobolectricTest: End
I/Collection: Collection closed
-- completed test "testDeletePendingAddExistingCardCount"

java.lang.NullPointerException: null cannot be cast to non-null type androidx.appcompat.app.AlertDialog
	at com.ichi2.anki.RobolectricTest.getAlertDialogText(RobolectricTest.kt:215)
	at com.ichi2.anki.CardTemplateEditorTest.testDeletePendingAddExistingCardCount(CardTemplateEditorTest.kt:467)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
	at org.junit.rules.TestWatcher$1.evaluate(TestWatcher.java:61)
	at com.ichi2.testutils.FailOnUnhandledExceptionRule$apply$1.evaluate(FailOnUnhandledExceptionRule.kt:57)
	at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
	at org.robolectric.RobolectricTestRunner$HelperTestRunner$1.evaluate(RobolectricTestRunner.java:489)
	at org.robolectric.internal.SandboxTestRunner$2.lambda$evaluate$2(SandboxTestRunner.java:290)
	at org.robolectric.internal.bytecode.Sandbox.lambda$runOnMainThread$0(Sandbox.java:104)
	at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:840)
Logs when passing
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/davidallison/.gradle/caches/transforms-4/7abe0f11969f889e91071cf394c241db/transformed/slf4j-timber-3.1-runtime.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/davidallison/.gradle/caches/transforms-4/2942083a1c2f97a1213598d3cb7713d2/transformed/slf4j-timber-3.1/jars/classes.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [com.arcao.slf4j.timber.TimberLoggerFactory]
-- executing test "testDeletePendingAddExistingCardCount"
D/Backend: Opening rust backend with lang=[en-US]
I/Collection: (Re)opening Database: /var/folders/ym/nqynp93d4j74dpzw20sq4wq00000gn/T/robolectric-CardTemplateEditorTest_testDeletePendingAddExistingCardCount15894813960812608825/external-files/Android/data/com.ichi2.anki.debug/AnkiDroid/collection.anki2
I/Themes: Setting theme to LIGHT
I/AnkiDroidApp$onCreate: CardTemplateEditor::onCreate
D/AnkiActivity: AnkiActivity.startLoadingCollection()
D/AnkiActivity: Synchronously calling onCollectionLoaded
I/CardTemplateEditor: CardTemplateEditor:: Card template editor successfully started for model id 1717500982796
D/CardTemplateEditor: Setting starting tab to -1
I/AnkiDroidApp$onCreate: CardTemplateEditor::onStart
I/AnkiDroidApp$onCreate: CardTemplateEditor::onResume
D/UsageAnalytics: sendAnalyticsScreenView(): CardTemplateEditor
D/UsageAnalytics: getOptIn() status: false
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout$SlidingTabIndicator{6b5db1b7 V.ED..... ......ID 0,0-320,48} during layout: running second layout pass
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 0 is not a pending add
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 1 is not a pending add
I/CardTemplateEditor$CardTemplateFragment$setupMenu: CardTemplateEditor:: Delete template button pressed
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 1 is not a pending add
D/CardTemplateNotetype: getDeleteDbOrds()
D/Notetypes: getCardIdsForModel found 1 cards to delete for model 1717500982796 and ords (1)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (1)'
D/Notetypes: postDeleteNoteCount would be 1
D/Notetypes: Deleting these cards will not orphan notes.
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 1 is not a pending add
D/CardTemplateEditor$CardTemplateFragment$setupMenu: Ordinal is not a pending add, so we'll get the current card count for confirmation
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onCreate
Invalid ID 0x00000000.
Invalid ID 0x00000000.
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onResume
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
D/CardTemplateEditor$CardTemplateFragment: deleteTemplate() found match - removing template with ord 1
D/CardTemplateNotetype: removeTemplate() on ordinal 1
D/CardTemplateNotetype: addTemplateChange() type DELETE for ordinal 1
D/CardTemplateNotetype: addTemplateChange() added ord/type: 1/DELETE
D/CardTemplateNotetype: dumpChanges() Change 0 is ord/type 1/DELETE
D/CardTemplateNotetype: dumpChanges() During save change 0 will be ord/type 1/DELETE
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout$SlidingTabIndicator{6b5db1b7 V.ED..... ......ID 0,0-320,48} during layout: running second layout pass
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout{277f181f VFED..... ......ID 0,0-320,48 #7f0903bd app:id/sliding_tabs} during layout: running second layout pass
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onDetach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
D/Notetypes: getCardIdsForModel found 1 cards to delete for model 1717500982796 and ords (0)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (0)'
D/Notetypes: postDeleteNoteCount would be 1
D/Notetypes: Deleting these cards will not orphan notes.
D/Notetypes: getCardIdsForModel found 1 cards to delete for model 1717500982796 and ords (1)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (1)'
D/Notetypes: postDeleteNoteCount would be 1
D/Notetypes: Deleting these cards will not orphan notes.
D/Notetypes: getCardIdsForModel found 2 cards to delete for model 1717500982796 and ords (0, 1)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (0, 1)'
D/Notetypes: postDeleteNoteCount would be 0
D/Notetypes: There will be orphan notes if these cards are deleted.
I/CardTemplateEditor$CardTemplateFragment$setupMenu: CardTemplateEditor:: Add template button pressed
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() determined changesIndex 0 was not a pending add
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 0 is not a pending add
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onCreate
Invalid ID 0x00000000.
Invalid ID 0x00000000.
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onResume
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() determined changesIndex 0 was not a pending add
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 0 is not a pending add
D/CardTemplateEditor$CardTemplateFragment: addNewTemplate() lastExistingOrd was 0
D/CardTemplateNotetype: addNewTemplate()
D/CardTemplateNotetype: addTemplateChange() type ADD for ordinal 1
D/CardTemplateNotetype: addTemplateChange() added ord/type: 1/ADD
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() pending add found at at index 1, old ord/adjusted ord 1/1
D/CardTemplateNotetype: getAdjustedTemplateChanges() change 1 ordinal adjusted from 1 to 1
D/CardTemplateNotetype: dumpChanges() Change 0 is ord/type 1/DELETE
D/CardTemplateNotetype: dumpChanges() During save change 0 will be ord/type 1/DELETE
D/CardTemplateNotetype: dumpChanges() Change 1 is ord/type 1/ADD
D/CardTemplateNotetype: dumpChanges() During save change 1 will be ord/type 1/ADD
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 1
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout$SlidingTabIndicator{6b5db1b7 V.ED..... ......ID 0,0-320,48} during layout: running second layout pass
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout{277f181f VFED..... ......ID 0,0-320,48 #7f0903bd app:id/sliding_tabs} during layout: running second layout pass
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onDetach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onSaveInstanceState
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() pending add found at at index 1, old ord/adjusted ord 1/1
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() determined changesIndex 0 was not a pending add
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() pending add found at at index 1, old ord/adjusted ord 1/1
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() ord 0 is not a pending add
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() determined changesIndex 0 was not a pending add
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() pending add found at at index 1, old ord/adjusted ord 1/1
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() found ord 1 was pending add (would adjust to 1)
I/CardTemplateEditor$CardTemplateFragment$setupMenu: CardTemplateEditor:: Delete template button pressed
D/CardTemplateNotetype: getTemplate() on ordinal 1
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() determined changesIndex 0 was not a pending add
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() pending add found at at index 1, old ord/adjusted ord 1/1
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() found ord 1 was pending add (would adjust to 1)
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() determined changesIndex 0 was not a pending add
D/CardTemplateNotetype$Companion: getAdjustedAddOrdinalAtChangeIndex() pending add found at at index 1, old ord/adjusted ord 1/1
D/CardTemplateNotetype$Companion: isOrdinalPendingAdd() found ord 1 was pending add (would adjust to 1)
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onCreate
Invalid ID 0x00000000.
Invalid ID 0x00000000.
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onResume
D/CardTemplateEditor$CardTemplateFragment: deleteTemplate() found match - removing template with ord 1
D/CardTemplateNotetype: removeTemplate() on ordinal 1
D/CardTemplateNotetype: addTemplateChange() type DELETE for ordinal 1
D/CardTemplateNotetype: compactTemplateChanges() merge/purge add/delete ordinal added as 1
D/CardTemplateNotetype: compactTemplateChanges() examining change entry 1 / DELETE
D/CardTemplateNotetype: compactTemplateChanges() examining change entry 1 / ADD
D/CardTemplateNotetype: compactTemplateChanges() found our entry at index 1
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onAttach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onCreate
D/CardTemplateNotetype: getTemplate() on ordinal 0
D/CardTemplateNotetype: getTemplate() on ordinal 0
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewCreated
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStart
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onResume
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout$SlidingTabIndicator{6b5db1b7 V.ED..... ......ID 0,0-320,48} during layout: running second layout pass
W/View: requestLayout() improperly called by com.google.android.material.tabs.TabLayout{277f181f VFED..... ......ID 0,0-320,48 #7f0903bd app:id/sliding_tabs} during layout: running second layout pass
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::ConfirmationDialog::onDetach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
D/Notetypes: getCardIdsForModel found 1 cards to delete for model 1717500982796 and ords (0)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (0)'
D/Notetypes: postDeleteNoteCount would be 1
D/Notetypes: Deleting these cards will not orphan notes.
D/Notetypes: getCardIdsForModel found 1 cards to delete for model 1717500982796 and ords (1)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (1)'
D/Notetypes: postDeleteNoteCount would be 1
D/Notetypes: Deleting these cards will not orphan notes.
D/Notetypes: getCardIdsForModel found 2 cards to delete for model 1717500982796 and ords (0, 1)
D/Notetypes: noteCountPreDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?)'
D/Notetypes: preDeleteNoteCount is 1
D/Notetypes: noteCountPostDeleteSql was 'select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in (0, 1)'
D/Notetypes: postDeleteNoteCount would be 0
D/Notetypes: There will be orphan notes if these cards are deleted.
I/CardTemplateEditor$CardTemplateFragment$setupMenu: CardTemplateEditor:: Save model button pressed
D/CardTemplateNotetype: saveToDatabase() called
D/CardTemplateNotetype: dumpChanges() Change 0 is ord/type 1/DELETE
D/CardTemplateNotetype: dumpChanges() During save change 0 will be ord/type 1/DELETE
D/CollectionOperationsKt: doInBackgroundSaveModel
D/CollectionOperationsKt: doInBackgroundSaveModel() deleting template currently at ordinal 1
D/CardTemplateEditor$CardTemplateFragment: saveModelAndExitHandler::postExecute called
I/AnkiActivity: finishWithAnimation DEFAULT
D/RobolectricTest: Calling destroy on controller com.ichi2.anki.CardTemplateEditor@57f49f3
I/AnkiDroidApp$onCreate: CardTemplateEditor::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onPause
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onStop
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onViewDestroyed
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDestroy
I/FragmentLifecycleLogger: CardTemplateEditor::CardTemplateFragment::onDetach
I/RobolectricTest: closeCollection: RobolectricTest: End
I/Collection: Collection closed
-- completed test "testDeletePendingAddExistingCardCount"
Screenshot 2024-06-04 at 12 37 44

@david-allison
Copy link
Member

david-allison commented Jun 4, 2024

CardTemplateNotetype.isOrdinalPendingAdd(tempModel, ordinal) is true in a 'good' commit

and false in a 'bad' commit

@SanjaySargam Next step is for you to figure out why

@SanjaySargam SanjaySargam force-pushed the card-template-editor branch from 23f9de4 to 7cc3887 Compare June 5, 2024 14:39
@SanjaySargam
Copy link
Contributor Author

@david-allison Finally figured it out

@david-allison david-allison added Needs Review and removed Needs Author Reply Waiting for a reply from the original author labels Jun 5, 2024
Copy link
Member

@Arthur-Milchior Arthur-Milchior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really great PR, thank you very much. I'm happy to see your work going so well, and already having results that'll be visible for the users :D

I tried it on simulator, not chromebook. Still a few issues, that may be resolved in future PR if it's not trivial to solve them here.

On big vertical screen, I think we can keep the buttons for front/back/css even when the keyboard appear on th escreen.
Moreso if you type with the plugged in keyboard, and there is no keyborad on screen. (Actually, the case of using an external keyboard might be true even on small screen)

I am not a fan of refreshing using the eye icon. I think we should follow anki lead and refresh when you are done editing the text for one second. Don't update for each character, but let the user directly see the result of their work.
Admittedly, when you have javascript in your card, that don't always work great. Especially if you have some alert('foo') in it. But I assume most people don't do it. And if we get complaint, we may want to consider adding a way to disable the automated refresh. But I suspect for most user, being as WISIWYG as possible will be a nicer experience.

For a reason I've not yet understood, on pixel tablet simulator at least, if you split the screen in two (using android split, to show a second app) and then put ankidroid back in full view mode, you'll get the CSS content that is copied to the front of the currently displayed card.

Plenty of smaller style remarks inside the code. Nothing that should require a lot of works to correct.

My main remark, really, is that I'd want to know in each commit what I'll see. And really, I'm loosing time because most often, I need to read the code in details to understand what the commit title meant. Please, unless your change is really trivial, add a paragraph. Explain what change is visible from the user point of view and how you achieve that change.

@@ -95,6 +96,8 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
// the current editor view among front/style/back
private var tabToViewId: HashMap<Int, Int?> = HashMap()
private var startingOrdId = 0
private var templatePreviewerFrame: View? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use FragmentContainerView instead of View. Or at least FrameLayout.
Just in case you one day need to use frame specific method, you won't have to cast.

By the way, if you don't already know it, you can use the debugger to see the type of variables at run time. This is how I found the type that is actually returned.

@@ -35,6 +35,7 @@ import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.commit
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would help me a lot to review your PR if you could add more content to the commit messages. That will even be required if we merge as multiple commit.

In the case of the first commit, what I understand after reading it in details, and I'd have loved you to explain is something such as:

On xlarge screen, display the previewer on the trailing side of the card template editor

This commit introduces a new view, card_template_editor.xml, that contains the card template editor (implemented in card_template_editor_activity.xml) and potentially the previewer on xlarge screen.

CardTemplateEditor.kt simply set the content view to card_template_editor instead of card_template_editor_activity, and add the previewer if needed.

This ensure that, we know what change is made. And then, how it's made.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'd add a upper case to "Attach" and generally to the first word of commit message.

requireActivity().onBackPressedDispatcher.onBackPressed()
val toolbar = view.findViewById<MaterialToolbar>(R.id.toolbar)
fragmented = requireActivity().javaClass == CardTemplateEditor::class.java
if (!fragmented) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the sake of readability, I'd generally prefer the if not to contains negation.

If it's fragmented, we want no navigation icon, otherwise ensure that the arrow request the transmit the "back" action to its parent

seems slightly easier to read than

If it's not fragmented the arrow request the transmit the "back" action to its parent otherwise , we want no navigation icon

Comment on lines 927 to 929
if (templateEditor.fragmented) {
templateEditor.loadTemplatePreviewerFragment()
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems each time you call loadTemplatePreviewerFragment you first check whether it's fragmented.
I believe you should change loadTemplatePreviewerFragment to loadTemplatePreviewerFragmentIfFragmented, and let this method do an early return if the view is not fragmented. It'll reduce the size of the code and the logic to consider in your PR

@@ -47,7 +47,7 @@
android:layout_height="wrap_content"
android:layout_gravity="bottom"
style="@style/BottomNavigationViewStyle"
android:background="?attr/appBarColor"
android:background="?attr/alternativeBackgroundColor"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the last commit, I admit I can't give very much feedback. It's really hard to read xml and state directly whether it's an improvement or not.
I'd appreciate a lot if you could add to your PR screenshot of everything that changed. Since those changes seems to be applied on phone too, it'll be easy to take screenshot of before/after and we can easily see the difference and whether most maintainers agree its an improvement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

@SanjaySargam SanjaySargam force-pushed the card-template-editor branch from 7cc3887 to 9f32704 Compare June 10, 2024 12:03
@SanjaySargam
Copy link
Contributor Author

Light Theme

Before After
WhatsApp Image 2024-06-10 at 5 14 55 PM (1) WhatsApp Image 2024-06-10 at 5 14 52 PM (1)
WhatsApp Image 2024-06-10 at 5 14 55 PM WhatsApp Image 2024-06-10 at 5 14 52 PM

Plain Theme

Before After
WhatsApp Image 2024-06-10 at 5 14 54 PM WhatsApp Image 2024-06-10 at 5 14 51 PM (1)
WhatsApp Image 2024-06-10 at 5 14 53 PM (1) WhatsApp Image 2024-06-10 at 5 14 51 PM

Black Theme

Before After
WhatsApp Image 2024-06-10 at 5 14 54 PM (2) WhatsApp Image 2024-06-10 at 5 14 50 PM (2)
WhatsApp Image 2024-06-10 at 5 14 54 PM (1) WhatsApp Image 2024-06-10 at 5 14 50 PM (1)

Dark Theme

Before After
WhatsApp Image 2024-06-10 at 5 14 53 PM WhatsApp Image 2024-06-10 at 5 14 50 PM
WhatsApp Image 2024-06-10 at 5 14 52 PM (2) WhatsApp Image 2024-06-10 at 5 14 49 PM

@SanjaySargam
Copy link
Contributor Author

Tablet

Screenshot 2024-06-10 6 28 11 PM

Screenshot 2024-06-10 6 28 34 PM

Screenshot 2024-06-10 6 30 42 PM

Screenshot 2024-06-10 6 27 02 PM

Copy link
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the fragment is loading, there is a flicker where the right side of the screen is white. This is subtle in the video, but much more noticeable in real life

Could this be fixed (either have the screen show a loading bar, or load the fragment immediately, with the card showing a loading screen)

16529-white.webm

@SanjaySargam
Copy link
Contributor Author

For a reason I've not yet understood, on pixel tablet simulator at least, if you split the screen in two (using android split, to show a second app) and then put ankidroid back in full view mode, you'll get the CSS content that is copied to the front of the currently displayed card.

@Arthur-Milchior
Not able to reproduce on physical tablet
Screen recording 2024-06-11 12.05.26 AM.webm

@david-allison
Copy link
Member

Copy link
Member

@Arthur-Milchior Arthur-Milchior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm done reviewing for now, I must leave. I'll do the last commits later.
Please re-read all commit messages and documentation. Think about the "it" and whether anything is ambiguous. Whether there is missing context.

Don't hesitate to write too much. It's not that bad if you write too much, at least, I'll be sure to get all information I need in order to review.

@@ -132,13 +145,47 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
tempModel = CardTemplateNotetype.fromBundle(savedInstanceState)
}

// check, if tablet layout
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry but I don't understand what you mean here.
I kind of have guesses of what you meant, but not sure.

Please, for the time being, do long sentence. Don't worry about doing sentences that are too long. If that is ever the case will correct that later. Adding too much details is generally not a problem.

What did you mean by "check, if table layout"?

}

/**
* This function initializes a TemplatePreviewerFragment with specific arguments based on the current note type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While I appreciate you had documentation, I'd really would like you to be far more specific in the details.

"This function" is not useful. We know you are documenting the function, so you can remove this part.

"Initializes" is not a notion that seems well defined in kotlin or android. There are init block, but I doubt this is what you refer to.
Also, what really matters is not that the object is initialized. What matter is that the fragment is shown to the user.

What do you mean by "specific arguments"?
This function has no parameter. TemplatePreviewerFragment is a class without parameter (which is normal, given that it is a fragment).

When is "current"? You probably do not mean "right now" when you wrote this or when I read it. I guess what matters is what note type this is based on. The one of tempModel, the one of getNote's type ?

By the way, what mean "based on"? This is far too vague. I'd expect that it's the exact note type. If so "based on" should be removed, as it implicitly means that it's similar to, but not exactly the same.


/**
* This function initializes a TemplatePreviewerFragment with specific arguments based on the current note type.
* It loads the contents of previewer fragment when there is change in template editor.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"when there is a change" seems strange.
I'd assume this function loads the content of the previewer fragment (on the trailing side) as soon as it's called.

Do you mean that it should be called when there is change in template editor? If so, write this explicitly, in a separate sentence. Explain that you expect this method to be called each time the template editor's content is changed. So this is clear that this is not part of the method's responsability. IT does not check "when there is change". On the contrary, something else looks for changes, and call it.

@@ -301,7 +301,11 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
if (keyCode == KeyEvent.KEYCODE_P) {
if (event.isCtrlPressed) {
val currentFragment = currentFragment
currentFragment?.performPreview()
if (fragmented) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The second commit is nice. But please, make real commit messages. I don't want to have to repeat it for every commit for every PR.

Makes long sentences. Explain everything. Don't worry if you write too much. I prefer to have too much instead of not enough.

So, I want you to explain which fragment you consider. Where it is loaded. And what you mean by "preview"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Arthur-Milchior
Oh Sorry, I forget to delete this commit.
Reason: Commit
The above commit will refresh the previewer fragment after one second of text change, so there is no point of loading it manually

@@ -30,6 +30,7 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.card.MaterialCardView
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.ichi2.anki.CardTemplateEditor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding third commit, please edit the commit message. "fragmend" is not a word.
And don't use "it" unless it's clear what it refers too. Here, the only thing it can refer to is the back button. And this back button is not fragmented.

view.findViewById<MaterialToolbar>(R.id.toolbar).setNavigationOnClickListener {
requireActivity().onBackPressedDispatcher.onBackPressed()
val toolbar = view.findViewById<MaterialToolbar>(R.id.toolbar)
fragmented = requireActivity().javaClass == CardTemplateEditor::class.java
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd expect this can be done during third commit. I don't see a reason to do it later.

* This line allow us to know in which case we are.
*/
fragmented = requireActivity().javaClass == CardTemplateEditor::class.java
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/** those two stars are for documentation only. That is, something that explains the meaning of a member, property, variable.
Don't use it inside function please. here, just use comments. With either // or a single star

*/
fragmented = requireActivity().javaClass == CardTemplateEditor::class.java
/**
* If it's fragmented, we want no navigation icon, otherwise ensure that the arrow request the transmit the "back" action to its parent.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use "it" unless it refers to the last noun you used. In this case, it's the start of the sentence, so it does not work.

Worse, the template previewer fragment is never fragmented. Instead, it's part of a fragmented activity. So explain this, that you consider whether this is part of a fragmented activity.

menu.clear()
menuInflater.inflate(R.menu.card_template_editor, menu)
/**
* This is the common menu which can be used either in template editor or in previewer fragment if [fragmented]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"This" seems strange. this has a specific meaning in Kotlin, and I don't think that what you mean here.
As the method's name indicate, the method is setting up the common menu.
So instead, you can state that this method setup the part of the menu that can be used either in template editor or in previewer fragment.

@@ -544,7 +544,9 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
insertField(bundle.getString(InsertFieldDialog.KEY_INSERTED_FIELD)!!)
}
}
setupMenu()
if (!templateEditor.fragmented) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the message of the fourth commit, please don't use "it" as often. Clarifies what you refer to

This commit ensures that the menu will be visible in previewer fragment if it is fragmented

It seems you mean that "it" is the preview fragment. But the previewer can't be fragmented. Instead, you can either state "if it belongs to a fragmented activity".
By the way, I think that "when" would be more grammatically correct than "if".

So it will be compatible to use same toolbar on both screens

I've no idea what "it" refers to. I'd expect "it" refers to "Toolbar". But then the end of the sentence makes no sense.

I think you should do shorter sentences. For example, here, one sentence to explain that you want the same kind of toolbar on both previewer and card template editor. And in another sentence, state that you switch from material toolbar to toolbar in the previewer.

@SanjaySargam SanjaySargam force-pushed the card-template-editor branch from b628699 to 2b0090d Compare June 21, 2024 04:37
Copy link
Member

@Arthur-Milchior Arthur-Milchior left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It really gets better and better, and it gets easier to review, thank you very much.

I'll almost be ready to tap "approve", if not for the fact that, on phone, for some reason, I get the card editor text area to disappear, which render it unusable.
Everything else can be solved later. But this does not seems an acceptable condition for the app.

* It loads the contents of previewer fragment when
* 1. Addition of Card
* 2. Renaming of Card
* 2. Deletion of Card
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you have 2 "2."

ViewCompat.setOnApplyWindowInsetsListener(mainView) { _, insets ->
val imeIsVisible = insets.isVisible(WindowInsetsCompat.Type.ime())
if (imeIsVisible) {
val isTablet = isTablet(requireContext())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2024-06-23-014720_358x561_scrot
There is still a problem with the length of the navigation bar when it's on top of the keyboard.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2024-06-23-020159_339x565_scrot
Actually, it seems there is an even worse problem on phone.
Can you please take a look at this ?

This comment was marked as outdated.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Arthur-Milchior This problem is solved by setting fitsSystemWindows to false

context.resources.configuration.screenLayout
and Configuration.SCREENLAYOUT_SIZE_MASK
)
>= Configuration.SCREENLAYOUT_SIZE_LARGE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your documentation consider "xlarge" but you use the "large" constant. There seems to be an issue here.
Also, it'll probably return true on chromebook, which are not tablet.

I think that, if we need this method, you should just name it isAtLeastXLarge
Also, you may want to add a comment that state that you have the assumption that, anything whose mask is at least SCREENLAYOUT_SIZE_XLARGE will be bigger than x-large.

This seesm to make sense, but I don't see any guarantee in the documentation

@@ -620,6 +630,11 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
menu.findItem(R.id.action_delete).isVisible = false
}

// Hide preview option if it is fragmented
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still an issue with a "it" that is not very clear

@@ -412,14 +415,21 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {

// Set text change listeners
val templateEditorWatcher: TextWatcher = object : TextWatcher {
private var refreshFragmentRunnable: Runnable? = null
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document this variable.

templateEditor.loadTemplatePreviewerFragmentIfFragmented()
}
refreshFragmentRunnable = updateRunnable
refreshFragmentHandler.postDelayed(updateRunnable, 1000)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd appreciate if you could move this 1000 into a top level constant. And document that this is the time to wait before we refresh the previewer.
This way, if we realize that half a second gives a better user experience, it's clear where to change it.

@@ -620,6 +630,11 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
menu.findItem(R.id.action_delete).isVisible = false
}

// Hide preview option if it is fragmented
if (templateEditor.fragmented) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It took me some time to understand why this was in the same commit than the auto-refresh. Until I realize that auto-refresh render this pointless.

I'd suggest either splitting it in two commits. Or explaining in the commit message why you put those together

@@ -73,6 +73,7 @@ class InsertFieldDialog : DialogFragment() {
REQUEST_FIELD_INSERT,
bundleOf(KEY_INSERTED_FIELD to textView.text.toString())
)
(activity as CardTemplateEditor).loadTemplatePreviewerFragmentIfFragmented()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that inserting a field already cause the text watcher to call afterTextChanged. And so this is redundant with the current line. This cause two reloads, which is not pleasant to see.

I'd suggest either:

  • removing this line,
  • or, better, ensuring that, in this case, refreshFragmentRunnable is set to null

@SanjaySargam SanjaySargam force-pushed the card-template-editor branch 2 times, most recently from f335fae to cc69459 Compare June 23, 2024 10:38
Copy link
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, let me know:

  • Is this to be squash merged, or rebase merged
  • When this is ready to merge, my comments are all non-blocking I believe

Thanks!!

@@ -118,7 +118,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
* If true, the view is split in two. The template editor appears on the leading side and the previewer on the trailing side.
* This occurs when the view is big enough.
*/
private var fragmented = false
var fragmented = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe this needs to be public, revert

@@ -166,7 +166,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {

slidingTabLayout = findViewById(R.id.sliding_tabs)
viewPager = findViewById(R.id.card_template_editor_pager)
setNavigationBarColor(R.attr.appBarColor)
setNavigationBarColor(R.attr.alternativeBackgroundColor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit message needs work if we're not squashing this

@@ -1114,6 +1134,9 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
private const val EDITOR_START_ORD_ID = "ordId"
private const val CARD_INDEX = "card_ord"

// Time to wait before refreshing the previewer, in milliseconds.
private const val REFRESH_PREVIEW_DELAY = 1000L
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does Anki Desktop use? This feels slow

@@ -1114,6 +1134,9 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
private const val EDITOR_START_ORD_ID = "ordId"
private const val CARD_INDEX = "card_ord"

// Time to wait before refreshing the previewer, in milliseconds.
private const val REFRESH_PREVIEW_DELAY = 1000L
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But as above, I'd reduce this to xxx.milliseconds

Index: AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt
--- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt	(revision 82bef8d240ba8ec1315c146975e9dbeb3c6a0838)
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt	(date 1722228861063)
@@ -67,6 +67,7 @@
 import com.ichi2.anki.previewer.TemplatePreviewerFragment
 import com.ichi2.anki.snackbar.showSnackbar
 import com.ichi2.anki.utils.ext.isImageOcclusion
+import com.ichi2.anki.utils.postDelayed
 import com.ichi2.annotations.NeedsTest
 import com.ichi2.compat.CompatHelper.Companion.getSerializableCompat
 import com.ichi2.libanki.Collection
@@ -89,6 +90,7 @@
 import java.util.regex.Pattern
 import kotlin.math.max
 import kotlin.math.min
+import kotlin.time.Duration.Companion.seconds
 
 /**
  * Allows the user to view the template for the current note type
@@ -1134,8 +1136,8 @@
         private const val EDITOR_START_ORD_ID = "ordId"
         private const val CARD_INDEX = "card_ord"
 
-        // Time to wait before refreshing the previewer, in milliseconds.
-        private const val REFRESH_PREVIEW_DELAY = 1000L
+        /** Time to wait before refreshing the previewer */
+        private val REFRESH_PREVIEW_DELAY = 1.seconds
 
         @Suppress("unused")
         private const val REQUEST_PREVIEWER = 0

On xlarge screen, display the previewer on the trailing side of the card template editor

This commit introduces a new view, card_template_editor.xml, that contains the card template editor (implemented in card_template_editor_activity.xml) and potentially the previewer on xlarge screen.

CardTemplateEditor.kt simply set the content view to card_template_editor instead of card_template_editor_activity, and add the previewer if needed.
…fragmented.

This commit adds a fragmented variable to TemplatePreviewerArguments, to
let the previewer knows whether it belongs to a fragmented view. If so,
the previewer's back button is removed, hence only the editor's back
button remain.
This commit switch the previewer menu from MateralToolbar to toolbar so that it has the same menu has the editor.
It introduces setupCommonMenu and handleCommonMenuItemSelected that add the previewer's menu entry and handle them. They are called from the editor's menu if the view is fragmented, and from the previewer's own menu otherwise.
@SanjaySargam
Copy link
Contributor Author

  • Is this to be squash merged, or rebase merged

Rebased one

This commit updates the previewer when addition, deletion, renaming of cards.
This commit ensures that if we change card on leading side then the previewer fragment will load and shows same card on trailing side
@david-allison
Copy link
Member

Needs de-conflicting as well

@david-allison
Copy link
Member

@SanjaySargam If this is being rebased, please fixup the commit messages

After this, I think it will be good to go. I'll do a final review

@david-allison
Copy link
Member

@SanjaySargam still pending, should be a quick one

@david-allison david-allison added the Needs Author Reply Waiting for a reply from the original author label Aug 7, 2024
@SanjaySargam
Copy link
Contributor Author

@SanjaySargam If this is being rebased, please fixup the commit messages

After this, I think it will be good to go. I'll do a final review

@david-allison As I know, it didn't harm commit messages. Will you please be specific which commit message?

@david-allison
Copy link
Member

"UI Improvements" needs more detail

This commit updates the UI to ensure that the previewer theme matches the card template editor theme.

CardTemplateEditor
- Set navigation bar color to use alternative background color
- Update background color attribute

TemplatePreviewerFragment
- Apply style to TabLayout for consistent appearance
refresh fragment after one second of text changed. And hide preview icon if fragmented as there is no point of having this option because the fragment will auto-refresh the content.
@SanjaySargam SanjaySargam force-pushed the card-template-editor branch from 0f77591 to f9a2c4f Compare August 7, 2024 17:04
@SanjaySargam SanjaySargam removed the Needs Author Reply Waiting for a reply from the original author label Aug 7, 2024
@Arthur-Milchior
Copy link
Member

@david-allison the commit "UI improvements" has more details.
If it's the only change you requested, you should be able to merge it

Copy link
Member

@david-allison david-allison left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@david-allison david-allison added Pending Merge Things with approval that are waiting future merge (e.g. targets a future release, CI wait, etc) and removed Needs Second Approval Has one approval, one more approval to merge labels Aug 11, 2024
@david-allison david-allison added this pull request to the merge queue Aug 11, 2024
Merged via the queue into ankidroid:main with commit 35cfd4d Aug 11, 2024
8 checks passed
@github-actions github-actions bot removed the Pending Merge Things with approval that are waiting future merge (e.g. targets a future release, CI wait, etc) label Aug 11, 2024
@github-actions github-actions bot added this to the 2.19 release milestone Aug 11, 2024
Copy link
Contributor

Hi there @SanjaySargam! This is the OpenCollective Notice for PRs merged from 2024-08-01 through 2024-08-31

If you are interested in compensation for this work, the process with details is here:

https://github.com/ankidroid/Anki-Android/wiki/OpenCollective-Payment-Process#how-to-get-paid

Important

PLEASE NOTE: The process was updated in August 2024. Re-read the Payment Process page if you have not already.

We only post one comment per person per month to avoid spamming you, regardless of the number of PRs merged, but this note applies to all PRs merged for this month

Please understand that our monthly budget is never guaranteed to cover all claims - the cap on payments-per-person may be lower, but we try to make our process as fair and transparent as possible, we just need your understanding.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
GSoC Pull requests authored by a Google Summer of Code participant [Candidate/Selected], for GSoC mentors
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants