ionic-team / capacitor-assets

Local Capacitor icon/splash screen resource generation tool
MIT License
509 stars 100 forks source link

Feedback requested: Capacitor Assets Use Cases #291

Closed mlynch closed 2 years ago

mlynch commented 2 years ago

Hi everyone,

I am going to be working on getting the Capacitor Assets (formerly cordova-res) tool updated and modernized, to enable developers to more efficiently manage their app icon/splash assets.

I would love to hear what you hope to get out of this tool or how you were using the cordova-res project.

A few goals that we have for the new version of this project:

With this in mind, I'd love feedback and thoughts on the use cases you have for image/asset generation and ideas on how we can make this tool more useful for you.

alexandermorgan commented 2 years ago

Thank you for this! I'm not sure if this is a Capacitor or iOS issue, but I've noticed that my iOS builds are several times larger than my Android ones (~15mb vs ~4mb). It's the same code base so the only difference is I have a lot more app icon and splash icon sizes and formats on iOS. Is there any way to make it so that only the icons that are relevant to the user's device get downloaded? It seems like this would dramatically reduce the download size of apps on iOS. Maybe I'm adding too many icon options and there's a single icon format that would work for all iOS device types. In this case, just making that clearer would be extremely helpful.

leo-petrucci commented 2 years ago

I have not used this tool before, but the most difficult part of shipping on Android is creating a 9 patch splash screen.

As far as I understand it it's supposed to fit on any Android screen, but from my experience it gets squashed no matter what I do πŸ˜‚

Would love a better way to work around that issue.

Hope that helps!

mlynch commented 2 years ago

@alexandermorgan hard to say without looking at it but either way I think I already cut a number of generated images, I need to see.

The size difference is likely due to embedding Swift libraries. The work Apple did in the App Store means the actual size that users download can be much smaller since it can strip away a lot of that especially in recent versions of Swift.

mlynch commented 2 years ago

@creativiii yea I need to assess where we are with 9 patches, there is definitely work to be done for better Android Adaptive icon support

dotNetkow commented 2 years ago

Hey boss! I'd love support for PWA manifest images to be included as well. This would fit Ionic's cross-platform/"build once" mantra of supporting web, ios, android out-of-the-box. Other tools do exist to create these though, so it's not a must-have.

mlynch commented 2 years ago

@dotNetkow great point. IIRC I already added this into my WIP branch a few months ago so this is definitely happening

JulienLecoq commented 2 years ago

It's been a few months since the last time I used cordova-res, but I remember that I had an issue with folders that are missing in Android while I was generating the splash screen & app icon. I'm not sure if it's solved now, so I would already be satisfied if I don't have this problem with Capacitor Assets haha.

I would like that we incorporate notification icon in this new plugin, it is not handled by cordova-res. If I remember well, for iOS, no notification icon is required but for Android, you need to have the same icon than the app icon but in white, the color of the icon is added in the code somewhere in Android config files.

mlynch commented 2 years ago

@JulienLecoq great idea. Seems we should support Notification, Settings, and Spotlight icons at least for iOS, not sure if there are additional ones we should support for Android:

image

https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/

Sampath-Lokuge commented 2 years ago

The other feature what we need here is the Adaptive Icons support for the Android devices: There are lot of open issues for this:

https://github.com/ionic-team/capacitor-assets/issues/164

https://github.com/ionic-team/capacitor/issues/229

mlynch commented 2 years ago

Yes good point that will be a key focus for this

mlynch commented 2 years ago

Can someone verify my understanding here: on iOS if you want to provide a different icon for notifications and settings, you should modify the smaller sizes of your icons listed here: https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/

There is no special icon set for notifications/etc. right? They are just taken from your main AppIcons?

In that case I think we'll overwrite those smaller icons if you provide specific notification/settings ones in your assets

mlynch commented 2 years ago

Hey everyone, the first alpha release of @capacitor/assets is ready for testing. It supports generating assets for iOS, Android, and PWA. For Android it supports Adaptive Icons and legacy icons. Both iOS and Android also support generating light and dark mode splash screens.

I know there is still work to be done on the tool, but I would really love some help testing and some feedback on things that are still missing.

To get started, check out the readme for the 1.x branch and I look forward to your feedback:

https://github.com/ionic-team/capacitor-assets/blob/1.x/README.md

riderx commented 2 years ago

@mlynch thanks a lot for this i just give it a try and some feedback:

I have made a PR for theses : https://github.com/ionic-team/capacitor-assets/pull/296

