2

I have an issue with opening JPG file in native application on Android. I'm using newest release of Xamarin Essentials, there is some functionality called Launcher. Here is my code

await Launcher.TryOpenAsync("file:///" + localPath);

My local path is file stored in Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);.

Whenever I try to open that file I am getting error:

file:////data/user/0/mypackagename/files/.local/share/Screenshot.jpg exposed beyond app through Intent.getData()

I found several solutions here on stackoverflow, but I don't want to use Intent because my application is designed to be cross-platform (I would like to avoid native platform coding if this possible).

Launcher throws error on iOS also:

canOpenURL: failed for URL: ** -- file:///" - error: "This app is not allowed to query for scheme file

What am I doing wrong here?

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
Adam Mrozek
  • 1,410
  • 4
  • 26
  • 49
  • What's your goal? Is it to display an Image on the screen that is saved as an embedded resource, https://stackoverflow.com/a/50031151/5953643? – Brandon Minnick Jan 07 '20 at 17:14
  • I need to download file from server and present it somehow. I thought that this will be the simplest solution. – Adam Mrozek Jan 07 '20 at 18:45
  • Got it! We'll use Xamarin.Essentials.Preferences to save/retrieve the file from disk. Xamarin.Essentials.Launcher is for opening other apps like Settings or Twitter. – Brandon Minnick Jan 07 '20 at 19:24
  • @BrandonMinnick If you found a solution, could you please add an answer describing what you did? This could help developers in the future – Paul Kertscher Jan 08 '20 at 06:19
  • @PaulKertscher Sure! I answered the question 11 hours ago, below: https://stackoverflow.com/a/59634820/5953643 – Brandon Minnick Jan 08 '20 at 06:25
  • @BrandonMinnick Sorry, this was a mix up. The *"Got it!"* somehow made me think that it was from the author of the question, which is obviously not you. Since there was no answer by the author of the question I wrote this comment. Sry bou that, – Paul Kertscher Jan 08 '20 at 06:28
  • @PaulKertscher Haha - no worries! – Brandon Minnick Jan 08 '20 at 06:28

1 Answers1

19

Step 1: Download the Image

We can use HttpClient to download the image.

HttpClient.GetByteArrayAsync will retrieve the image data and save it in memory.

In DownloadImage below, we will retrieve the image as a byte[].

static class ImageService
{
    static readonly HttpClient _client = new HttpClient();

    public static Task<byte[]> DownloadImage(string imageUrl)
    {
        if (!imageUrl.Trim().StartsWith("https", StringComparison.OrdinalIgnoreCase))
            throw new Exception("iOS and Android Require Https");

        return _client.GetByteArrayAsync(imageUrl);
    }
}

Step 2 Save Image to Disk

Now that we've downloaded the image, we will save it to disk.

Xamarin.Essentials.Preferences allows us to save items to disk using key-value pairs. Since byte[] is just a pointer to memory, we'll have to first convert the byte[] to base64-string before we can save it to disk.

static class ImageService
{
    static readonly HttpClient _client = new HttpClient();

    public static Task<byte[]> DownloadImage(string imageUrl)
    {
        if (!imageUrl.Trim().StartsWith("https", StringComparison.OrdinalIgnoreCase))
            throw new Exception("iOS and Android Require Https");

        return _client.GetByteArrayAsync(imageUrl);
    }

    public static void SaveToDisk(string imageFileName, byte[] imageAsBase64String)
    {
        Xamarin.Essentials.Preferences.Set(imageFileName, Convert.ToBase64String(imageAsBase64String));
    }
}

Step 3 Retrieve the Image for Display

Now that we've downloaded the image and saved it to disk, we need to be able to retrieve the image from disk to display it on the screen.

GetFromDisk below retrieves the image from disk and converts it to Xamarin.Forms.ImageSource.

static class ImageService
{
    static readonly HttpClient _client = new HttpClient();

    public static Task<byte[]> DownloadImage(string imageUrl)
    {
        if (!imageUrl.Trim().StartsWith("https", StringComparison.OrdinalIgnoreCase))
            throw new Exception("iOS and Android Require Https");

        return _client.GetByteArrayAsync(imageUrl);
    }

    public static void SaveToDisk(string imageFileName, byte[] imageAsBase64String)
    {
        Xamarin.Essentials.Preferences.Set(imageFileName, Convert.ToBase64String(imageAsBase64String));
    }

    public static Xamarin.Forms.ImageSource GetFromDisk(string imageFileName)
    {
        var imageAsBase64String = Xamarin.Essentials.Preferences.Get(imageFileName, string.Empty);

        return ImageSource.FromStream(() => new MemoryStream(Convert.FromBase64String(imageAsBase64String)));
    }
}

Example: Using ImageService in a Xamarin.Forms.ContentPage

class App : Application
{
    public App() => MainPage = new MyPage();
}

class MyPage : ContentPage
{
    readonly Image _downloadedImage = new Image();

    public MyPage()
    {
        Content = _downloadedImage;
    }

    protected override  async void OnAppearing()
    {
        const string xamrainImageUrl = "https://cdn.dribbble.com/users/3701/screenshots/5557667/xamarin-studio-1_2x_4x.png"
        const string xamarinImageName = "XamarinLogo.png";

        var downloadedImage = await ImageService.DownloadImage(xamrainImageUrl);

        ImageService.SaveToDisk(xamarinImageName, downloadedImage);

        _downloadedImage.Source = ImageService.GetFromDisk(xamarinImageName);
    }
}

enter image description here

Brandon Minnick
  • 13,342
  • 15
  • 65
  • 123
  • I would like to open this image in native application. Is this possible? I have implemented similar solution to yours but I am not very happy about that. – Adam Mrozek Jan 08 '20 at 05:37
  • Can you elaborate more on what you mean by Native Application? Are you using Xamarin.Forms? – Brandon Minnick Jan 08 '20 at 06:19
  • 1
    Yes, I am using Xamarin Forms. I would like to download jpg file (it is done) and then open that jpg in default application on Android/iOS (opening photo browser with this photo) – Adam Mrozek Jan 08 '20 at 07:13
  • Oh - ok. That’s a completely different question and a completely different process. Let’s do this: open a new question with that specific explanation, then comment below with the link and we’ll answer it there. I don’t want to delete this question and this answer because both already have a bunch of views and upvotes meaning that this discussion has been helpful to other devs. – Brandon Minnick Jan 08 '20 at 07:16
  • Ok, new question here: https://stackoverflow.com/questions/59642350/xamarin-forms-open-downloaded-file-in-default-android-ios-application – Adam Mrozek Jan 08 '20 at 08:54