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

Support for Dynamic Feature Modules #4810

Open
fruitedev opened this issue Jun 12, 2020 · 24 comments
Open

Support for Dynamic Feature Modules #4810

fruitedev opened this issue Jun 12, 2020 · 24 comments
Assignees
Labels
enhancement Proposed change to current functionality.

Comments

@fruitedev
Copy link

Hello,

I know there is support for AppBundles which makes things easier, but i couldn't find whether Dynamic Feature Modules is supported or not. Support of Dynamic Feature Modules will be really really helpful for us so please share any plan you have.

Thanks,

@jonathanpeppers
Copy link
Member

@fruitedev I remember there might have been some limitations when I first implemented app bundles:

https://github.com/xamarin/xamarin-android/blob/master/Documentation/guides/app-bundles.md#what-are-app-bundles

I remember something about needing to support "Instant Apps" for dynamic modules to work, which has a hard file-size limit. The Mono runtime was over the limit.

We should still look into this again. Anyone interested, please give a thumbs up above and that will help us know the demand, thanks!

@johnthiriet
Copy link

@jonathanpeppers Let's consider that the module I want to load dynamically is an aar archive with some jar dependencies and I have a binding over this one. Would that make the process easier on your side ? I mean I do want to load this aar dynamically since it's pretty heavy.

@evansmj
Copy link

evansmj commented Jul 30, 2020

Now that Apple has introduced App Clips, clients are now more interested in Android Instant Apps. Using app bundles may not be so possible due to the Mono runtime limit and that is fine, but if there was a way we could at least attach natively written instant app apk's as feature modules to the Xamarin.Android app that would be great.

@johnthiriet
Copy link

Still really really interested in the dynamic delivery for our applications. We are open for a quick call to explain more about why if you want.
@jonathanpeppers @davidortinau.

@dellis1972
Copy link
Contributor

I found that a community member has already bound the PlayCore library, https://github.com/PatGet/XamarinPlayCoreUpdater. This might have enough support to allow you to deliver a natively written apk as a dynamic feature and keep the main app as a Xamarin App.

@johnthiriet
Copy link

@dellis1972 Hum that might be but actually "publishing" this module to the play store will maybe be tricky if even possible as the application is in Xamarin. Even by creating a native application just for packaging this module I think it will not work well.
Have you heard about someone doing it ?

@dellis1972
Copy link
Contributor

@johnthiriet I haven't heard of anyone doing it yet. The developer it seems only used it for in app updates.

dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Nov 25, 2020
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
@dellis1972
Copy link
Contributor

dellis1972 commented Dec 2, 2020

I did some investigation last week as to what might be needed to support this feature.
Firstly the API's required do use this feature are not currently working in https://github.com/PatGet/XamarinPlayCoreUpdater. When trying to use them you will get a javac error at build time. This is down to the actual binding itself. One of the problems is that the SplitInstallManager uses Listener types which make use of Generics. Our binding story for generics is not completely working at this time. So that will be the first hurdle, get the binding working somehow. This might end up being something like a custom .jar which has a wrapper with a simplified API. We then bind the simplified API rather than use the code google provides. While this is not a great approach, it might be our only option.

The next issue is how do we build a "feature". Some investigation into this shows that it is not going to be straight forward.
The "feature" apk/zip needs to be built against the apk of the application. So we need to build any features after we have built the main application , but before we do the final bundling. The best option is to run a custom target before _PrepareBuildApk which will allow us to build the features, and gather any outputs.

The reason we need to build the features after is the packaged_resources zip file that gets produced by aapt2 as part of our build process MUST be passed as a -I argument to aapt2 when building the "feature"

aapt2 link --allow-reserved-package-id --package-id 0x7e --auto-add-overlay -o $(LevelPackageTemp) -I $(JavaPlatformJarPath) -I $(_PackagedResources) --manifest AndroidManifest.xml -A assets

This is required because the entires in the AndroidManifest.xml we need to include in the "Feature" MUST use resources from the main app for its distribution elements. This is a sample of a working AndoridManfiest.xml for an "Asset Feature"

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.infinitespacestudios.adtest"
        featureSplit="level1"
        android:isFeatureSplit="true">
	<dist:module dist:title="@string/level1" dist:instant="false">
		<dist:delivery>
            <dist:on-demand />
   		</dist:delivery>
		<dist:fusing dist:include="false" />
	</dist:module>
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
	<application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>

Note the @string/level1 resource. That item must be present in the main app strings.xml. So we need to build the main app before we build the feature. Also we cannot build the resources using the --proto-format it seems. We need to build as a normal apk then use the aapt2 convert call to change the format later.

aapt2 convert --output-format proto -o $(LevelPackageProtoTemp) $(LevelPackageTemp)

We then need to manipulate the output file to move things around a bit. By default aapt2 places the compiled AndroidManifest.xml in the root of the zip file. However for aab files it needs to be in a manifest subdirectory within the zip. So we need to fix up the zip. Luckily we already have a task that can do that, so we can probably reuse some code to handle this. Any assemblies we include in the "feature" zip file need to be in the root/assemblies folder as per our normal aab code. At this time the runtime does not support "reloading" our assembly list so we cannot support dynamic delivery of assemblies at this time. However with some additional investigation we might be able to support it.

Once we have this "feature" zip file build we can then pass that to the bundletool via the AndroidAppBundleModules ItemGroup (see here)

The current idea on how an end user might use this is that we allow for an additional piece of meta data on the ProjectReference ItemGroup.

  <ItemGroup>
      <ProjectReference Include="Levels\Level1\Level1.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
      </ProjectReference>  
  </ItemGroup>

In this case ReferenceOutputAssembly HAS to be false otherwise our build system will just include the assembly in the final package. AndroidDynamicFeature is the new item and if 'true' means that during packaging, the feature package will also be produced. The nice thing about using ProjectReference is that the feature will be built as part of a normal build.

At runtime once a user has installed a "feature" they can then use CreatePackageContext to get a new instance of an AssetManager which will contain the new assets.

var ass = CreatePackageContext(PackageName, 0);

For assemblies we need to decide what to do. For java apps they can call StartActivity to kick off an activity assuming the feature contains android activities. The AndroidManifest.xml for the feature would also need to have those activity elements in it so android can pick those up. This might work once we have runtime support to detect new split installs. However we are currently unsure how to do this.

The other option is to use Assembly.Load to load the new "feature" assembly. This might be required if you want to call C# code since there is no reference between the main app and the feature. Reflection will probably be needed to create an instance of a feature class and execute methods. We might be able to hook into the runtime Assembly.Load method do so a search in additional apks/directories if we don't find an assembly in our current mapping list. However this will need us to be able to known "where" the new things are installed.

So we have a number of things we need to implement and investigate before we can fully support this. We will probably have to break this up into a smaller set of projects. The initial goal will be to support dynamic delivery of "assets" the follow that up with dynamic delivery of assemblies later since the runtime changes will not be easy.

UPDATE: So more investigation has turned up some interesting info. Each "Feature" needs to have a different package-id. By
default the package-id is 0x7f for an app. What gradle seems to do is DECREMENT this value for each "feature" that is in the project. So your first "feature" will be 0x7e , you second 0x7d etc. So there is a hard limit on the number of features you can have. This value will need to be passed to aapt2 via the --package-id argument in addition to the --allow-reserved-package-id.

Some thoughts on using ProjectReference. I don't think we can use that now, mostly because we probably need to be able to reference the main app from the feature. To do this we need to pass the feature a path to the main app assembly which needs to be built first. So using a ProjectRefernce will not work since they will be built before the main app. So we will probably need to create a new ItemGroup AndroidDynamicFeature which we can then build after the main app.

<ItemGroup>
     <AndroidDynamicFeature Include="Features/Asset/AssetFeature.csproj" />
</ItemGroup>

This will allow us to pass in not only the main app dll but also all the references which the main app have. This will include things like AndroidX.* and GooglePlayServices. So there should be no need to add those things to the "feature" as PackageReference .

So a new todo items are.

  • Make sure all calls to BuildAppBundle pass the AndroidAppBundleModules ItemGroup
  • Update Aapt2Link task to support multiple items for the JavaPlatformJarPath or provide a new property to allow us to pass additional -I items.
  • Create a new Aapt2Convert task to handle converting from apk to aab formats.
  • Update Tasks like BuildApk and BuildBaseAppBundle so that we don't always include runtime native libraries and assemblies. These are not required when building a feature zip.
  • Update BuildApkSet to allow users to pass modules [Xamarin.Android.Build.Tasks] Allow users to specify modules for apk sets. #5327
  • Update BuildApkSet to allow users to use the --local-testing argument which can be used to test dynamic features without having to upload to google [Xamarin.Android.Build.Tasks] Allow users to specify modules for apk sets. #5327.
  • Investigate what might be needed to detect when new split installs are done and refresh our assemblies mapping in the runtime.
  • Create a new set of .targets which are specific to dynamic features and are geared to its requirements.
  • Investigate the idea of auto generating the AndroidManifest.xml for features as the format is slightly different.
  • Create a new template for dotnet new for a dynamic feature?
  • See if there is a way to allow debugging support when using aab files. This will be required in order to let users test downloading features etc. This cannot be done using apk files since we must build an aab for any of this to work.
  • Add support for --package-id and --allow-reserved-package-id to Aapt2Link.
  • Update the Resource.designer.cs generator to take in multiple R.txt files.
  • Compile the "Feature" against the assemblies from the main app. This will give it access to all the package and references of the main app (like AndroidX). We only want to pack the "Feature" assembly and nothing else.
  • Make sure Java binding code is generated and included in the Feature manifest file
  • A whole bunch of other stuff which I have not thought about or has not come up yet.

This is just a brain dump of all the stuff I have found other the last couple of days. There are probably more things we need to do but this is a good starting point. To manage some expectations, this is not going to be a quick process. We have not even mentioned IDE integration or the actual end user experience yet. So this is going to be an on going process. But we are looking at it, which is a start :)

@jonathanpeppers
Copy link
Member

Ah, so we should be able to knock this out in an afternoon, right??? 👀

@johnthiriet
Copy link

johnthiriet commented Dec 2, 2020

@dellis1972 That is the most interesting thing I have read in weeks :-)

dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jan 7, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jan 13, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Feb 3, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Feb 5, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Feb 8, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Feb 10, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Feb 23, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Feb 24, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Mar 1, 2021
…ets.

Context dotnet#4810

When using app bundles users install the application from an `apkset`.
At this time this only contains the base application. However we might
in the future support dynamic features in some fashion. In this case
developers should be able to pick which features they want installed.

This commit adds an `AndroidInstallModules` ItemGroup which will allow
developers to specify which modules to install. By default it will be
empty. This means all modules which are to be installed on first install
will be installed. So there is no change in the current behavior.

This commit also adds support for passing additional arguments to
`bundletool` via the new `$(AndroidBundleToolExtraArgs)` property.
jonpryor pushed a commit that referenced this issue Mar 9, 2021
…5327)

Context: #4810
Context: #4810 (comment)
Context: https://developer.android.com/guide/app-bundle/play-feature-delivery

Android introduced support for using [Android App Bundles][0] to
package features into separately downloadable [feature modules][1].

When using app bundles, users install the application from an `apkset`.
At this time this only contains the base application.  However we might
in the future support feature modules in some fashion.  In this case
developers should be able to pick which features they want installed.

This commit adds an `@(AndroidInstallModules)` ItemGroup which will
allow developers to specify which modules to install.  By default it
will be empty, which means that all modules which are to be installed
on first install will be installed.

There will be no change to current behavior when
`@(AndroidInstallModules)` is empty.

Also add support for passing additional arguments to `bundletool` via
the new `$(AndroidBundleToolExtraArgs)` property.

[0]: https://developer.android.com/guide/app-bundle
[1]: https://developer.android.com/guide/app-bundle/play-feature-delivery
@chc7042
Copy link

chc7042 commented Mar 29, 2021