Otherwise, the tool worked well, and way faster than the old one ! I will love to have this tool also create the notification icon for Android platform.

I good addition i would love to have: A quick generator, most of my app use the logo in centre in the spashscreen background is same in icon and splash. So have a way to generate all from a colour and foreground icon would make our life easier.

mlynch commented 2 years ago

@riderx for the last addition, meaning you just take a logo and it and generates the other assets for you so you don't have to create the source splash.png etc?

This would be primarily for splash screens?

Seems reasonable

riderx commented 2 years ago

Yes Exactly :)

mlynch commented 2 years ago

@riderx Got the iOS side of that done, moving to android + pwa

riderx commented 2 years ago

@mlynch amazing ! i just try my Android app and all assets got broken link. i think migrate from old asset's system to new one has issue on Android. I'm trying to understand why i get Invalid resource directory name, i event try to remove android folder and add again to see if that come from one of my config but still same issue

mlynch commented 2 years ago

@riderx 1.0.0-next.2 is out and it supports generating all assets from a single logo file.

Check out the new Easy Mode docs, curious to get your feedback πŸ˜„ https://github.com/ionic-team/capacitor-assets/tree/1.x#usage---easy-mode

riderx commented 2 years ago

Made my day! I just discovered quasar has same kind of features, have a check there lot of interesting stuff! I will check tomorrow :)

riderx commented 2 years ago

@mlynch i tryed capacitor-assets generate --iconBackgroundColor '#FFFFFF' --iconBackgroundColorDark '#111111' --splashBackgroundColor '#FFFFFF' --splashBackgroundColorDark '#111111'

with the last version 1.0.0-next.1 and this logo : logo and got some errors:

Unable to generate assets TypeError: Cannot use 'in' operator to search for 'alpha' in true
    at new Color (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/color@4.2.1/node_modules/color/index.js:77:15)
    at Color (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/color@4.2.1/node_modules/color/index.js:26:10)
    at Sharp._createInputDescriptor (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/sharp@0.29.3/node_modules/sharp/lib/input.js:216:30)
    at new Sharp (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/sharp@0.29.3/node_modules/sharp/lib/constructor.js:289:29)
    at Sharp (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/sharp@0.29.3/node_modules/sharp/lib/constructor.js:137:12)
    at AndroidAssetGenerator._generateAdaptiveIconsFromLogo (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/@capacitor+assets@1.0.0-next.1_e79e62fe450383fd2d418267dc75e645/node_modules/@capacitor/assets/dist/platforms/android/index.js:81:52)
    at AndroidAssetGenerator.generateFromLogo (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/@capacitor+assets@1.0.0-next.1_e79e62fe450383fd2d418267dc75e645/node_modules/@capacitor/assets/dist/platforms/android/index.js:50:51)
    at AndroidAssetGenerator.generate (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/@capacitor+assets@1.0.0-next.1_e79e62fe450383fd2d418267dc75e645/node_modules/@capacitor/assets/dist/platforms/android/index.js:25:29)
    at InputAsset.generate (/Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/@capacitor+assets@1.0.0-next.1_e79e62fe450383fd2d418267dc75e645/node_modules/@capacitor/assets/dist/input-asset.js:41:25)
    at /Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/node_modules/.pnpm/@capacitor+assets@1.0.0-next.1_e79e62fe450383fd2d418267dc75e645/node_modules/@capacitor/assets/dist/tasks/generate.js:51:63

It seems from the log i cannot have alpha channel in my logo. Edit: i tried to use a logo with background and same issue.

mlynch commented 2 years ago

Okay thanks for the report will look at this right now @riderx

riderx commented 2 years ago

@mlynch there a need to change the folder name from ressources to assets ? i see some cordova tools rely on the name of the folder. Maybe we can keep it for compatibility reason ?

mlynch commented 2 years ago

@riderx haven't fixed that issue yet but will soon once I finish up the pwa splash generation which is the last big task. I can easily support resources or assets

riderx commented 2 years ago

Thanks to keep me posted πŸ‘Œ

mlynch commented 2 years ago

