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

Android GetFileFromApplicationUriAsync doesn't work #8618

Closed
pkar70 opened this issue Apr 25, 2022 · 24 comments · Fixed by #7328
Closed

Android GetFileFromApplicationUriAsync doesn't work #8618

pkar70 opened this issue Apr 25, 2022 · 24 comments · Fixed by #7328
Labels
difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. kind/bug Something isn't working triage/needs-information Indicates an issue needs more information in order to work on it. triage/untriaged Indicates an issue requires triaging or verification

Comments

@pkar70
Copy link
Contributor

pkar70 commented Apr 25, 2022

Current behavior

Exception **Java.IO.FileNotFoundException:** is thrown.

Expected behavior

File should be 'openable'.

How to reproduce it (as minimally and precisely as possible)

            Uri oPicUri = new Uri("ms-appx:///Assets/1950.gif");
            Windows.Storage.StorageFile oFile;
            try
            {  
                oFile = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(oPicUri);
            }
            catch
            {
                return false;
            }

Exception: **Java.IO.FileNotFoundException:** 'Assets/__1950.gif'

Part of: https://github.com/pkar70/KrakTram

GIF files are present, e.g. X\KrakTram\KrakTram_Uno\KrakTram_Uno.Shared\Assets\1950.gif
On build, they are copied to X\KrakTram\KrakTram_Uno\KrakTram_Uno.Droid\obj\Debug\120\res\drawable-nodpi__1950.gif

Workaround

None known.

Works on UWP/WinUI

Yes

Environment

Uno.UI / Uno.UI.WebAssembly / Uno.UI.Skia

NuGet package version(s)

UnoUI 4.2.6

Affected platforms

Android

IDE

Visual Studio 2022

IDE version

17.0.5

Relevant plugins

No response

Anything else we need to know?

No response

@pkar70 pkar70 added difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. kind/bug Something isn't working triage/untriaged Indicates an issue requires triaging or verification labels Apr 25, 2022
@jeromelaban
Copy link
Member

jeromelaban commented Apr 25, 2022

Make sure to validate first with this sample application: https://github.com/unoplatform/Uno.Samples/tree/master/UI/PackageResources

GitHub
A collection of code samples for the Uno Platform. Contribute to unoplatform/Uno.Samples development by creating an account on GitHub.

@jeromelaban jeromelaban added the triage/needs-information Indicates an issue needs more information in order to work on it. label Apr 25, 2022
@pkar70
Copy link
Contributor Author

pkar70 commented Apr 25, 2022

Sample has no files in Assets folder, so it is not related.
Android head should work same as UWP head, and UWP head works.

@jeromelaban
Copy link
Member

jeromelaban commented Apr 26, 2022

Thanks. This is likely this Visual Studio issue, as this has been working in Uno for a while. Apply the workaround, and make sure to upvote the VS issue so it gets fixed.

Also, when you're creating issues, don't point to your full app repository, make sure to create a small sample to attach to the issue.

@ghost
Copy link

ghost commented Apr 26, 2022

That seams to be the same problem with the unit tests for PR #8370
The PR itself is ok, correcting the issues #5453 and #7288. The tests passed only on iOS / macOS and the reason why is the GetFileFromApplicationUriAsync not loading Assets.
The exception is exactly the same on Android.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 26, 2022

One more check: files are present in .apk file:
X\KrakTram\KrakTram_Uno\KrakTram_Uno.Droid\bin\Debug\pkar.KrakTram.apk\res\drawable-nodpi-v4__1950.gif

@jeromelaban
Copy link
Member

@pkar70 did you try with the workaround? Does it change anything?

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 26, 2022

No...
a) I have no VS 2022 Preview
b) files ARE in apk
c) after Android R, accessing resources is changed (see below)
d) I would test some things by debugging :)

(After Build.VERSION_CODES#R, Resources must be obtained by Activity or Context created with Context.createWindowContext(int, Bundle). Application#getResources() may report wrong values in multi-window or on secondary displays.

@jeromelaban
Copy link
Member

Thanks. Files are in the APK but with an invalid path / file name, which points to a similar shared project related issue.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 26, 2022

After trying workaround - same. Java.IO.FileNotFoundException.
And files are still in same folder within apk: X\KrakTram\KrakTram_Uno\KrakTram_Uno.Droid\bin\Debug\pkar.KrakTram.apk\res\drawable-nodpi-v4\__1950.gif

@jeromelaban
Copy link
Member

jeromelaban commented Apr 26, 2022

This reminds me that images are not handled the same way as other file types. If you want images to be read by GetFileFromApplicationUriAsync, you need to include them as AndroidAsset not AndroidResource. This is not something we'll be able to change, as android mangles the file names when included with nesting.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 26, 2022

I have workaround...

  1. copy files from Uno\Shared\Assets\ folder to Uno\Droid\Assets folder
    This step is required, as files in Uno\Shared\Assets\ are placed (by build process) in res folder, without directory tree.

  2. use this as Android replacement for GetFileFromApplicationUri():

        private async System.Threading.Tasks.Task<Windows.Storage.StorageFile> AndroidGetFileFromApplicationUri(Uri uri)
        {
            // "ms-appx:///Assets/" + iRok + ".gif"
            // "__" + iRok + ".gif" w pkar.KrakTram.apk\res\drawable-nodpi-v4\

            if (uri.Scheme != "ms-appx")
            { // we don't handle "ms-appdata://" prefix (app root folder inside user) - limit of this implementation
                throw new InvalidOperationException("Uri is not using the ms-appx scheme");
            }

            string sFilename = uri.ToString();

            if (!sFilename.StartsWith("ms-appx:///Assets/"))
            { // limit of this implementation
                throw new InvalidOperationException("Uri is not ms-appx:///Assets/");
            }

            sFilename = sFilename.Substring("ms-appx:///Assets/".Length);

            var assetMan = Android.App.Application.Context.Assets;
            if (assetMan is null)
            {
                throw new InvalidOperationException("Cannot get AssetManager");
            }

            var outputCachePath = System.IO.Path.Combine(Android.App.Application.Context.CacheDir.AbsolutePath, sFilename);

            if (!System.IO.File.Exists(outputCachePath))
            {
                System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(outputCachePath));

                using System.IO.Stream fileInApk = assetMan.Open(sFilename);
                using var output = System.IO.File.OpenWrite(outputCachePath);

                await fileInApk.CopyToAsync(output);
            }

            return await Windows.Storage.StorageFile.GetFileFromPathAsync(outputCachePath);
        }

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 26, 2022