Hi @dellis1972 and all
i saw the arcicle https://github.com/hinojosachapel/DynamicModules and it working correctly. as i know the android app-bundle has an limitation unlike with WPF dynamic loading. main purpose of android app bundle is seperate some size of binary as bundle for reduce initial time for loading. and it could not be splitted because app bundle should be determined at compile time. so it really different with WPF dynamic bundle. how do you think about this?
and more what i want to know is (i am beginer of WPF(C#)) is there no way to loading module look like https://github.com/hinojosachapel/DynamicModules?

dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue May 6, 2021
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
@dellis1972
Copy link
Contributor

I'm working on the initial support for this on #5890 .

It would be nice to get some community feedback on how I plan to implement this. You can read the PR and some initial documentation. All of this is subject to change, but it's a good start. I have something working for AndroidAsset locally but we are still a way off from getting other parts like AndroidResource or even Activities etc.

dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Mar 29, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Apr 8, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Apr 21, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Apr 27, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue May 6, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue May 16, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue May 25, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jun 6, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jun 22, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jun 27, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
@Eversor
Copy link

Eversor commented Jun 30, 2022

Looking forward to this feature, happy to know that a Monogame veteran is doing the job ;) may i help you testing it?

@dellis1972
Copy link
Contributor

@Eversor its still a work in progress I'm afraid. I have had to put it on hold for a while because of other more important tasks on Xamarin.Android. That said I did find some time to put a MonoGame specific example together at https://github.com/infinitespace-studios/MonoGameAndroidAssetPackExample for those who are interested 😄

dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jul 11, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jul 18, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Aug 29, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Sep 8, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Oct 20, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Nov 2, 2022
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
@jpobst jpobst added enhancement Proposed change to current functionality. and removed feature-request labels Dec 1, 2022
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jan 10, 2023
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jan 30, 2023
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Aug 8, 2023
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Oct 18, 2023
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Nov 8, 2023
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
dellis1972 added a commit to dellis1972/xamarin-android that referenced this issue Jan 4, 2024
Context dotnet#4810

This is the first implementation for supporting building Dynamic Feature
Assets modules for Android.  This idea is to have these "features" as
standard Xamarin Android Library projects. The `ProjectReference` will need to
use the following

```xml
<ItemGroup>
    <ProjectReference Include="Features\AssetsFeature\AssetsFeature.csproj">
         <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
         <AndroidDynamicFeature>true</AndroidDynamicFeature>
    </ProjectReference>
  </ItemGroup>
```

We set `ReferenceOutputAssembly` to `false` to ensure that the assembly
for the feature is NOT included in the final aab file.  The new meta
Data `AndroidDynamicFeature` allows the build system to pick up project
references which are "features".

As part of the final packaging step of the main app we will gather up all
the `ProjectRefernce` items which have `AndroidDynamicFeature` set to
`true` (and maybe `ReferenceOutputAssembly` set to `false`). This will
be done by the `_BuildDynamicFeatures` which will run just after
`_CreateBaseApk`.

It will call `_GetDynamicFeatureOutputs` for each `ProjectReference`
which will collect the `output` files for each feature. It will then call
the `BuildDynamicFeature` target via the `MSBuild` task for each
`ProjectReference`.  The `BuildDynamicFeature` is the target responsible
for collecting all the assets and packaging them using `aapt2` up into a
zip. Once all the `BuildDynamicFeature` calls are complete the created
`zip` files will be added to the `AndroidAppBundleModules` and then
included in the final `aab` file.

It might seem odd that the feature projects are built after the main app.
However this is required because the  feature needs to use the
`packaged_resources` file as an input to `aapt2` when building the
feature. This is why the `_BuildDynamicFeatures` happens AFTER `_CreateBaseApk`.
It is only at that point that the final `packaged_resources` file exists.

One of the very weird things is that the feature zip needs to be built
using the `aapt2` `--static-lib` flag. As a result we need to call
`aapt2 convert` on the final zip. This is because it is in the `apk`
`binary` format and needs to be converted over to the `aab` `proto` format.
So there is a new `Aapt2Convert` task which handles that job. It also
makes sure the `AndroidManifest.xml` file is in the right place when
converting to `proto` format.

A basic project example using .net 6 for a feature would look like this.

```xml
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
</Project>
```

As you can see it is just a normal library project. At this time is CANNOT
contain any `AndroidResource` items such as drawables or layouts. It must
only contain `AndroidAsset` items. So we probably should have a new
template for a `Dynamic Feature` which just creates the `csproj` and
the `Assets` folder.

One sticking point is probably the `AndroidManifest.xml` file which we
need for a `feature`. There is a sample

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:dist="http://schemas.android.com/apk/distribution"
        android:versionCode="1"
        android:versionName="1.0"
        package="com.companyname.DynamicAssetsExample"
        featureSplit="assetsfeature"
        android:isFeatureSplit="true">
  <dist:module dist:title="@string/assetsfeature" dist:instant="false">
    <dist:delivery>
      <dist:on-demand />
    </dist:delivery>
    <dist:fusing dist:include="false" />
  </dist:module>
  <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
  <application android:hasCode="false" tools:replace="android:hasCode" />
</manifest>
```

The interesting parts are all the additional `dist` elements. What we can
probably do is auto generate this during the `BuildDynamicFeature`.
However we need to think carefully about this since if we plan to have
code and `Activities` in the feature at some point, those will also need
to end up in the `AndroidManifest.xml`.

For additional information on the Play Core Dynamic Features check the
following links.
[1] https://developer.android.com/guide/playcore/asset-delivery
[2] https://developer.android.com/guide/playcore/feature-delivery
@dellis1972
Copy link
Contributor

For those of you who are interested in asset packs I have a new PR up at #8631.

We could use some feedback on the approach we plan to take on this if anyone has the time :)

jonpryor pushed a commit that referenced this issue Mar 15, 2024
Context: #4810

Google Android began supporting splitting up the app package into
multiple "packs" with the introduction of the `aab` package format.
Each "pack" can be downloaded to the device either at install time or
on-demand. This allows application developers to save space and
install time by only installing the required parts of the app
initially, then installing other packs as required.

There are two types of packs: Feature packs and Asset packs.

*Feature* pack contains *non-native* Java code and other resources.
Java code in feature packs can be launched via
`Context.StartActivity()`.  Currently, .NET Android cannot support
Feature packs, as Feature packs cannot contain native code.

*Asset* packs contain *only* `@(AndroidAsset)`s.  They *cannot* contain
any code or other resources.  This type of pack can be installed at
install-time, fast-follow, or ondemand.  It is most useful for apps
which contain a lot of Assets, such as Games or Multi Media applications.

Consider a default Android app with an asset:

	% dotnet new android
	% mkdir Assets
	% touch Assets/movie1.mp4
	% dotnet build -c Release
	% unzip -l bin/Release/net9.0-android/*-Signed.aab | grep -Ei 'asset|movie'
	        0  01-01-2010 00:00   base/assets/movie1.mp4
	…
	       12  01-01-2010 00:00   base/assets.pb

Note that assets are listed within the `.aab`, within the
`base/assets` directory.

Add support for the following metadata items:

  * `%(AndroidAsset.AssetPack)`: the *name* of the asset pack that
    should contain the asset.
  * `%(AndroidAsset.DeliveryType)`: How the named asset pack should
    be installed.

When `%(AndroidAsset.AssetPack)` is specified, the asset is placed into
a different directory within the `.aab` file.  For example, add
additional assets:

	% touch Assets/movie-install.mp4
	% touch Assets/movie-follow.mp4
	% touch Assets/movie-demand.mp4

Then update the `.csproj` to specify `%(AssetPack)` and
`%(DeliveryType)`:

	<ItemGroup>
	  <AndroidAsset Update="Assets\movie-install.mp4" AssetPack="install_movies" DeliveryType="InstallTime" />
	  <AndroidAsset Update="Assets\movie-follow.mp4"  AssetPack="follow_movies"  DeliveryType="FastFollow" />
	  <AndroidAsset Update="Assets\movie-demand.mp4"  AssetPack="demand_movies"  DeliveryType="OnDemand" />
	</ItemGroup>

Now we see:

	% dotnet build -c Release
	% unzip -l bin/Release/net9.0-android/*-Signed.aab | grep -Ei 'asset|movie'
	        0  01-01-2010 00:00   base/assets/movie1.mp4
	       12  01-01-2010 00:00   base/assets.pb
	        0  01-01-2010 00:00   demand_movies/assets/movie-demand.mp4
	      644  01-01-2010 00:00   demand_movies/manifest/AndroidManifest.xml
	       12  01-01-2010 00:00   demand_movies/assets.pb
	        0  01-01-2010 00:00   follow_movies/assets/movie-follow.mp4
	      646  01-01-2010 00:00   follow_movies/manifest/AndroidManifest.xml
	       12  01-01-2010 00:00   follow_movies/assets.pb
	        0  01-01-2010 00:00   install_movies/assets/movie-install.mp4
	      648  01-01-2010 00:00   install_movies/manifest/AndroidManifest.xml
	       12  01-01-2010 00:00   install_movies/assets.pb

The `AndroidManifest.xml` files tell the Google Play Store how they
asset packs should be deployed.
@dellis1972
Copy link
Contributor

Asset Pack support has been merged, should be in the next .NET 9 Previous.

Still trying to figure out how we can even support feature packs :/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Proposed change to current functionality.
Projects
None yet
Development

No branches or pull requests

9 participants