@riderx can you try @capacitor/assets@1.0.0-next.4@next? I made a bunch of changes and fixed the issue you reported above (I wasn't correctly implementing the command flags).

I added easy mode/logo generation to all platforms and this is just about getting close to a 1.0 release I think

riderx commented 2 years ago

This go to the end again and seems correct with splash and icon but i'm back with the issue in android.


2: Task failed with an exception.
-----------
* What went wrong:
Execution failed for task ':app:mergeDebugResources'.
> /Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/android/app/src/main/res/drawable-night-port-xhdpi: Error: Invalid resource directory name

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugResources'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:187)
    at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:268)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:185)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:173)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
    at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:408)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:395)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:388)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:374)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: com.android.build.gradle.tasks.ResourceException: /Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/android/app/src/main/res/drawable-night-port-xhdpi: Error: Invalid resource directory name
    at com.android.build.gradle.tasks.MergeResources.doFullTaskAction(MergeResources.java:312)
Caused by: com.android.build.gradle.tasks.ResourceException: /Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/android/app/src/main/res/drawable-night-port-xhdpi: Error: Invalid resource directory name

    at com.android.build.gradle.internal.tasks.IncrementalTask.handleIncrementalInputs(IncrementalTask.kt:110)
    at com.android.build.gradle.internal.tasks.IncrementalTask.access$handleIncrementalInputs(IncrementalTask.kt:65)
    at com.android.build.gradle.internal.tasks.IncrementalTask$taskAction$$inlined$recordTaskAction$1.invoke(BaseTask.kt:62)
    at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:51)
    at com.android.build.gradle.internal.tasks.IncrementalTask.taskAction$gradle_core(IncrementalTask.kt:137)
    at jdk.internal.reflect.GeneratedMethodAccessor722.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
    at org.gradle.api.internal.project.taskfactory.IncrementalTaskInputsTaskAction.doExecute(IncrementalTaskInputsTaskAction.java:47)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:51)
    at org.gradle.api.internal.project.taskfactory.AbstractIncrementalTaskAction.execute(AbstractIncrementalTaskAction.java:25)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:29)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:498)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:29)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$1.execute(DefaultBuildOperationRunner.java:26)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.run(DefaultBuildOperationRunner.java:56)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$run$1(DefaultBuildOperationExecutor.java:71)
    at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.runWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:45)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:71)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:483)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:466)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:105)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:270)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:248)
    at org.gradle.internal.execution.steps.ExecuteStep.executeInternal(ExecuteStep.java:83)
    at org.gradle.internal.execution.steps.ExecuteStep.access$000(ExecuteStep.java:37)
    at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:50)
    at org.gradle.internal.execution.steps.ExecuteStep$1.call(ExecuteStep.java:47)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
    at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:47)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:37)
    at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:68)
    at org.gradle.internal.execution.steps.RemovePreviousOutputsStep.execute(RemovePreviousOutputsStep.java:38)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:50)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:36)
    at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:41)
    at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:74)
    at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:55)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:51)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:29)
    at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:54)
    at org.gradle.internal.execution.steps.CaptureStateAfterExecutionStep.execute(CaptureStateAfterExecutionStep.java:35)
    at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:60)
    at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:27)
    at org.gradle.internal.execution.steps.BuildCacheStep.executeWithoutCache(BuildCacheStep.java:174)
    at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:74)
    at org.gradle.internal.execution.steps.BuildCacheStep.execute(BuildCacheStep.java:45)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:40)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:29)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:36)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:22)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:99)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:92)
    at java.base/java.util.Optional.map(Optional.java:265)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:52)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:84)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:41)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:37)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:27)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:91)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:49)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:78)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:49)
    at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:105)
    at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:50)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:86)
    at java.base/java.util.Optional.orElseGet(Optional.java:369)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:86)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:32)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:38)
    at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:43)
    at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:31)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.lambda$execute$0(AssignWorkspaceStep.java:40)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution$2.withWorkspace(ExecuteActionsTaskExecuter.java:283)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:40)
    at org.gradle.internal.execution.steps.AssignWorkspaceStep.execute(AssignWorkspaceStep.java:30)
    at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:37)
    at org.gradle.internal.execution.steps.IdentityCacheStep.execute(IdentityCacheStep.java:27)
    at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:49)
    at org.gradle.internal.execution.steps.IdentifyStep.execute(IdentifyStep.java:35)
    at org.gradle.internal.execution.impl.DefaultExecutionEngine$1.execute(DefaultExecutionEngine.java:76)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:184)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:173)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:109)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:51)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:200)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:195)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:75)
    at org.gradle.internal.operations.DefaultBuildOperationRunner$3.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:153)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:68)
    at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:62)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.lambda$call$2(DefaultBuildOperationExecutor.java:76)
    at org.gradle.internal.operations.UnmanagedBuildOperationWrapper.callWithUnmanagedSupport(UnmanagedBuildOperationWrapper.java:54)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:76)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:74)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:408)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:395)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:388)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:374)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
    at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: /Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/android/app/src/main/res/drawable-night-port-xhdpi: Error: Invalid resource directory name
    at com.android.ide.common.resources.MergingException.throwIfNonEmpty(MergingException.java:170)
    at com.android.ide.common.resources.DataSet.loadFromFiles(DataSet.java:264)
    at com.android.build.gradle.tasks.MergeResources.lambda$doFullTaskAction$0(MergeResources.java:261)
    at com.android.build.gradle.internal.tasks.Blocks.recordSpan(Blocks.java:51)
    at com.android.build.gradle.tasks.MergeResources.doFullTaskAction(MergeResources.java:255)