I don't know WHAT places files from Shared\Assets to Droid\res, and how to prevent it.

@jeromelaban
Copy link
Member

jeromelaban commented Apr 26, 2022

There's currently no way to change this:

private static bool IsImageAsset(string path)
{
var extension = Path.GetExtension(path).ToLowerInvariant();
return extension == ".png"
|| extension == ".jpg"
|| extension == ".jpeg"
|| extension == ".gif";

The only workaround to handle this is to add the items as AndroidAssets manually, like this:

<ItemGroup>
   <AndroidAsset Include="../MyProject.Shared/Assets/**" />
</ItemGroup>
GitHub
Build Mobile, Desktop and WebAssembly apps with C# and XAML. Today. Open source and professionally supported. - uno/RetargetAssets.cs at master · unoplatform/uno

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 26, 2022

So, if I add this AndroidAsset to csproj in Uno.Droid, files from Uno.Shared\Assets would be visible inside apk under assets folder?

@jeromelaban
Copy link
Member

It should be yes, let us know if that helps.

@ghost
Copy link

ghost commented Apr 27, 2022

Hummmm
Following the outputCachePath var, I did a shot in the dark, changing the value of PATH in:

using var input = assets.Open(path);

from 'Assets/myfile.jpeg' to only 'myfile.jpeg' and "voilà"! Android could load perfectly the image.

I don't know why path has the 'Assets/' since we are using outputCachePath as well.

@jeromelaban

@jeromelaban
Copy link
Member

@iury-kc yes, that's what it's supposed to be doing somehow, but the problem is the handling of conflicts in file names. We've been trying to adjust this for a while (#7328) but it's quite tricky.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 27, 2022

But what if I have such structure:
Assets
+- Digital
| +- 10.png
| +- 20.png
+- Analog
| +- 10.png
| +- 20.png

similar to https://github.com/pkar70/ZegarSloneczny/tree/master/ZegarSloneczny/pic

'Flattening' dirtree is bad.

GitHub
Contribute to pkar70/ZegarSloneczny development by creating an account on GitHub.

@jeromelaban
Copy link
Member

Flattening is what android does with resources, it's not something we can change. Assets don't have that problem, when using AndroidAsset solution I provided above.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 27, 2022

Still..
I have UWP app with pictures inside Assets, and GetFileFromApplicationUriAsync() doesn't work for this (on Android).

            Uri oPicUri = new Uri("ms-appx:///Assets/" + iRok + ".gif");
            Windows.Storage.StorageFile oFile;
            oFile = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(oPicUri);
            Windows.UI.Xaml.Media.Imaging.BitmapImage oBmp = new Windows.UI.Xaml.Media.Imaging.BitmapImage();
            oBmp.UriSource = oPicUri;

And I cannot use such code for Android.
Because build moves files from Uno.Shared\Assets to \res, and GetFileFromApplicationUriAsync throws exception that file is not found.

I can replace this code with "special code for Android", but code should work correctly (or show Uno0001 warning that it is unimplemented). Maybe, in this case, there should be e.g. "Uno0002 Limited implementation for Android, works only for non-picture files, and only for ms-appx (not ms-appdata)" ? And same in all other places where Uno's implementation is somehow limited (e.g., because target platform has limits).

@jeromelaban
Copy link
Member

Can you add a simple repro for your scenario here please (not one from your github) ? We'll make modifications to it and see what's needed.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 27, 2022

Simple repro:

  1. add picture to Uno.Shared/Assets folder, e.g. 1950.gif
  2. add code to e.g. MainPage.Loaded
            Uri oPicUri = new Uri("ms-appx:///Assets/1950.gif");
            var oFile = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(oPicUri);

@jeromelaban
Copy link
Member

jeromelaban commented Apr 27, 2022

This is what I mean by adding a simple repro: Test8618.zip.

The workaround for this is to add the following to the android project:

  <ItemGroup>
    <AndroidAsset Include="..\Test8618.Shared\Assets\**" Link="Assets\Assets\%(RecursiveDir)\%(FileName)%(Extension)" />
  </ItemGroup>

Take a look at the repro to see it in action.

@pkar70
Copy link
Contributor Author

pkar70 commented Apr 27, 2022

Thanks. In next app version, I will use this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty/tbd Categorizes an issue for which the difficulty level needs to be defined. kind/bug Something isn't working triage/needs-information Indicates an issue needs more information in order to work on it. triage/untriaged Indicates an issue requires triaging or verification
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants