Integrate image occlusion editor#14884
Conversation
|
Message to maintainers, this PR contains strings changes.
Read more about updating strings on the wiki, |
| else -> {} | ||
| } | ||
|
|
||
| col.backend.addImageOcclusionNotetype() |
There was a problem hiding this comment.
I assume it's fine not to call this in a background task. The computer version doesn't bother using background task for this.
There was a problem hiding this comment.
Technically, we probably should be using a background task for the desktop as well. If the user has a large number of notetypes + slow disk, the operation to fetch all the notetypes could potentially block for some time.
For methods that access the collection, standard practice is to write a little wrapper function in the Collection class and call that inside withCol, instead of using the backend directly. We only access the backend directly for operations that don't require/want the collection lock (such as checking progress). One problem this avoids is the collection is guaranteed not to be closed in the middle of an operation running inside withCol.
dae
left a comment
There was a problem hiding this comment.
Thanks for looking into this Abdo!
|
|
||
| class ImageOcclusion : PageFragment() { | ||
|
|
||
| override val title = R.string.image_occlusion |
There was a problem hiding this comment.
You can use TR.notetypesImageOcclusionName() to avoid having to add a duplicate string.
| else -> {} | ||
| } | ||
|
|
||
| col.backend.addImageOcclusionNotetype() |
There was a problem hiding this comment.
Technically, we probably should be using a background task for the desktop as well. If the user has a large number of notetypes + slow disk, the operation to fetch all the notetypes could potentially block for some time.
For methods that access the collection, standard practice is to write a little wrapper function in the Collection class and call that inside withCol, instead of using the backend directly. We only access the backend directly for operations that don't require/want the collection lock (such as checking progress). One problem this avoids is the collection is guaranteed not to be closed in the middle of an operation running inside withCol.
| "getImageOcclusionNote" -> CollectionManager.getBackend().getImageOcclusionNoteRaw(bytes) | ||
| "getImageForOcclusionFields" -> CollectionManager.getBackend().getImageOcclusionFieldsRaw(bytes) | ||
| "addImageOcclusionNote" -> CollectionManager.getBackend().addImageOcclusionNoteRaw(bytes) | ||
| "updateImageOcclusionNote" -> CollectionManager.getBackend().updateImageOcclusionNoteRaw(bytes) |
There was a problem hiding this comment.
These would be better implemented as wrappers inside a withCol block.
2a2cffd to
834ec12
Compare
Sorry, missed that comment. I think when @krmanik originally introduced this feature, we didn't have a comments field in the notetype. Presumably when it was later added, we forgot to update the mobile-only entrypoint. We should probably keep it hidden until we can transition to the desktop editor implementation, as I think the mobile-only entrypoint will fail if that field is missing, and we don't protect it. Once we've migrated all clients to the desktop editor, we'll be able to remove the protection on header/back extra as well, so users can remove them if they wish. |
| } | ||
| mPasteImaegOcclusionImageButton?.text = TR.notetypesIoPasteImageFromClipboard() | ||
| mPasteImaegOcclusionImageButton?.setOnClickListener { | ||
| if (ClipboardUtil.hasImage(clipboard)) { |
There was a problem hiding this comment.
Clipboard support is not properly tested yet. I wrote this referencing similar features across the codebase. I've not managed to copy images to the clipboard for some reason (tested copying from Chrome).
There was a problem hiding this comment.
Just confirmed this is working. But maybe hasImage() should be extended to support all accepted types? https://github.com/ankitects/anki/blob/6f3550464d37aee1b8b784e431cbfce8382d3ce7/rslib/src/image_occlusion/imagedata.rs#L154
There was a problem hiding this comment.
This can be left as a TODO if you want
| "getImageForOcclusionFields" -> withCol { getImageOcclusionFieldsRaw(bytes) } | ||
| "addImageOcclusionNote" -> withCol { addImageOcclusionNoteRaw(bytes) }.also { | ||
| activity.launchCatchingTask { | ||
| // Allow time for toast message to appear before closing editor |
There was a problem hiding this comment.
@dae does AnkiMobile show the toast messages? If so, how is this handled there?
There was a problem hiding this comment.
It 'cheats': the screen is closed immediately, and it prints something else:
showPopup(tr.importingNoteAdded(count: 1))
There was a problem hiding this comment.
This may be somewhat related - a crash showing JS dialogs/confirms when AnkiPages tried to open an AlertDialog after the AnkiPages object was destroyed (if my hypothesis there is correct) #14920 - I see some other discussion in this area and I haven't read all comments so this comment should be considered as an isolated bit of context just in case, and it may not be useful
david-allison
left a comment
There was a problem hiding this comment.
Comments from a few days ago, things may have moved on
| // nothing was changed by the note editor so just redraw the card | ||
| redrawCard() | ||
| } else if (resultCode == NoteEditor.RESULT_UPDATED_IO_NOTE) { | ||
| displayCardQuestion(true) |
There was a problem hiding this comment.
(question, not a request)
Is an OpChanges exposed which we can use inside an undoableOp instead?
There was a problem hiding this comment.
Both adding and updating I/O notes return OpChanges, so that should be possible, but as the calls are currently of the Raw variant, the return value would need to be decoded as OpChangesOnly.
| get() = mEditorNote!!.items().map { it.requireNoNulls() }.toTypedArray() | ||
|
|
||
| private fun currentNotetypeIsImageOcclusion(): Boolean { | ||
| println("currentNotetypeIsImageOcclusion: ${currentlySelectedNotetype?.fieldsNames}") |
There was a problem hiding this comment.
(if this is something you feel would be useful moving forward, and not temporary debug info that should be removed)
| "getImageForOcclusionFields" -> withCol { getImageOcclusionFieldsRaw(bytes) } | ||
| "addImageOcclusionNote" -> withCol { addImageOcclusionNoteRaw(bytes) }.also { | ||
| activity.launchCatchingTask { | ||
| // Allow time for toast message to appear before closing editor |
There was a problem hiding this comment.
Android toasts persist globally, as a future extension, we can move this to a native call
There was a problem hiding this comment.
For context David, this current screen is a 'temporary' one: the desktop editor integrates the I/O editor directly, and this separate screen is provided just for the mobile clients. Hopefully they'll be able to switch to the desktop editor in the future, making this entrypoint obsolete.
| private const val ARG_KEY_PATH = "path" | ||
|
|
||
| fun getIntent(context: Context, kind: String, noteOrNotetypeId: Long, imagePath: String?): Intent { | ||
| val arguments = Bundle().apply { |
| // nothing was changed by the note editor so just redraw the card | ||
| redrawCard() | ||
| } else if (resultCode == NoteEditor.RESULT_UPDATED_IO_NOTE) { | ||
| displayCardQuestion(true) |
There was a problem hiding this comment.
This should be rebased on main, this line conflicts (parameter was removed in API changes)
| ) | ||
|
|
||
| private val requestIOEditorLauncher = registerForActivityResult( | ||
| ActivityResultContracts.StartActivityForResult(), |
There was a problem hiding this comment.
|
@dae Does AnkiMobile have the "Reset Image Occlusion" button? It probably makes more sense as part of the IO page on mobile. |
|
It doesn't - you need to close and re-open the window if you wish to select a different image. |
The comments field added later, the header and back extra fields are currently basic implementation and enough for most users. |
OK, will skip that then. This PR is ready for another review if someone has the time. |
|
This will update the interface to show the change in undo status, and avoids the need for manually refreshing the review screen: diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt
index a56e7ac178..aad2eb588c 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt
@@ -297,9 +297,6 @@ abstract class AbstractFlashcardViewer :
} else if (result.resultCode == RESULT_CANCELED && !reloadRequired) {
// nothing was changed by the note editor so just redraw the card
redrawCard()
- } else if (result.resultCode == NoteEditor.RESULT_UPDATED_IO_NOTE) {
- reloadWebViewContent()
- onEditedNoteChanged()
}
}
)
diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt
index 183867c0cc..b75925708a 100644
--- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt
+++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt
@@ -19,6 +19,7 @@ package com.ichi2.anki.pages
import android.app.Activity
import androidx.fragment.app.FragmentActivity
+import anki.collection.OpChanges
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.NoteEditor
@@ -100,21 +101,31 @@ open class AnkiServer(
"getImageForOcclusion" -> withCol { getImageForOcclusionRaw(bytes) }
"getImageOcclusionNote" -> withCol { getImageOcclusionNoteRaw(bytes) }
"getImageForOcclusionFields" -> withCol { getImageOcclusionFieldsRaw(bytes) }
- "addImageOcclusionNote" -> withCol { addImageOcclusionNoteRaw(bytes) }.also {
+ "addImageOcclusionNote" -> {
+ val data = withCol {
+ addImageOcclusionNoteRaw(bytes)
+ }
+ undoableOp { OpChanges.parseFrom(data) }
activity.launchCatchingTask {
// Allow time for toast message to appear before closing editor
delay(1000)
activity.setResult(Activity.RESULT_OK)
activity.finish()
}
+ data
}
- "updateImageOcclusionNote" -> withCol { updateImageOcclusionNoteRaw(bytes) }.also {
+ "updateImageOcclusionNote" -> {
+ val data = withCol {
+ updateImageOcclusionNoteRaw(bytes)
+ }
+ undoableOp { OpChanges.parseFrom(data) }
activity.launchCatchingTask {
// Allow time for toast message to appear before closing editor
delay(1000)
activity.setResult(NoteEditor.RESULT_UPDATED_IO_NOTE)
activity.finish()
}
+ data
}
"congratsInfo" -> withCol { congratsInfoRaw(bytes) }
else -> { throw Exception("unhandled request: $methodName") }Apart from that, adding and editing seems to work well. The other AD devs are more qualified to comment on the Android-related changes here, but from my backend-focused perspective, it looks good. For your first non-documentation PR, you've done well to get up to speed with Android development so quickly! |
Thanks. I worked briefly with Android and Kotlin on a project a couple of years ago. |
BrayanDSO
left a comment
There was a problem hiding this comment.
LGTM. Really great having IO in AnkIDroid
|
Pending
Strings
&
squash-merge
|
|
Really glad to see this one in, thank you 🙏🙏 |
|
This is ridiculous, we have image occlusion in AnkiDroid !?!?! @abdnh - champion work 👑 thank you I literally never thought I'd see the day |
* Integrate image occlusion editor * Remove duplicate translation * Reuse translation from upstream * Wrap in withCol * Run addImageOcclusionNotetype in background * Close IO editor after adding/updating note * Do not load field/tag changes for IO note * Add 'Paste Image from Clipboard' button * Use .apply * Remove print * Use bundleOf() * Use ActivityResultContracts.GetContent() * Fix Edit Occlusions button * Fix reviewer note not being updated after editing IO note * Use undoableOp ankidroid#14884 (comment) * Prefer structural equality * Avoid Hungarian notation * Add bottom padding to IO buttons container * Add TODO about image formats * Replace START animation
This comment has been minimized.
This comment has been minimized.
|
Hi there @abdnh! This is the OpenCollective Notice for PRs merged from 2023-12-01 through 2023-12-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 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! |
Purpose / Description
This integrates the image occlusion editor with NoteEditor.
Fixes
Fixes #6980
Approach
The IO editor is accessed from the normal editor via a button.
How Has This Been Tested?
Manual testing in the emulator. Android 14.0, API level 34
Checklist
Please, go through these checks before submitting the PR.
Issues