Sharing multiple files with Gluon ShareService (im

2019-02-25 03:05发布

问题:

We want to know how we can share multiple files (image and txt file) with the Gluon ShareService. Especially how to share an image which was previously taken and stored (in gallery) with the PictureService.

But we need to create a file first with the path and image name. Unfortunately, the PictureService saves the image with the image title consisting of date and time at the moment the picture was taken.

We tried to get the image name with the loadImageFromGallery method but this returns void and opens the recent-screen.

Here what we've tried to share an image:

public void sharePicture() {
    Services.get(PicturesService.class).ifPresent(picturesService -> {
          Image image = picturesService.loadImageFromGallery().get();
      File file= new File("Pictures", image.toString());
      Services.get(ShareService.class).ifPresent(service -> {
        service.share("image/jpg", file);
      });
    });
  }
  • How can we store the image where we want with a title we want?
  • How can we share a file and an image together?

回答1:

You are on the right track, combining different services from Charm Down, in order to select an image from the gallery and share it.

There is a major problem in this approach, though: You can't convert easily a JavaFX Image into a File.

So far the PicturesService returns only a JavaFX Image, and not a File, so we need a way to save that image into a file that we can read and share.

And the process is not easy since on mobile we don't have SwingUtilities.

The initial approach of using a PixelReader to read the image and get a byte array doesn't really work, as it will give you a big raw file that can't be read or share.

I've used this solution that makes use of a PNG encoder to get the byte array of a png from a JavaFX image:

PngEncoderFX encoder = new PngEncoderFX(image, true);
byte[] bytes = encoder.pngEncode();

Then I'll save that byte array into a file in the public storage folder (so it can be shared), that I can retrieve using the `StorageService:

private File getImageFile(Image image) {
    if (image == null) {
        return null;
    }

    // 1. Encode image to png
    PngEncoderFX encoder = new PngEncoderFX(image, true);
    byte[] bytes = encoder.pngEncode();

    // 2.Write byte array to a file in public storage 
    File root = Services.get(StorageService.class)
            .flatMap(storage -> storage.getPublicStorage("Pictures"))
            .orElse(null);
    if (root != null) {
        File file = new File(root, "Image-" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("uuuuMMdd-HHmmss")) + ".png");
        try (FileOutputStream fos = new FileOutputStream(file)) {
            fos.write(bytes);
            return file;
        } catch (IOException ex) {
            System.out.println("Error: " + ex);
        }
    }
    return null;
}

Now, you can call the PicturesService, retrieve the image, save it to the file and finally share it:

Services.get(PicturesService.class).ifPresent(pictures -> {
    // 1. Retrieve picture from gallery
    pictures.loadImageFromGallery().ifPresent(image -> {
        // 2. Convert image to file
        File imageFile = getImageFile(image);

        // 3. Share file
        if (imageFile != null) {
            Services.get(ShareService.class).ifPresent(share -> {
                share.share("image/png", imageFile);
            });
        }
    });
});

Note that you may run into memory issues if you try to encode big images.

Anyway, all the process could be simplified if the PicturesService will return a file in the first place. If you want to file an issue, you can do it here.

EDIT

A possible solution to avoid memory issues, and to reduce the size of the shared file, and based on this solution, is scaling down the original image, if it exceeds certain size, like it is already done in the iOS implementation of the PicturesService:

private Image scaleImage(Image source) {
    // Possible limit based on memory limitations
    double maxResolution = 1280; 

    double width = source.getWidth();
    double height = source.getHeight();
    double targetWidth = width;
    double targetHeight = height;
    if (width > maxResolution || height > maxResolution) {
        double  ratio = width/height;
        if (ratio > 1) {
            targetWidth = maxResolution;
            targetHeight = targetWidth/ ratio;
        }
        else {
            targetHeight = maxResolution;
            targetWidth = targetHeight * ratio;
        }
    }

    ImageView imageView = new ImageView(source);
    imageView.setPreserveRatio(true);
    imageView.setFitWidth(targetWidth);
    imageView.setFitHeight(targetHeight);
    return imageView.snapshot(null, null);
}

This method can be used now in getImageFile():

    // 1 Scale image to avoid memory issues
    Image scaledImage = scaleImage(image);

    // 2. Encode image to png
    PngEncoderFX encoder = new PngEncoderFX(scaledImage, true);
    byte[] bytes = encoder.pngEncode();

    // 3. Write byte array to a file in public storage 
    ...