Caused by: /Users/martindonadieu/Documents/Projects.nosync/Capacitor/capgo/android/app/src/main/res/drawable-night-port-xhdpi: Error: Invalid resource directory name

    ... 133 more

==============================================================================

it's seems there only one issue for drawable-night-port-xhdpi i have try to remove the platform and do it from fresh platform and the issue still happen I really love how fast it is to get the assets !

mlynch commented 2 years ago

Cool I get the same issue so will fix here shortly. Making progress 🀞

mlynch commented 2 years ago

@riderx 1.0.0-next.5@next should have this fixed

riderx commented 2 years ago

@mlynch after remove the platform and add again it works! Thanks a lot for this work, a new stone for our mobile app to don't need cordova stuff anymore! I still have a tiny issue related to that, it how the splash look when we start a new Capacitor app:

https://user-images.githubusercontent.com/4084527/160916734-693fc75f-2a47-45e6-85da-a7f863b55a28.mp4

The splash screen doesn't have the appropriate size by default on many phones, and after a few seconds it works. it give a bad feeling of dev who start to use Capacitor.

i know that a matter of settings in Android, but maybe since the tool you did could be added to the capacitor CLI. If you make this setting default, that will improve the feeling of Capacitor is a strong solution ready to go :) You can find a stack overflow post about it here : https://stackoverflow.com/questions/66285483/ionic-capacitor-android-splash-screen-responsiveness

mlynch commented 2 years ago

@riderx if you follow the steps in that linked solution does it resolve the scaling issue for you?

riderx commented 2 years ago

@mlynch it does, but just by making the first image as color, so we saw only the second one, not really optimal in my memory.

mlynch commented 2 years ago

@riderx I don't understand, can you elaborate?

riderx commented 2 years ago

I have tried many solutions and the only one I found who was working, was replacing the first broken image by a background color, and then when the second one load everything is good. I can check later tonight if you need additional information. You can see there : https://github.com/ionic-team/capacitor/issues/1627 it is a recurring problem with a lot of solution, that why I was thinking if capacitor-assets setup it, then no more discussion :)

mlynch commented 2 years ago

Thanks, I think this is out of scope of the current work I'm doing on this tool, but I'll share that with the team.

Ignoring that issue, is everything else working for you then? My tests are good, so I might cut a 1.0 soon and update the main branch to point to the new Capacitor version. let me know.

riderx commented 2 years ago

yes i understand! It was a bonus one ahh! i do think it's feature complete and well documented. Just still have a warning about the folder renaming, but otherwise this is good for me to release.

riderx commented 2 years ago

@mlynch got a new issue in IOS, when I upload to the app store it says some mandatory assets are missing.

Screenshot 2022-04-09 at 17 14 45 Screenshot 2022-04-09 at 17 17 10 Screenshot 2022-04-09 at 17 16 56
piotr-cz commented 2 years ago

It's possible to create Android 9-Patch splash screens with sharp:

Here's a sample code from my cordova legacy repo: https://gist.github.com/piotr-cz/1a3f1360d189231792db21756cb4a72d

SimonGolms commented 2 years ago

Actually there is a difference when only one platform is choosen. In the README the command is listed with npm run capacitor-assets generate ios and the platform is added as argument, while the command currently only accepts the platform as an option to work: npm run capacitor-assets generate -- --ios:

https://github.com/ionic-team/capacitor-assets/blob/3fd3c6d39e5a05dce9de1b2a06d6a4df509c0b3d/src/index.ts#L23-L30

I would have fixed the typo in the README directly as PR, but I wasn't sure if the actual intention to list the platform as argument is already correct and instead the code should be updated...

mlynch commented 2 years ago

@piotr-cz are 9 patches still used for splash/icons?

mlynch commented 2 years ago

@SimonGolms fixed thanks

mlynch commented 2 years ago

@riderx will fix, thanks for the note

piotr-cz commented 2 years ago

@piotr-cz are 9 patches still used for splash/icons?

@mlynch I'm a seasonal mobile developer so I'm not sure about current trend Given so many android devices and screen resolutions it's very convenient to use 9-patch for splash screens (or at least transparent logo with solid background)

leo-petrucci commented 2 years ago

Hey I tried easy mode with "@capacitor/assets": "^1.0.0-next.5" and wow, icon creation is incredible.

EDIT: My stretching issues were fixed by using the following SplashScreen plugin settings:

    SplashScreen: {
      launchAutoHide: false,
      androidScaleType: 'CENTER_CROP',
      backgroundColor: '#fff',
    },

Might be worth mentioning the optimal capacitor config setup to use the generated files? Either way, fantastic tool!

I'm also experiencing the issue mentioned in riderX's post, solution mentioned in the stackoverflow works πŸ‘

marcjulian commented 2 years ago

Thanks for this great tool πŸ‘ I am using "@capacitor/assets": "^1.0.0-next.5", and noticed that not all AppIcon and Splash sizes are generated.

My assets dir looks like this: Screenshot 2022-05-12 at 15 36 03

When I now execute capacitor-assets generate --ios the following 17 AppIcons and 1 Splash screen is generated.

Screenshot 2022-05-12 at 15 42 46

The following 14 AppIcons and 3 Splash Screens sizes are not generated, which are included by the default capacitor ios app:

Screenshot 2022-05-12 at 15 37 30

Do you plan on supporting the other sizes? Are those sizes not necessary anymore?

marcjulian commented 2 years ago

I receive the following error when I try to upload the App to TestFlight, when I remove the default Capacitor AppIcons

Missing App Icon. An app icon measuring 1024 by 1024 pixels in PNG format must be included in the Asset Catalog of apps built for iOS, iPadOS, or watchOS. Without this icon, apps cannot be submitted for review. For details, see https://developer.apple.com/ios/human-interface-guidelines/icons-and-images/app-icon/.

The AppIcon with the name AppIcon-512@2x.png is in that size 1024x1024px, but is not generated with capacitor-assets.

Looks like the same error as @riderx received while uploading to the Apple Store.

However, capacitor-assets generates the following AppIcon including AppIcon-1024x1024.png which is 1024x1024px, so why does Apple complain about this missing AppIcon size?

Screenshot 2022-05-13 at 09 33 07

One workaround which allows me to upload too TestFlight is to copy AppIcon-1024x1024.png and rename it to AppIcon-512@2x.png.

sandstrom commented 2 years ago

Feedback:

sanderschnydrig commented 2 years ago

One workaround which allows me to upload too TestFlight is to copy AppIcon-1024x1024.png and rename it to AppIcon-512@2x.png.

This workaround is perfect for now, thanks @marcjulian !

AE1NS commented 2 years ago

The easy mode is a really great feature to generate the resources! Unfortunately I have some challenges.

My input files (512x512):

/assets/logo.svg
/assets/logo-dark.svg
"dependencies": {
    "@capacitor/android": "4.1.0",
    "@capacitor/core": "4.1.0",
    "@capacitor/ios": "4.1.0",
},
"devDependencies": {
    "@capacitor/assets": "1.0.0-next.5",
}
  1. If the logo (visible content) covers the whole 512 and I generate the logo, I get cut off results in Android. I scaled the logo down to 128*185 and centered it inside the 512x512 container and then the logo looks fine on both, android and ios. But on the splash screen the logo is really small then. What am I doing wrong here? Am I missing any other parameter?
  2. If I run the script for ios (appended --ios), the log output displays 2 runs for ios? Also I see some issues with my contents.json inside the splash assets. The script just append new content to this file on each run. Is this a bug?
  3. How should the ios environment be set up? Do I have to change something inside the launch board? When I remove the existing logo and splash files and let the script generate it again, I see some yellow exclamation marks when I open the assets viewer in XCode. Are there any files missing in the generation? Or is there any demo project to see how the project must be basically set up before using this script?

Thanks for your work!

AE1NS commented 2 years ago

Additionally: As I am using the new Android 12 Splash Screen API, I switched from the generated drawables to simple vector assets with svg content (background color + logo vector centered). Would it be possible to add an option to this library to just generate the logo assets?

mlynch commented 2 years ago

Appreciate the feedback everyone. I had to put this on the backburner for a bit over the summer but taking a fresh look at it