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

Add Crop functionality #6543

Merged
merged 24 commits into from
Jun 28, 2020
Merged

Add Crop functionality #6543

merged 24 commits into from
Jun 28, 2020

Conversation

mikehardy
Copy link
Member

Pull Request template

Purpose / Description

There was no way to crop images previously, but cameras especially are generating very large images these days

This allows the advanced editor's image field component to crop images, and prompts users to do so for photos or if the image is large

This is entirely based on the work from @NightXlt - everything about how to do the Intent correctly, to the URI / File path conversion is their work

I took the PR contents of #5301 and re-factored them to fit better with the work done since @NightXlt originally submitted the work, then I tested, tested, and tested some more with little fixes related to preview handling and button visibility added in.

Finally, I deconstructed the work into chunks as small as I could so review would be easier.

Please understand the commit separation was done after the fact and there may be a couple cross-dependencies still present in the commit. The separate commits are for easier review but I make no guarantee they will function separately, testing has only been performed with the complete work

Fixes

Fixes #2554
Obsoletes #5301

Approach

The basic idea is to

  • handle the path internally with a path backup
  • add the general ability to create temporary files
  • add the general ability to generate URIs for our cache files
  • add the general ability to translate between URIs and Paths
  • ask the system to crop when we want, with a URI to a new internal file for the result

How Has This Been Tested?

API16, 18, 24, 29, 30 emulators

The scenarios I ran are:

  • add note, paperclip, add image, gallery, verify preview, choose crop, do the crop, verify preview, save, note editor preview
  • same as above but camera
  • save note, view note verify images

Note that after adding images but before you save the note, the field has data and the paperclip menu behaves differently than if you were editing a note and it had data because the paperclip menu listener hasn't re-been rebound. This is an existing bug and not addressed here

Same as above but with an existing note with image data:

  • edit note (with image data), paperclip (opens advanced image editor w/preview), crop, do crop+return, verify image, save, preview from note editor, verify image, return and save note, view note and verify images

Learning (optional, can help others)

Of interest from this PR:

Attempting to implement an "EDIT" intent vs stricly crop is better but fails in mysterious ways, especially with API30

API30 in general may require more testing

Of interest that existed prior to the PR and still exist:

The advanced editor is rough to deal with, it's pretty fluffy with regard to number of objects and how control passes around

The paperclip menu behaves inconsistently right now based on whether the note had data in the field to start or was empty. If empty to start it will show a popup menu, if it had data to start it sends you right to the advanced editor.

The advanced editor doesn't handle the case of multiple things in the same a field very well.

The advanced editor doesn't handle the case of editing a multimedia field very well (it just keeps appending the edits) but at least with preview from note editor this is easy to fix

Checklist

Please, go through these checks before submitting the PR.

  • You have not changed whitespace unnecessarily (it makes diffs hard to read)
  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • Your code follows the style of the project (e.g. never omit braces in if statements)
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code

@mikehardy
Copy link
Member Author

I want to explicitly note that there is a bounty on the upstream issue and @NightXlt should get the proceeds https://www.bountysource.com/issues/29271594-crop-edit-image-before-adding-to-a-note - it was important to reshape his PR a bit but all I did was bend his work containing the original effort to the current codebase

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.

This is one of those things that I'll want to test manually.

It looks OK, but there are so many edge cases that we're bound to run into which are going to slip through the testing net.

Personally, that's acceptable for a new feature as long as we don't have code paths which will crash, and we're performing appropriate exception reporting. Could we add a (beta) label to the Crop Image button, just to set expectations?

Could we get a TODO that this should ideally be both unit tested and instrumentation tested, as we're got a very high regression risk due to the variety of implementations, APIs and inputs

I'll also note that there are currently bugs in this area, which revolve around trying to extract a file from a URI, rather than copying out of a InputStream from a ContentProvider: #5973

AnkiDroid/src/main/res/values/16-multimedia-editor.xml Outdated Show resolved Hide resolved
Uri uri = FileProvider.getUriForFile(this, this.getApplicationContext().getPackageName() + ".apkgfileprovider", file);
DecimalFormat decimalFormat = new DecimalFormat(".00");
String size = decimalFormat.format(length);
String content = getString(R.string.save_dialog_content, size);
Copy link
Member

Choose a reason for hiding this comment

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

Maybe go with Formatter.formatShortFileSize.

You could bring the format string inside the resource.

} else if ("content".equalsIgnoreCase(uri.getScheme())) {
imagePath = getImagePathFromContentResolver(context, uri, null);
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
imagePath = uri.getPath();
Copy link
Member

Choose a reason for hiding this comment

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

Has this path been exercised on a later API? I know there were changes to the way file:// data could be accessed

Copy link
Member Author

Choose a reason for hiding this comment

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

Lots of changes - I exercised all the functions on later APIs, but I'm not sure if every case was hit. It should be that on later APIs people are providing files via content provider so this is not actually hit, but I'm not sure?

Copy link
Member

Choose a reason for hiding this comment

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

From my understanding: yes and no. they should, but I think a user using an old file browser on a later API can still trigger a crash on this path (or sometimes using the file path in a content provider, but we check this path already).

Might just be best to confirm that exchanging these paths for a throw will send an exception report and won't crash the app.

Where's the can of worms emoji?

AnkiDroid/src/main/res/xml/filepaths.xml Show resolved Hide resolved
@mikehardy
Copy link
Member Author

Nice - thanks for the review. Lots to chew on, I'll address what I can, but I don't have the time to add automated tests as I was just carrying this over the finish line. It was all tested as mentioned in a full skew of emulators from the perspective of the API breaks in the code. It was asserted quite strongly in the original PR that there are device-specific faults requiring some of the strangest bits.

I thought about labeling beta or something as well, but honestly it seems to work well, so I didn't. I'll address everything in a batch in the next day or two though

@david-allison
Copy link
Member

david-allison commented Jun 24, 2020

Thanks, agreed that (sadly) it's not the right time/place to be adding tests, but it's one of the aspects of the program that could really do with it.

Addressing the points in the OP, Anki's somewhat moved from the concept of "One image per field" to "potentially mixed content per field". We're still mostly following the old model in our note editor, and that'll cause UX issues.

@NightXlt
Copy link

Thanks, agreed that (sadly) it's not the right time/place to be adding tests, but it's one of the aspects of the program that could really do with it.

Addressing the points in the OP, Anki's somewhat moved from the concept of "One image per field" to "potentially mixed content per field". We're still mostly following the old model in our note editor, and that'll cause UX issues.

Maybe next three days I would be available, I will add some comment and give you reply @mikehardy @david-allison-1

@mikehardy
Copy link
Member Author

Here's a parallel APK you can use for side-by-side testing (I chose the 'D' variant randomly) for easy testing if you guys want. @NightXlt I would especially love if you can check it on the devices you knew had problems (Huawei? Xioami? I'm not sure)

AnkiDroid-2.12alpha16-crop.parallel.D.apk.zip

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.

Thanks!

  • Load Image From Gallery
  • Crop
  • Back

"You may wish to compress" message shows.

  • Crop
  • "Couldn't load the image"

  • Select Image from Gallery (471KB)
  • Crop
  • Image is now 1.02MB. I assume this is the compression stage making things larger.
  • Save
  • Current image size is X, do you want to crop it?

  • "Current image size is X, do you want to crop it".
    • "Cancel" is ambiguous here.
    • Maybe "Crop", "Save/Continue" "Cancel"?

What does a user expect out of "crop" if they have two cards with the same image? Currently it only affects one card.

@mikehardy
Copy link
Member Author

It would be useful to add file sizes as Timber.d output at each stage to see what is happening where

That first series you did is something interesting, What API? When you say "Back", like back button, or back like the checkmark to go back?

Crop copies before edit at this point, I think that's the majority use case. I could see it going either way but clobbering would be altering the code of course

@david-allison
Copy link
Member

That first series you did is something interesting, What API? When you say "Back", like back button, or back like the checkmark to go back?

The Android OS back button on my device. "Crop Image" - select "Crop Picture" (Huawei intent for cropping) - "Press Android Back button (triangle)" once the cropping UI appears

@mikehardy mikehardy added Needs Author Reply Waiting for a reply from the original author and removed Needs Review labels Jun 25, 2020
@mikehardy mikehardy self-assigned this Jun 25, 2020
@mikehardy
Copy link
Member Author

I changed the dialog to "OK" vs "No" because Cancel was ambiguous yes

I changed the default rotateAndCompress format from PNG @90% to JPG @90% after adding size statements that showed rotateAndCompress was bloating the size. Now it's actually shrinking. Why was it PNG before? No idea

Just curious, were you running with system dev setting don't save activities? Because none of this stuff in advanced editor works with that AFAICT

Still working through your scenarios but cropping fails on macOS emulators, I can only test it on linux ugh

@david-allison
Copy link
Member

david-allison commented Jun 26, 2020

I changed the default rotateAndCompress format from PNG @90% to JPG @90% after adding size statements that showed rotateAndCompress was bloating the size. Now it's actually shrinking. Why was it PNG before? No idea

Could a TODO/Defect Ticket/Fix be added:

  • To change the compress stage to be a noop if the image turned out to be larger
  • Change the filename if we're changing the internal representation of the file from png to jpg

Just curious, were you running with system dev setting don't save activities? Because none of this stuff in advanced editor works with that AFAICT

AFAIK, no. Taking camera images or selecting from my phone gallery doesn't work if Don't Keep Activities is enabled.

Let me know which scenarios you'd like better debugging output for

@mikehardy
Copy link
Member Author

The filename changes every time in our implementation so there will never be collision
Will open a ticket to nop the compress if results are unsatisfactory though interplay with rotate will need to be considered there...

Your scenarios seem fine just I switch between main machine every couple days (from Linux to macOS in order to do Xcode-y react-native-y iOS-y things) and I was on my macOS machine last couple days, which doesn't handle crop in emulators, so haven't been able to test that. Will tack this down when I'm back on linux later today

@mikehardy
Copy link
Member Author

whew! This one is better. Still hard to tease apart the commits post-hoc but there was a reason to do this much change:

  • it handles the ephemeral "file does not actually exist" files now with internalizeFile()
  • it correctly clean up file droppings everywhere and hives them off in the cache
  • it handles activity restart now so go ahead and throw away activities for image field controller anyway
  • it is much more cautious about all sorts of errors
  • it de-dupiclates the crop dialog code

You'll probably find a scenario that breaks it but it's getting more hardened and featureful at the same time so I'm not too concerned if you can still break 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.

Still needs a manual test

@david-allison
Copy link
Member

david-allison commented Jun 28, 2020

Happy path works well. One bug relating to invalid images (unsure if we want to hold this for later, or deal with now).

  • Select Valid Image
  • Select Invalid Image (video/I have some corrupt/empty files)
  • No Error message
  • Preview still shows valid image
  • Saving leaves a "file not found" placeholder on the note

Note line:

2020-06-28 16:24:43.310 9481-9481/com.ichi2.anki I/BasicImageFieldController: setImagePreview() could not process image /storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624435939946859804876207.jpg

logcat ```

2020-06-28 16:24:16.821 9481-9481/com.ichi2.anki W/Settings: Setting device_provisioned has moved from android.provider.Settings.Secure to android.provider.Settings.Global.
2020-06-28 16:24:20.398 9481-9481/com.ichi2.anki I/NoteEditor: NoteEditor:: Multimedia button pressed for field 2
2020-06-28 16:24:21.419 9481-9481/com.ichi2.anki I/NoteEditor: NoteEditor:: Add image button pressed
2020-06-28 16:24:21.469 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - NoteEditor
2020-06-28 16:24:21.493 9481-9481/com.ichi2.anki W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@1fa29d1
2020-06-28 16:24:21.504 9481-9481/com.ichi2.anki D/AnkiDroidApp: AnkiDroidApp::getLanguageConfig - setting locale to en_GB
2020-06-28 16:24:21.511 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onCreate - MultimediaEditFieldActivity
2020-06-28 16:24:21.579 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: recreateEditingUi()
2020-06-28 16:24:21.580 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity$UIRecreationHandler: onPreFieldControllerReplacement
2020-06-28 16:24:21.581 9481-9481/com.ichi2.anki D/BasicImageFieldController: loadInstanceState but null so nothing to load
2020-06-28 16:24:21.581 9481-9481/com.ichi2.anki D/BasicImageFieldController: createUI()
2020-06-28 16:24:21.614 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity$UIRecreationHandler: onPostUICreation. State: 0
2020-06-28 16:24:21.616 9481-9481/com.ichi2.anki D/ActivityThread: add activity client record, r= ActivityRecord{bbf7ef6 token=android.os.BinderProxy@1fa29d1 {com.ichi2.anki/com.ichi2.anki.multimediacard.activity.MultimediaEditFieldActivity}} token= android.os.BinderProxy@1fa29d1
2020-06-28 16:24:21.620 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - MultimediaEditFieldActivity
2020-06-28 16:24:21.626 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - MultimediaEditFieldActivity
2020-06-28 16:24:21.628 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): MultimediaEditFieldActivity
2020-06-28 16:24:21.628 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:24:21.630 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:24:21.723 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onCreateOptionsMenu() - mField.getType() = IMAGE
2020-06-28 16:24:21.826 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - NoteEditor
2020-06-28 16:24:21.829 9481-9481/com.ichi2.anki D/WidgetStatus: WidgetStatus.update(): already running or not enabled
2020-06-28 16:24:21.830 9481-9481/com.ichi2.anki D/UIUtils: saveCollectionInBackground: start
2020-06-28 16:24:21.832 9481-9481/com.ichi2.anki I/NoteEditor: Saving instance
2020-06-28 16:24:21.832 9481-10598/com.ichi2.anki D/CollectionTask: doInBackgroundSaveCollection
2020-06-28 16:24:21.838 9481-9481/com.ichi2.anki D/UIUtils: saveCollectionInBackground: finished
2020-06-28 16:24:21.838 9481-9481/com.ichi2.anki D/CollectionTask: enabling garbage collection of mPreviousTask...
2020-06-28 16:24:22.958 9481-9481/com.ichi2.anki W/Settings: Setting device_provisioned has moved from android.provider.Settings.Secure to android.provider.Settings.Global.
2020-06-28 16:24:23.106 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - MultimediaEditFieldActivity
2020-06-28 16:24:24.854 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - MultimediaEditFieldActivity
2020-06-28 16:24:24.876 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onSaveInstanceState - saving state
2020-06-28 16:24:24.877 9481-9481/com.ichi2.anki D/BasicImageFieldController: saveInstanceState
2020-06-28 16:24:24.877 9481-9481/com.ichi2.anki D/BasicImageFieldController: saveInstanceState
2020-06-28 16:24:33.589 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onActivityResult()
2020-06-28 16:24:33.590 9481-9481/com.ichi2.anki D/BasicImageFieldController: onActivityResult()
2020-06-28 16:24:33.591 9481-9481/com.ichi2.anki I/BasicImageFieldController: handleSelectImageIntent() Intent: Intent { dat=content://media/external/images/media/522243 flg=0x1 (has extras) }. extras: media-path
2020-06-28 16:24:33.593 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImageUri for data Intent { dat=content://media/external/images/media/522243 flg=0x1 (has extras) }
2020-06-28 16:24:33.594 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() URI: content://media/external/images/media/522243
2020-06-28 16:24:33.599 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromContentResolver() content://media/external/images/media/522243
2020-06-28 16:24:33.634 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImageInfoFromContentResolver() decoded image info path/name /storage/emulated/0/Download/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg
2020-06-28 16:24:33.635 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() returning path/name /storage/emulated/0/Download/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg
2020-06-28 16:24:33.636 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() URI: content://media/external/images/media/522243
2020-06-28 16:24:33.640 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromContentResolver() content://media/external/images/media/522243
2020-06-28 16:24:33.648 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImageInfoFromContentResolver() decoded image info path/name /storage/emulated/0/Download/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg
2020-06-28 16:24:33.649 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() returning path/name /storage/emulated/0/Download/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg/methode_times_prod_web_bin_2483c964-a03e-11ea-b3fd-83a0d4cc538d.jpg
2020-06-28 16:24:33.654 9481-9481/com.ichi2.anki D/BasicImageFieldController: internalizeUri() got file path for direct copy from Uri content://media/external/images/media/522243
2020-06-28 16:24:33.664 9481-9481/com.ichi2.anki D/BasicImageFieldController: getUriForFile() /storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624336135621249303170398.jpg
2020-06-28 16:24:33.666 9481-9481/com.ichi2.anki I/BasicImageFieldController: handleSelectImageIntent() Decoded image: '/storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624336135621249303170398.jpg'
2020-06-28 16:24:33.705 9481-9481/com.ichi2.anki D/BasicImageFieldController: setPreviewImage path /storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624336135621249303170398.jpg has size 837307
2020-06-28 16:24:33.714 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - MultimediaEditFieldActivity
2020-06-28 16:24:33.727 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - MultimediaEditFieldActivity
2020-06-28 16:24:33.736 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): MultimediaEditFieldActivity
2020-06-28 16:24:33.737 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:24:33.738 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:24:33.742 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onCreateOptionsMenu() - mField.getType() = IMAGE
2020-06-28 16:24:35.217 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - MultimediaEditFieldActivity
2020-06-28 16:24:36.999 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - MultimediaEditFieldActivity
2020-06-28 16:24:37.020 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onSaveInstanceState - saving state
2020-06-28 16:24:37.021 9481-9481/com.ichi2.anki D/BasicImageFieldController: saveInstanceState
2020-06-28 16:24:37.021 9481-9481/com.ichi2.anki D/BasicImageFieldController: saveInstanceState
2020-06-28 16:24:43.253 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onActivityResult()
2020-06-28 16:24:43.253 9481-9481/com.ichi2.anki D/BasicImageFieldController: onActivityResult()
2020-06-28 16:24:43.255 9481-9481/com.ichi2.anki I/BasicImageFieldController: handleSelectImageIntent() Intent: Intent { dat=content://media/external/images/media/32790 flg=0x1 (has extras) }. extras: media-path
2020-06-28 16:24:43.256 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImageUri for data Intent { dat=content://media/external/images/media/32790 flg=0x1 (has extras) }
2020-06-28 16:24:43.257 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() URI: content://media/external/images/media/32790
2020-06-28 16:24:43.262 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromContentResolver() content://media/external/images/media/32790
2020-06-28 16:24:43.280 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImageInfoFromContentResolver() decoded image info path/name /storage/emulated/0/Facebook Messenger/Media/FB_IMG_15354798651055743.jpg/FB_IMG_15354798651055743.jpg
2020-06-28 16:24:43.281 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() returning path/name /storage/emulated/0/Facebook Messenger/Media/FB_IMG_15354798651055743.jpg/FB_IMG_15354798651055743.jpg
2020-06-28 16:24:43.282 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() URI: content://media/external/images/media/32790
2020-06-28 16:24:43.286 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromContentResolver() content://media/external/images/media/32790
2020-06-28 16:24:43.295 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImageInfoFromContentResolver() decoded image info path/name /storage/emulated/0/Facebook Messenger/Media/FB_IMG_15354798651055743.jpg/FB_IMG_15354798651055743.jpg
2020-06-28 16:24:43.295 9481-9481/com.ichi2.anki D/BasicImageFieldController: getImagePathFromUri() returning path/name /storage/emulated/0/Facebook Messenger/Media/FB_IMG_15354798651055743.jpg/FB_IMG_15354798651055743.jpg
2020-06-28 16:24:43.301 9481-9481/com.ichi2.anki D/BasicImageFieldController: internalizeUri() got file path for direct copy from Uri content://media/external/images/media/32790
2020-06-28 16:24:43.305 9481-9481/com.ichi2.anki D/BasicImageFieldController: getUriForFile() /storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624435939946859804876207.jpg
2020-06-28 16:24:43.307 9481-9481/com.ichi2.anki I/BasicImageFieldController: handleSelectImageIntent() Decoded image: '/storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624435939946859804876207.jpg'
2020-06-28 16:24:43.308 9481-9481/com.ichi2.anki D/skia: --- Failed to create image decoder with message 'unimplemented'
2020-06-28 16:24:43.309 9481-9481/com.ichi2.anki D/skia: --- Failed to create image decoder with message 'unimplemented'
2020-06-28 16:24:43.310 9481-9481/com.ichi2.anki I/BasicImageFieldController: setImagePreview() could not process image /storage/emulated/0/Android/data/com.ichi2.anki/cache/temp-photos/img_202006281624435939946859804876207.jpg
2020-06-28 16:24:43.315 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - MultimediaEditFieldActivity
2020-06-28 16:24:43.319 9481-9490/com.ichi2.anki W/System: A resource failed to call close.
2020-06-28 16:24:43.323 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - MultimediaEditFieldActivity
2020-06-28 16:24:43.325 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): MultimediaEditFieldActivity
2020-06-28 16:24:43.326 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:24:43.328 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:24:43.336 9481-9481/com.ichi2.anki D/MultimediaEditFieldActivity: onCreateOptionsMenu() - mField.getType() = IMAGE
2020-06-28 16:24:45.177 9481-9481/com.ichi2.anki I/MultimediaEditFieldActivity: Save button pressed
2020-06-28 16:24:45.178 9481-9481/com.ichi2.anki I/AnkiActivity: finishWithoutAnimation
2020-06-28 16:24:45.193 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - MultimediaEditFieldActivity
2020-06-28 16:24:45.214 9481-9481/com.ichi2.anki D/NoteEditor: onActivityResult() with request/result: 2/-1
2020-06-28 16:24:45.234 9481-9481/com.ichi2.anki D/Utils: Creating new file... = /storage/emulated/0/AnkiDroidGithub/collection.media/img_202006281624435939946859804876207.jpg
2020-06-28 16:24:45.219 9481-9481/com.ichi2.anki W/com.ichi2.anki: type=1400 audit(0.0:6684): avc: granted { create } for name="img_202006281624435939946859804876207.jpg" scontext=u:r:untrusted_app:s0:c118,c257,c512,c768 tcontext=u:object_r:sdcardfs:s0:c118,c257,c512,c768 tclass=file
2020-06-28 16:24:45.234 9481-9481/com.ichi2.anki D/Utils: Creating new file... = /storage/emulated/0/AnkiDroidGithub/collection.media/img_202006281624435939946859804876207.jpg
2020-06-28 16:24:45.739 9481-9481/com.ichi2.anki W/com.ichi2.anki: type=1400 audit(0.0:6685): avc: granted { create } for name="img_202006281624435939946859804876207.jpg" scontext=u:r:untrusted_app:s0:c118,c257,c512,c768 tcontext=u:object_r:sdcardfs:s0:c118,c257,c512,c768 tclass=file
2020-06-28 16:24:45.755 9481-9481/com.ichi2.anki D/Utils: Finished writeToFile!
2020-06-28 16:24:45.756 9481-9481/com.ichi2.anki D/Utils: Utils.writeToFile: Size: 0 Kb, Duration: 0 s, Speed: 0 Kb/s
2020-06-28 16:24:45.757 9481-9481/com.ichi2.anki D/Media: Marking media file addition in media db: img_202006281624435939946859804876207.jpg
2020-06-28 16:24:45.910 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - NoteEditor
2020-06-28 16:24:45.916 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - NoteEditor
2020-06-28 16:24:45.918 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): NoteEditor
2020-06-28 16:24:45.919 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:24:45.920 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:24:45.923 9481-9481/com.ichi2.anki I/Choreographer: Skipped 42 frames! The application may be doing too much work on its main thread.
2020-06-28 16:24:46.060 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - MultimediaEditFieldActivity
2020-06-28 16:24:46.066 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onDestroy - MultimediaEditFieldActivity
2020-06-28 16:24:46.068 9481-9481/com.ichi2.anki D/ActivityThread: Remove activity client record, r= ActivityRecord{bbf7ef6 token=android.os.BinderProxy@1fa29d1 {com.ichi2.anki/com.ichi2.anki.multimediacard.activity.MultimediaEditFieldActivity}} token= android.os.BinderProxy@1fa29d1
2020-06-28 16:24:47.835 9481-9481/com.ichi2.anki I/NoteEditor: NoteEditor:: Save note button pressed
2020-06-28 16:24:47.837 9481-9481/com.ichi2.anki I/AnkiActivity: finishWithAnimation 1
2020-06-28 16:24:47.849 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - NoteEditor
2020-06-28 16:24:47.880 9481-9481/com.ichi2.anki I/NavigationDrawerActivity: Handling Activity Result: 0. Result: -1
2020-06-28 16:24:47.881 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: General Notifications/AnkiDroid
2020-06-28 16:24:47.885 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: Synchronization/Synchronization
2020-06-28 16:24:48.076 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: Global Reminders/Cards due
2020-06-28 16:24:48.079 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: Deck Reminders/Reminders
2020-06-28 16:24:48.085 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Saving card...
2020-06-28 16:24:48.086 9481-10976/com.ichi2.anki I/CollectionTask: Obtaining card
2020-06-28 16:24:48.089 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - Reviewer
2020-06-28 16:24:48.108 9481-10976/com.ichi2.anki D/Collection: [1593357888] SchedV2.java:getCard(): 'mData': , 'mDid': 1592047443962, 'mDue': 44, 'mElapsedTime': 0.0, 'mFactor': 2500, 'mFlags': 1, 'mId': 1592743150838, 'mIvl': 1, 'mLapses': 0, 'mLastIvl': 0, 'mLeft': 1001, 'mMod': 1593357749, 'mNid': 1592743150836, 'mODid': 0, 'mODue': 0, 'mOrd': 0, 'mQueue': 2, 'mReps': 64, 'mType': 2, 'mUsn': -1, 'mWasNew': false
2020-06-28 16:24:48.109 9481-10976/com.ichi2.anki D/Note: load()
2020-06-28 16:24:48.121 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - Reviewer
2020-06-28 16:24:48.124 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): Reviewer
2020-06-28 16:24:48.124 9481-10977/com.ichi2.anki D/CollectionTask: doInBackgroundUpdateNote
2020-06-28 16:24:48.124 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:24:48.126 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:24:48.149 9481-10977/com.ichi2.anki D/Collection: [1593357888] Card.java:flush(): 'mData': , 'mDid': 1592047443962, 'mDue': 44, 'mElapsedTime': 4.454999923706055, 'mFactor': 2500, 'mFlags': 1, 'mId': 1592743150838, 'mIvl': 1, 'mLapses': 0, 'mLastIvl': 0, 'mLeft': 1001, 'mMod': 1593357888, 'mNid': 1592743150836, 'mODid': 0, 'mODue': 0, 'mOrd': 0, 'mQueue': 2, 'mReps': 64, 'mType': 2, 'mUsn': -1, 'mWasNew': false
2020-06-28 16:24:48.151 9481-10977/com.ichi2.anki D/Note: load()
2020-06-28 16:24:48.155 9481-10977/com.ichi2.anki D/Note: load()
2020-06-28 16:24:48.175 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:24:48.299 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: displayCardQuestion()
2020-06-28 16:24:48.306 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: updateCard()
2020-06-28 16:24:48.308 9481-9481/com.ichi2.anki D/Sound: expandSounds
2020-06-28 16:24:48.315 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:24:48.315 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: fillFlashcard()
2020-06-28 16:24:48.316 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:24:48.328 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Question successfully shown for card id 1592743150838
2020-06-28 16:24:48.341 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:24:48.385 9481-9481/com.ichi2.anki D/CollectionTask: enabling garbage collection of mPreviousTask...
2020-06-28 16:24:48.401 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:24:48.453 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: displayCardQuestion()
2020-06-28 16:24:48.459 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: updateCard()
2020-06-28 16:24:48.460 9481-9481/com.ichi2.anki D/Sound: expandSounds
2020-06-28 16:24:48.465 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:24:48.466 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: fillFlashcard()
2020-06-28 16:24:48.467 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:24:48.475 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Question successfully shown for card id 1592743150838
2020-06-28 16:24:48.488 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:24:48.530 9481-9481/com.ichi2.anki D/CollectionTask: enabling garbage collection of mPreviousTask...
2020-06-28 16:24:48.532 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: Fullscreen delayed hide in 200ms
2020-06-28 16:24:48.602 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered
2020-06-28 16:24:48.613 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - NoteEditor
2020-06-28 16:24:48.621 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onDestroy - NoteEditor
2020-06-28 16:24:48.628 9481-9481/com.ichi2.anki D/ActivityThread: Remove activity client record, r= ActivityRecord{818c33d token=android.os.BinderProxy@46439e5 {com.ichi2.anki/com.ichi2.anki.NoteEditor}} token= android.os.BinderProxy@46439e5
2020-06-28 16:24:48.650 9481-9481/com.ichi2.anki I/chromium: [INFO:CONSOLE(1)] "Uncaught ReferenceError: onPageFinished is not defined", source: file:///storage/emulated/0/AnkiDroidGithub/collection.media/viewer.html (1)
2020-06-28 16:24:48.756 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered
2020-06-28 16:24:48.757 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered
2020-06-28 16:24:49.818 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Show answer button pressed
2020-06-28 16:24:49.819 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: displayCardAnswer()
2020-06-28 16:24:49.821 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: correct answer =
2020-06-28 16:24:49.821 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: user answer =
2020-06-28 16:24:49.822 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: updateCard()
2020-06-28 16:24:49.823 9481-9481/com.ichi2.anki D/Sound: expandSounds
2020-06-28 16:24:49.830 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:24:49.830 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: fillFlashcard()
2020-06-28 16:24:49.830 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:24:49.952 9481-9481/com.ichi2.anki I/chromium: [INFO:CONSOLE(91)] "Uncaught TypeError: Cannot read property 'getElementsByTagName' of undefined", source: file:///storage/emulated/0/AnkiDroidGithub/collection.media/viewer.html (91)
2020-06-28 16:24:49.952 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered
2020-06-28 16:24:49.954 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered
2020-06-28 16:24:52.986 9481-9481/com.ichi2.anki I/Reviewer: Reviewer:: Edit note button pressed
2020-06-28 16:24:53.038 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - Reviewer
2020-06-28 16:24:53.040 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: onPause()
2020-06-28 16:24:53.066 9481-9481/com.ichi2.anki W/ActivityThread: handleWindowVisibility: no activity for token android.os.BinderProxy@d432ed3
2020-06-28 16:24:53.076 9481-9481/com.ichi2.anki D/AnkiDroidApp: AnkiDroidApp::getLanguageConfig - setting locale to en_GB
2020-06-28 16:24:53.082 9481-9481/com.ichi2.anki D/NoteEditor: onCreate()
2020-06-28 16:24:53.083 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onCreate - NoteEditor
2020-06-28 16:24:53.162 9481-9481/com.ichi2.anki D/AnkiActivity: AnkiActivity.startLoadingCollection()
2020-06-28 16:24:53.163 9481-9481/com.ichi2.anki D/AnkiActivity: Synchronously calling onCollectionLoaded
2020-06-28 16:24:53.164 9481-9481/com.ichi2.anki D/NoteEditor: NoteEditor() onCollectionLoaded: caller: 1
2020-06-28 16:24:53.179 9481-9481/com.ichi2.anki D/NoteEditor: updateCards()
2020-06-28 16:24:53.180 9481-9481/com.ichi2.anki D/NoteEditor: updateCards() template count is 1
2020-06-28 16:24:53.302 9481-9481/com.ichi2.anki I/NoteEditor: onCollectionLoaded() Edit note activity successfully started with card id 1592743150838
2020-06-28 16:24:53.308 9481-9481/com.ichi2.anki D/ActivityThread: add activity client record, r= ActivityRecord{ca0003a token=android.os.BinderProxy@d432ed3 {com.ichi2.anki/com.ichi2.anki.NoteEditor}} token= android.os.BinderProxy@d432ed3
2020-06-28 16:24:53.311 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - NoteEditor
2020-06-28 16:24:53.319 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - NoteEditor
2020-06-28 16:24:53.321 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): NoteEditor
2020-06-28 16:24:53.322 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:24:53.324 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:24:53.814 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - Reviewer
2020-06-28 16:24:53.824 9481-9481/com.ichi2.anki D/WidgetStatus: WidgetStatus.update(): already running or not enabled
2020-06-28 16:24:53.825 9481-9481/com.ichi2.anki D/UIUtils: saveCollectionInBackground: start
2020-06-28 16:24:53.831 9481-10977/com.ichi2.anki D/CollectionTask: doInBackgroundSaveCollection
2020-06-28 16:24:53.831 9481-10977/com.ichi2.anki I/Collection: flush - Saving information to DB...
2020-06-28 16:24:53.847 9481-9481/com.ichi2.anki D/UIUtils: saveCollectionInBackground: finished
2020-06-28 16:24:53.848 9481-9481/com.ichi2.anki D/CollectionTask: enabling garbage collection of mPreviousTask...
2020-06-28 16:24:54.657 9481-9481/com.ichi2.anki W/Settings: Setting device_provisioned has moved from android.provider.Settings.Secure to android.provider.Settings.Global.
2020-06-28 16:24:57.263 9481-9481/com.ichi2.anki D/TextClassifierService: No configured system TextClassifierService
2020-06-28 16:24:57.265 9481-9481/com.ichi2.anki D/TextClassifierService: No configured system TextClassifierService
2020-06-28 16:24:57.268 9481-10994/com.ichi2.anki D/TextClassifierService: No configured system TextClassifierService
2020-06-28 16:24:57.628 9481-10994/com.ichi2.anki D/androidtc: Loading ModelFile { path=/data/misc/textclassifier/textclassifier.model name=textclassifier.model version=609 locales=en }
2020-06-28 16:24:58.620 9481-9481/com.ichi2.anki D/TextClassifierService: No configured system TextClassifierService
2020-06-28 16:24:58.623 9481-10977/com.ichi2.anki D/TextClassifierService: No configured system TextClassifierService
2020-06-28 16:25:03.462 9481-9481/com.ichi2.anki I/NoteEditor: NoteEditor:: Save note button pressed
2020-06-28 16:25:03.464 9481-9481/com.ichi2.anki I/AnkiActivity: finishWithAnimation 1
2020-06-28 16:25:03.474 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onPause - NoteEditor
2020-06-28 16:25:03.507 9481-9481/com.ichi2.anki I/NavigationDrawerActivity: Handling Activity Result: 0. Result: -1
2020-06-28 16:25:03.509 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: General Notifications/AnkiDroid
2020-06-28 16:25:03.512 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: Synchronization/Synchronization
2020-06-28 16:25:03.517 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: Global Reminders/Cards due
2020-06-28 16:25:03.519 9481-9481/com.ichi2.anki I/CompatV26: Creating notification channel with id/name: Deck Reminders/Reminders
2020-06-28 16:25:03.659 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Saving card...
2020-06-28 16:25:03.659 9481-10994/com.ichi2.anki I/CollectionTask: Obtaining card
2020-06-28 16:25:03.664 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStart - Reviewer
2020-06-28 16:25:03.670 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onResume - Reviewer
2020-06-28 16:25:03.671 9481-9481/com.ichi2.anki D/UsageAnalytics: sendAnalyticsScreenView(): Reviewer
2020-06-28 16:25:03.672 9481-9481/com.ichi2.anki D/UsageAnalytics: getOptIn() status: false
2020-06-28 16:25:03.673 9481-9481/com.ichi2.anki D/DialogHandler: Reading persistent message
2020-06-28 16:25:03.678 9481-10994/com.ichi2.anki D/Collection: [1593357903] SchedV2.java:getCard(): 'mData': , 'mDid': 1592047443962, 'mDue': 44, 'mElapsedTime': 0.0, 'mFactor': 2500, 'mFlags': 1, 'mId': 1592743150838, 'mIvl': 1, 'mLapses': 0, 'mLastIvl': 0, 'mLeft': 1001, 'mMod': 1593357888, 'mNid': 1592743150836, 'mODid': 0, 'mODue': 0, 'mOrd': 0, 'mQueue': 2, 'mReps': 64, 'mType': 2, 'mUsn': -1, 'mWasNew': false
2020-06-28 16:25:03.678 9481-10994/com.ichi2.anki D/Note: load()
2020-06-28 16:25:03.695 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:25:03.706 9481-10994/com.ichi2.anki D/CollectionTask: Waiting for 3 to finish before starting 7
2020-06-28 16:25:03.708 9481-10994/com.ichi2.anki D/CollectionTask: Finished waiting for 3 to finish. Status= RUNNING
2020-06-28 16:25:03.708 9481-10994/com.ichi2.anki D/CollectionTask: doInBackgroundUpdateNote
2020-06-28 16:25:03.728 9481-10994/com.ichi2.anki D/Collection: [1593357903] Card.java:flush(): 'mData': , 'mDid': 1592047443962, 'mDue': 44, 'mElapsedTime': 4.565000057220459, 'mFactor': 2500, 'mFlags': 1, 'mId': 1592743150838, 'mIvl': 1, 'mLapses': 0, 'mLastIvl': 0, 'mLeft': 1001, 'mMod': 1593357903, 'mNid': 1592743150836, 'mODid': 0, 'mODue': 0, 'mOrd': 0, 'mQueue': 2, 'mReps': 64, 'mType': 2, 'mUsn': -1, 'mWasNew': false
2020-06-28 16:25:03.730 9481-10994/com.ichi2.anki D/Note: load()
2020-06-28 16:25:03.733 9481-10994/com.ichi2.anki D/Note: load()
2020-06-28 16:25:03.820 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: displayCardQuestion()
2020-06-28 16:25:03.827 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: updateCard()
2020-06-28 16:25:03.830 9481-9481/com.ichi2.anki D/Sound: expandSounds
2020-06-28 16:25:03.836 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:25:03.837 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: fillFlashcard()
2020-06-28 16:25:03.837 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:25:03.849 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Question successfully shown for card id 1592743150838
2020-06-28 16:25:03.862 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:25:03.935 9481-9481/com.ichi2.anki D/CollectionTask: enabling garbage collection of mPreviousTask...
2020-06-28 16:25:03.937 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: Fullscreen delayed hide in 200ms
2020-06-28 16:25:04.030 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:25:04.114 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: displayCardQuestion()
2020-06-28 16:25:04.121 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: updateCard()
2020-06-28 16:25:04.121 9481-9481/com.ichi2.anki D/Sound: expandSounds
2020-06-28 16:25:04.128 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:25:04.128 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: fillFlashcard()
2020-06-28 16:25:04.129 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer: base url = file:///storage/emulated/0/AnkiDroidGithub/collection.media/
2020-06-28 16:25:04.138 9481-9481/com.ichi2.anki I/AbstractFlashcardViewer: AbstractFlashcardViewer:: Question successfully shown for card id 1592743150838
2020-06-28 16:25:04.153 9481-9481/com.ichi2.anki D/Collection: undoAvailable() undo size: 0
2020-06-28 16:25:04.209 9481-9481/com.ichi2.anki D/CollectionTask: enabling garbage collection of mPreviousTask...
2020-06-28 16:25:04.317 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onStop - NoteEditor
2020-06-28 16:25:04.326 9481-9481/com.ichi2.anki I/AnkiActivity: AnkiActivity::onDestroy - NoteEditor
2020-06-28 16:25:04.329 9481-9481/com.ichi2.anki D/ActivityThread: Remove activity client record, r= ActivityRecord{ca0003a token=android.os.BinderProxy@d432ed3 {com.ichi2.anki/com.ichi2.anki.NoteEditor}} token= android.os.BinderProxy@d432ed3
2020-06-28 16:25:04.420 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered
2020-06-28 16:25:04.421 9481-9481/com.ichi2.anki D/AbstractFlashcardViewer$CardViewerWebClient: onPageFinished triggered


</details>

@mikehardy
Copy link
Member Author

Pretty easy to roll through at least the most common unhappy path, I'll throw invalid files at it and see what happens, what I suspect will happen based on what you saw in the logcat is that it will suss out something with regard to the temp-file cleanup work I did where I mess up the accounting somewhere and clean up the wrong thing.

Glad the happy path works! The internalizeFile / URI-handling stuff should be generalizable later for imports and if the person from the mailing list wants to add media insert into the ContentProvider even

@david-allison
Copy link
Member

david-allison commented Jun 28, 2020

I'd recommend adding a Timber.w to BitmapUtil.decodeFile. Let me get the full stack trace.

EDIT: No exception BitmapFactory.decodeStream returns null if provided with a video.

EDIT: Actually... maybe intent.setType("image/*"); on the pick image intent so we don't get videos suggested

Arrays are convenient for the same reason they are dangerous, using
a Pair in place makes it impossible to have anything other than 2
elements, which was the prior intention
@mikehardy
Copy link
Member Author

Makes me think that in the getting help screen we should have a "send exception report right now" button that clears any ACRALimiter clamping and does a broader logcat sweep. If users encounter random problems here or in other parts of the app and we have Timber categorizing correctly, we'll get traces like you just gave me, which would help a lot for recoverable errors

This is where the Bitmap.decode happens, so it can provide useful signal
in case something about the image crop or camera selection resulted in
a bad image

combined with image picker clamped to actual images, we should only
allow valid images to pass through the controller to NoteEditor for save now
@mikehardy
Copy link
Member Author

added in all sorts of protection against ingesting or using bad data, should handle most bad cases

@mikehardy mikehardy added Needs Review and removed Needs Author Reply Waiting for a reply from the original author labels Jun 28, 2020
@mikehardy mikehardy assigned david-allison and unassigned mikehardy Jun 28, 2020
@mikehardy
Copy link
Member Author

I'm going to go ahead and merge this one, it's passing all our happy path tests and I can't make it fail anywhere any more. I will obviously hop on anything that needs mop up but I want to generate an alpha of what will likely become 2.12

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Crop/edit image before adding to a note
3 participants