dankito / RichTextEditor

Rich text WYSIWYG editor for Android and JavaFX
Apache License 2.0
95 stars 36 forks source link

Add image from storage #2

Closed E-Mans-Application closed 6 years ago

E-Mans-Application commented 6 years ago

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 commented 6 years ago

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

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

E-Mans-Application commented 6 years ago

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 commented 6 years ago

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:

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

E-Mans-Application commented 6 years ago

"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 commented 6 years ago

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 commented 6 years ago

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).