Add file associations support to Compose Desktop
<!-- Optional -->
Fixes#773
## Testing
Tested on the [sample
project](https://github.com/zhelenskiy/file-associations-demo).
Behaviours per OSs:
- MacOS Sonoma: associations work for distributables.
- Windows 11: associations work after the installation of the MSI.
- Kubuntu: associations do not work, but everything else works fine.
However, IDEA also does not have associations there, so I assume this is
fine.
I didn't write any unit tests because I don’t know which of them you are
expecting me to write. So, I'm looking forward to your feedback and
suggestions.
<!-- Optional -->
This should be tested by QA
## Release Notes
<!--
Optional, if omitted - won't be included in the changelog
Sections:
- Highlights
- Known issues
- Breaking changes
- Features
- Fixes
Subsections:
- Multiple Platforms
- iOS
- Desktop
- Web
- Resources
- Gradle Plugin
-->
### Highlight - Desktop
- Introduction of the new DSL function in `nativeDistributions` block:
```kotlin
fun fileAssociation(mimeType: String, extension: String, description:
String): Unit
```
With the new API it is possible to customize compose resources
directories. For example:
```kotlin
abstract class GenerateAndroidRes : DefaultTask() {
@get:Inject
abstract val layout: ProjectLayout
@get:OutputDirectory
val outputDir = layout.buildDirectory.dir("generatedAndroidResources")
@TaskAction
fun run() {...}
}
compose.resources {
customDirectory(
sourceSetName = "androidMain",
directoryProvider = tasks.register<GenerateAndroidRes>("generateAndroidRes").map { it.outputDir.get() }
)
customDirectory(
sourceSetName = "desktopMain",
directoryProvider = provider { layout.projectDirectory.dir("desktopResources") }
)
}
```
<!-- Optional -->
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4718
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4564
## Release Notes
### Features - Resources
- Add a customization for resources directories. Now it is possible to
use e.g downloaded resources.
We had two build services:
1) to check unsupported compose compiler plugins were applied
2) to check a native cache kind configuration
Both of them are outdated and we may get rid of them.
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4815
## Release Notes
### Fixes - Gradle Plugin
- Delete outdated build services
Fixes#4886
## Testing
- Built the gradle plugin to mavenLocal
- used it in the reproducer of #4886, - the issue is gonve
This should be tested by QA
## Release Notes
### Fixes - Gradle Plugin
- Make sure tryGetSkikoRuntimeIfNeeded is executed only during the task
execution
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4548
## Testing
Add compose resources with name such as "package", "is" or "item_$xxx"
and check that app compiles and works fine. Accessors should be properly
escaped
## Release Notes
### Fixes - Resources
- Fix resource accessors escaping. Now it is possible to use resources
with names: "package", "is", "item_$xxx" etc
By mistake the generation resources directory was linked to "podInstall"
task instead "podspec".
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4720
## Testing
- create a new Compose App project with an iOS integration via Cocoapods
- add some multiplatform resources
- clean all caches and build dirs
- call "pod install"
- check that first run of the iOS app works fine
## Release Notes
### Fixes - Resources
- Create an empty resource dir with "podspec" task instead "podInstall"
AGP 7 isn't supported by Jetpack Compose 1.7 (which we'll use in CMP
1.7) and by Lifecycle 2.8.
After this CI is merged, I will:
- revert the revert of [the skiko API
fix](https://github.com/JetBrains/compose-multiplatform-core/pull/1328)
in jb-main
- raise the AGP version on TeamCity
### Details
It was discovered after merging [the skiko API
fix](https://github.com/JetBrains/compose-multiplatform-core/pull/1328).
We had `D8: java.lang.NullPointerException` error on TeamCity. After
investigating dependencies, it appears that previous the dependencies
were incorrect:
```
org.jetbrains.compose.material:material -> androidx.compose.material:material:1.6.7 -> androidx.lifecycle:lifecycle-livedata-core:2.6.1
```
And the new are correct, but aren't supported by AGP 7:
```
org.jetbrains.compose.material:material -> org.jetbrains.compose.ui -> org.jetbrains.androidx.lifecycle:lifecycle-common -> androidx.lifecycle:lifecycle-common:2.8.0 -> androidx.lifecycle:lifecycle-livedata-core:2.8.0
```
The dependency chains was printed by this command on the
`commonResources` test project:
```
./gradlew dependencyInsight --configuration demoDebugRuntimeClasspath --dependency androidx.lifecycle:lifecycle-livedata-core
```
## Testing
1. Create a pure Jetpack Compose project via Android Studio Hedgehog |
2023.1.1 Patch 2
2. Set Jetpack Compose to 1.7.0-beta01
3. Downgrade AGP to 7.4.0
4. Run
5. See the error:
```
ERROR:C:\Users\igord\.gradle\caches\transforms-4\7fcc63c0892ce9266300d6463c3c751d\transformed\lifecycle-livedata-core-2.8.0-runtime.jar: D8: java.lang.NullPointerException: Cannot invoke "String.length()" because "<parameter1>" is null
Execution failed for task ':app:mergeExtDexDebug'.
> Could not resolve all files for configuration ':app:debugRuntimeClasspath'.
> Failed to transform lifecycle-livedata-core-2.8.0.aar (androidx.lifecycle:lifecycle-livedata-core:2.8.0) to match attributes {artifactType=android-dex, asm-transformed-variant=NONE, dexing-enable-desugaring=true, dexing-enable-jacoco-instrumentation=false, dexing-is-debuggable=true, dexing-min-sdk=24, org.gradle.category=library, org.gradle.dependency.bundling=external, org.gradle.libraryelements=aar, org.gradle.status=release, org.gradle.usage=java-runtime}.
> Execution failed for DexingNoClasspathTransform: C:\Users\igord\.gradle\caches\transforms-3\4aae1223695d47acd0b149bb5811ca5f\transformed\lifecycle-livedata-core-2.8.0-runtime.jar.
> Error while dexing.
```
6. Change AGP to 8.1.0
7. The run is successful.
## Release Notes
### Breaking changes - Android
- Minimal supported AGP raised to 8.1.0
Fixes: https://github.com/JetBrains/compose-multiplatform/issues/4823
In https://github.com/JetBrains/compose-multiplatform/pull/4796 we
intentionally started to configure the web app for all k/js and k/wasm
targets. The configuration involves adding a dependency on skiko-wasm
runtime and unpacking it.
Some projects don't need skiko-wasm-runtime (like those based on
compose.html or just compose.runtime).
**Solution:**
We check if there is a dependency on org.jetbrains.compose.ui libraries
(including transitive dependencies). If we find it, then we enable
skikoUnpack task. Otherwise it's disabled.
## Testing
- Build the gradle plugin locally (with this change)
- Used it in our html landing example:
https://github.com/JetBrains/compose-multiplatform/blob/master/examples/html/landing
- Run `./gradlew jsBrowserDistribution`, check
`.../compose-multiplatform/examples/html/landing/build/dist/js/productionExecutable`
and see NO skiko.* files added there
- Then add `implementation(compose.foundation)` dependency in
build.gradle.jts and run `./gradlew clean jsBrowserDistribution` again -
the build/dist contains skiko.* now
Since 1.6.10 Compose for Web goes to Alpha and experimental
configuration is not needed anymore. We'll configure the web targets by
default when they're added to projects.
## Testing
- I built the plugin to mavenLocal. And used it in a couple of our
samples.
- After gradle sync completes, I observe the Deprecation warning in
build.gradle.kts on `compose.experimental.web` usages (see a screenshot
below)
<img width="1022" alt="Screenshot 2024-05-10 at 15 41 14"
src="https://github.com/JetBrains/compose-multiplatform/assets/7372778/e8ede073-8d34-4dd7-ae74-c83ca0ff5c96">
Then I remove deprecated API usages and test that the project works
without it:
- run `./gradlew clean` just in case
- run the app: `./gradlew wasmJsBrowserRun` and `./gradlew jsBrowserRun`
- both run fine
- build the production distribution: `./gradlew
wasmJsBrowserDistribution`
- `cd ..../build/dist/wasmJs/productionExecutable` and run `python -m
http.server`, open a browser at `http://localhost:8000` - the app should
work the same way it works with gradle tasks above
<!-- Optional -->
This should be tested by QA
## Release Notes
### Highlights - Web
- Compose for Web goes to Alpha!
Some experimental Compose Multiplatform Gradle plugin APIs for web app
configuration were deprecated. Their usage is not required anymore.
The PR sets a static path to the compose resources for the cocoapods
integration because there maybe only one integration framework for a
gradle module.
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4720
## Release Notes
### Fixes - Resources
- _(prerelease fix)_ Fix resources with cocoapods integration
The Compose Multiplatform Resources with the mulimodule.publication
support is a new feature and may have some not found problems. The PR
introduces a gradle property to switch back to the previous behavior:
`org.jetbrains.compose.resources.multimodule.disable=true`
## Testing
After update to Kotlin 2.0.0 and CMP 1.6.10 all projects will be
switched to the new logic with the multimodule support.
Gradle info output should contain a message:
```
Configure KMP resources
```
If the new feature breaks user's projects they may add
`org.jetbrains.compose.resources.multimodule.disable=true` to the
`gradle.properties` to disable it. After that Gradle info output will
contain:
```
Configure compose resources
```
## Release Notes
### Resources
To disable the Compose Resources publication and the multimodule support
in cases of problems add
`org.jetbrains.compose.resources.multimodule.disable=true` to the root
`gradle.properties`.
## Proposed changes
1. Added support to join JARs to the uber JAR with ProGuard, disabled by
default:
```
compose.desktop {
application {
buildTypes.release.proguard {
joinOutputJars.set(true)
}
}
}
```
2. All 'release' tasks now really depend on ProGuard, as stated in
[tutorial](https://github.com/JetBrains/compose-multiplatform/tree/master/tutorials/Native_distributions_and_local_execution#minification--obfuscation).
## Testing
- A new auto test
- Manual:
1. Test on Windows/macOs/Linux
2. Test the new Gradle parameter `joinOutputJars`:
```
compose.desktop {
application {
buildTypes.release.proguard {
joinOutputJars.set(true)
}
}
}
```
`false` (by default) should generate multiple jars (except for
`package*UberJarForCurrentOS`)
`true` should generate a single jar in a result distribution
3. Test debug tasks:
```
run
runDistributable
createDistributable
packageUberJarForCurrentOS
```
4. Test release tasks:
```
runRelease
runReleaseDistributable
createReleaseDistributable
packageReleaseUberJarForCurrentOS
```
The jars should be reduced in size (because Proguard is enabled in the
release mode)
This should be test by QA.
## Issues fixed
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4129
---------
Co-authored-by: Igor Demin <igordmn@users.noreply.github.com>
Compose resources can be located in different KMP source sets in the
`composeResources` directory. For each resource an accessor will be
generated in the suitable kotlin source set.
Adds a public `Res.getUri(path: String): String` function.
It lets external libraries a way to read resource files by a platform
dependent Uri.
E.g.: video players, image loaders or embedded web browsers.
```kotlin
val uri = Res.getUri("files/my_video.mp4")
```
fixes https://github.com/JetBrains/compose-multiplatform/issues/4360
The issue was in a case where there wasn't a direct dependency on the
skiko artifact, the task would attempt to get one at task execution
time. This moves that to task configuration time.
Users noticed if an app has big a `string.xml` file it affects the app
startup time:
https://github.com/JetBrains/compose-multiplatform/issues/4537
The problem is slow XML parsing.
Possible ways for optimization:
1) inject text resources direct to the source code
2) convert XMLs to an optimized format to read it faster
We selected the second way because texts injected to source code have
several problems:
- strict limitations on text size
- increase compilation and analysation time
- affects a class loader and GC
> Note: android resources do the same and converts xml values to own
`resources.arsc` file
Things was done in the PR:
1) added support any XML files in the `values` directory
2) **[BREAKING CHANGE]** added `Res.array` accessor for string-array
resources
3) in a final app there won't be original `values*/*.xml` files. There
will be converted `values*/*.cvr` files.
4) generated code points on string resources as file -> offset+size
5) string resource cache is by item now (it was by the full xml file
before)
6) implemented random access to read CVR files
7) tasks for syncing ios resources to a final app were seriously
refactored to support generated resources (CVR files)
8) restriction for 3-party resources plugin were deleted
9) Gradle property `compose.resources.always.generate.accessors` was
deleted. It was for internal needs only.
Fixes https://github.com/JetBrains/compose-multiplatform/issues/4537
Ports a part of Unicode's ICU in pure Kotlin and implements
Android-style plural string resource support. Fixes
JetBrains/compose-multiplatform#425.
# Changes
- Added `org.jetbrains.compose.resources.intl.{PluralCategory,
PluralRule, PluralRuleList}`, which parses and evaluates scripts in
Unicode's Locale Data Markup Langauge.
- Copied `plurals.xml` from Unicode's
[CLDR](https://github.com/unicode-org/cldr/blob/release-44-1/common/supplemental/plurals.xml).
- Added `GeneratePluralRuleListsTask`, which parses `plurals.xml` and
generates required Kotlin source codes.
- Added `PluralStringResource`, `pluralStringResource`, or
`getPluralString`, corresponding to `StringResource`, `stringResource`,
or `getString`.
- Modified `ResourcesSpec.kt` so the generated `Res` class exposes
`Res.plurals`.
# Potential Further Improvements
- [ ] Allow configuring the default language in the `compose.resources
{}` block (#4482) to determine the default pluralization rule (or just
presume English as default)
- [ ] Move the parser logic to the Gradle plugin and generate
pluralization rules in `Res` only for languages used in
`composeResources`
---------
Co-authored-by: Konstantin Tskhovrebov <konstantin.tskhovrebov@jetbrains.com>