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

1.6.0 Resources are not packaged to Android file system #4503

Closed
KevinnZou opened this issue Mar 18, 2024 · 14 comments · Fixed by #4965
Closed

1.6.0 Resources are not packaged to Android file system #4503

KevinnZou opened this issue Mar 18, 2024 · 14 comments · Fixed by #4965
Assignees
Labels
discussion Need further discussion to understand if it actually needed

Comments

@KevinnZou
Copy link

I encountered the that issue in my CMP Webview library with CMP 1.6.0.

Previously, I would place the HTML files in the commonMain/resources/assets folder. This would automatically package the files into the Android assets folder, allowing us to load them using the webView.loadUrl("file:///android_asset/$fileName") method.

However, after upgrading to 1.6.0, we moved the HTML files to the commonMain/composeResources/files/assets folder, but got FILE_NOT_FOUND error.

I am wondering how files will be packaged for other platforms (Android & iOS) in the new resources management system.

@PMARZV
Copy link

PMARZV commented Mar 18, 2024

#4487 there is a person already assigned for the issue, its because the resource library doesn't accept subfolders inside the main folders (files in this case) yet! If you put all files in the main folder it will work if im not wrong

@KevinnZou
Copy link
Author

@PMARZV Thanks for your suggestions! But I think the issuse I encounterd is that the files under composeResources are not packaged to Android assets folder, as it should be done before version 1.6.0. Since I am loading the HTML file from the Android side, it needs to be packaged into its file system.

@pjBooms pjBooms removed the submitted label Mar 20, 2024
@pjBooms
Copy link
Collaborator

pjBooms commented Mar 20, 2024

I am wondering how files will be packaged for other platforms (Android & iOS) in the new resources management system.

It is implementation details that you should not rely on because it is a subject to change.

We are going to provide an API to convert a resource to URI so you can use it from WebView (see this comment).

@pjBooms pjBooms closed this as not planned Won't fix, can't repro, duplicate, stale Mar 20, 2024
@KevinnZou
Copy link
Author

@pjBooms Thanks to your reply! However, I am relying on native ability to implement the loadHtmlFiles feature in CMP WebView. Thus, I need to know the exact location of the resources inside each platfrom's package. Then I can use webView.loadUrl("file:///android_asset/$fileName") for android and val res = NSBundle.mainBundle.resourcePath + "/compose-resources/assets/" + fileName for iOS.

Before 1.6.0, I know that the resources under resources folder will be packaged to Android assets folder and then can be read by file system. I am just wondering how it works in 1.6.0?

@pjBooms pjBooms added discussion Need further discussion to understand if it actually needed and removed bug Something isn't working labels Mar 22, 2024
@pjBooms pjBooms reopened this Mar 22, 2024
@pjBooms
Copy link
Collaborator

pjBooms commented Mar 22, 2024

Then I can use webView.loadUrl("file:///android_asset/$fileName") for android and val res = NSBundle.mainBundle.resourcePath + "/compose-resources/assets/" + fileName for iOS.

If we provide an API to convert a resource to URI won't it solve your problem?

@KevinnZou
Copy link
Author

@pjBooms I don't think URI can solve the problem because we are not reading resources from common code. Instead, we rely on the native method of each platform to load the HTML file directly. For example, Android Webview provides the method loadUrl("file:///android_asset/$fileName") for this purpose. However, this requires us to place the HTML files under the assets folder. Therefore, if we want to put the files under the common resource folder, we need to understand how they will be packaged for each platform and their exact location. Then, we can pass the information to the actual load HTML method on each platform. I hope this explanation is clear. Please let me know if you have any further questions.

@pjBooms
Copy link
Collaborator

pjBooms commented Mar 26, 2024

For example, Android Webview provides the method loadUrl("file:///android_asset/$fileName") for this purpose. However, this requires us to place the HTML files under the assets folder.

I still cannot understand why URI does not work here. Does not Android Webview accept an arbitrary URL?

@KevinnZou
Copy link
Author

@pjBooms Both Android and iOS WebView can accept arbitrary URLs, but they have different requirements for the URL format. In Android, the file needs to be located under the assets package and the URL should be in the form of file:///android_asset/$fileName. iOS also has its own rules for URLs, so in this case, an arbitrary URL will not work. To solve this, I need to understand the logic behind resource packaging and pass a different URL to each platform.

@KevinnZou
Copy link
Author

