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 image from storage #2

Closed
E-Mans-Application opened this issue Jul 17, 2018 · 6 comments
Closed

Add image from storage #2

E-Mans-Application opened this issue Jul 17, 2018 · 6 comments
Labels

Comments

@E-Mans-Application
Copy link
Contributor

Hello,

The only way to insert an image is currently to know the link to it (at least this is the only way I found in the toolbar). Therefore the image can not be displayed anymore if it changes url, or if it is removed from the server it is stored on.

I suggest to add the possibility to choose the image from phone storage.
We can indeed set the src attribute with the content of the image as follows:
<img src="data:image/png;base64,..." />
(Note that image/png must be replaced with the actual mime type of the image.)

I implemented this in one of my app already, and it works:

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case ACTION_PICK_IMAGE:

                    if (data == null) {
                        return;
                    }

                    Uri uri = data.getData();
                    ContentResolver cR = this.getContentResolver();
                    String type = cR.getType(uri);
                    StringBuilder img = new StringBuilder("<img src=\"data:");
                    img.append(type).append(";base64,");

                    try {
                        final Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
                        final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                        bitmap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
                        final byte[] byteArray = byteArrayOutputStream.toByteArray();
                        final String encoded = Base64.encodeToString(byteArray, Base64.DEFAULT);
                        img.append(encoded).append("\">");
                        richTextEditor.setHtml(String.format("%s%s", this.editor.getHtml(), img.toString()));
                       //insertHtml method do not work
                    } catch (final FileNotFoundException e) {
                        e.printStackTrace();
                    } catch (final IOException e) {
                        e.printStackTrace();
                    }
                    break;
            }

        }
    }

This code requires the READ_EXTERNAL_STORAGE permission.
It must be improved because it sometimes throws an OutOfMemoryError.
Maybe using Bitmap.CompressFormat.JPEG and a quality lower than 100 could solve the problem.

@dankito dankito mentioned this issue Jul 25, 2018
@dankito
Copy link
Owner

dankito commented Jul 25, 2018

You're definitely right, there's a button missing to add an image from local storage.

But actually I don't think that it's a good idea to embed an image into html as

  • Base64 rises the image's size by 33 %,
  • the HTML code then becomes quite large (which may leads to an OutOfMemoryException).

But what do you think of adding a button to download images from remote URLs to local storage e. g. to

  • app's private storage,
  • Android image folder,
  • a user selected folder?

@E-Mans-Application
Copy link
Contributor Author

Hello,

I agree that base64-encode images is not a good idea, but the content needs to be accessible from all the devices which run my application, so I cannot use a local path.
I could upload all images to a server, but I do not have extensible storage, so I would have to put limitations on the users.

Downloading images from remote URLs could be useful, it would allow to display the full HTML content even if the user is offline.

@dankito
Copy link
Owner

dankito commented Aug 15, 2018

Just released version 1.2.0.

This now adds the possibility to add images from phone storage.
(Suggestions to improve UI are welcome.)

Also an option to download remote images to phone storage has now been implemented.
So the user then can see full HTML content even if he/she is offline.
But this necessarily sets image path to the local file in HTML.

Some points I don't understand about synchronizing your users data:

  • "I could upload all images to a server, but I do not have extensible storage"
    But how do you then synchronize the HTML content?
    Isn't it possible to use the same synchronization mechanism for the images as you do for the HTML content?
    And if this involves server space, wouldn't it be better to synchronize images separately? First embedding the images in HTML as Base64 would use up 33 % more space. And second you could synchronize the small HTML quite fast so the user would already see it on his/her device while you synchronize the images in the background, but the remaining text could already get displayed.

  • "but the content needs to be accessible from all the devices which run my application, so I cannot use a local path"
    Yes you can. You could for example save the images to app folder and store image's path relatively. So all images reside in the same relative path on all app instances and therefore could unproblematically be displayed on all user's devices.

Do you think this issue now can be closed?
If you have further questions or suggestions regarding synchronization, please file another issue.

@dankito dankito added the v1.2.0 label Aug 15, 2018
@E-Mans-Application
Copy link
Contributor Author

"I could upload all images to a server, but I do not have extensible storage"
But how do you then synchronize the HTML content?

The HTML is stored in a database.
If the image is embedded, it is removed from the HTML when the image is deleted.
If it is uploaded on the server, I have to check all the database entries to see whether the image is still used to delete it if appropriate. That's possible, but I didn't take the time to implement it yet (embedding was easier).

"but the content needs to be accessible from all the devices which run my application, so I cannot use a local path"
You could for example save the images to app folder and store image's path relatively.

I don't quite understand. Doesn't it need each single user to download all the images to its phone local storage?

@dankito
Copy link
Owner

dankito commented Aug 16, 2018

If the image is embedded, it is removed from the HTML when the image is deleted.

That's true, in this case embedding into HTML makes image handling way easier.

For my knowledge and document management app DeepThought I had a similar issue.
What I did was: Before saving I compared the image URLs of the previously saved and the just edited HTML (from head, may contains faults):

List<String> previousImageUrls = Jsoup.parse(previouslySavedHtml).select("img[href]");
List<String> newImageUrls = Jsoup.parse(justEditedHtml).select("img[href]");
// now compare the two lists for added and removed image URLs, e. g.
List<String> removedImageUrls = previousImageUrls.removeAll(newImageUrls);

So you would know which images to remove from and which images to add to server.

I also remember I once did an image synchronizing app with Couchbase Lite as database. First I stored images as Couchbase attachments which also use Base64.
But saving three images in a short time were already enough to fill phone memory on older Androids like Galaxy S 3 and crashing the app with an OutOfMemoryException.

I don't quite understand. Doesn't it need each single user to download all the images to its phone local storage?

Of course he must.
But a small HTML synchronizes way faster.
So you can already show the HTML to the user while synchronizing the images in the background.
And till the user scrolls to the image, the image may is already synchronized, so the user won't see that it has been loaded later.

@E-Mans-Application
Copy link
Contributor Author

I think I will follow your example to upload images.
I didn't choose yet whether I will check only the database entry loaded in the editor (so I would have one upload folder per entry on my server) or if I will check all the entries (so I wouldn't re-upload images which are used elsewhere).

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

No branches or pull requests

2 participants