@pjBooms Are there updates on this issue? Actually, I just need to know how files under composeResources are packaged to the Android system. Will it be packaged into the Android assets folder?

@KevinnZou
Copy link
Author

@pjBooms I was able to solve the issue by creating an assets folder directly under the composeResource folder. This allowed the resources to be packaged to the Android asset folder as before.

However, I encountered another error in Kotlin 2.0 as mentioned in this issue. It appears that Kotlin 2.0 does not support a custom resource folder under the composeResource folder. Is there a way to lift this restriction?

@terrakok
Copy link
Collaborator

Unfortunately, Android Web view doesn't support files inside jar archives.
You are suppose to load a html content by your own and pass it to the webview.
image

@terrakok
Copy link
Collaborator

If your page uses other resources (e.g. images) it won't work. So, you may copy required content to the file system and use it as before by uri.

@terrakok
Copy link
Collaborator

I investigated the problem a bit: the loadUrl("file:///android_asset/$fileName") method is the android (hackish) specific API to read files by a "magic" string from resources. It will not work on other platforms. For example, on a JVM you are not able to load a html page by uri which points into the jar.

Since you want to do it in a multiplatform application, you are suppose to use platform independent approach: extract required files to the file system and use it directly.

Or, if it is required for the android only then you are able to use classic assets in the androidMain/assets directory

@terrakok
Copy link
Collaborator

Oh! I found another way to achieve the local web pages loading - a custom web view client:

private class MyWebViewClient : WebViewClient() {
    override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
        val url = request?.url?.toString().orEmpty()
        return if (url.startsWith("file://", ignoreCase = true)) runBlocking {
            val path = url.substringAfter("file://")
            val bytes = Res.readBytes(path)
            val stream = ByteArrayInputStream(bytes)
            WebResourceResponse("text/html", "utf-8", stream)
        } else super.shouldInterceptRequest(view, request)
    }
}
AndroidView(factory = {
    WebView(it).apply {
        layoutParams = ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.MATCH_PARENT
        )
        webViewClient = MyWebViewClient()
        settings.allowFileAccess = true
        loadUrl("file://files/page.html")
    }
})
image

terrakok added a commit that referenced this issue Jun 20, 2024
The PR changes the android resources packaging. Now all resources are
packed to the android assets (not only fonts). It unblocks usage android
URIs to the resources in a WebView or other external resource consumers.
Additionally the PR fixes Android Studio Compose Previews work with
multiplatform resources:


![](https://private-user-images.githubusercontent.com/3532155/341182790-ef26b667-ad0d-4efd-b7f9-23cff92ab49d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MTg4Nzg0MTgsIm5iZiI6MTcxODg3ODExOCwicGF0aCI6Ii8zNTMyMTU1LzM0MTE4Mjc5MC1lZjI2YjY2Ny1hZDBkLTRlZmQtYjdmOS0yM2NmZjkyYWI0OWQucG5nP1gtQW16LUFsZ29yaXRobT1BV1M0LUhNQUMtU0hBMjU2JlgtQW16LUNyZWRlbnRpYWw9QUtJQVZDT0RZTFNBNTNQUUs0WkElMkYyMDI0MDYyMCUyRnVzLWVhc3QtMSUyRnMzJTJGYXdzNF9yZXF1ZXN0JlgtQW16LURhdGU9MjAyNDA2MjBUMTAwODM4WiZYLUFtei1FeHBpcmVzPTMwMCZYLUFtei1TaWduYXR1cmU9OTY1MzdhMTAxMjNmZDRhMDA4ZjdjODBjYzg3M2MyNDg0ZTA5OWFkZGZkZjk1ZDUwOWFkZDk3MmQ2YjIzNzJiYiZYLUFtei1TaWduZWRIZWFkZXJzPWhvc3QmYWN0b3JfaWQ9MCZrZXlfaWQ9MCZyZXBvX2lkPTAifQ.xgUAr_2--ZHo6txhdAANRbe8ju2SQ5EACvK96gaGJnY)

For a backward compatibility the resources library tries to read
resources in java resources if assets were not found.

Fixes #4877
Fixes #4503
Fixes #4932
Fixes #4476

## Release Notes

### Features - Resources
- Android Studio Preview works with Compose Multiplatform resources now
- Compose Multiplatform resources are stored in the android assets now.
This fixes such cases as a rendering resource files in WebViews or Media
Players
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
discussion Need further discussion to understand if it actually needed
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants