unity-jar-resolver icon indicating copy to clipboard operation
unity-jar-resolver copied to clipboard

Android - Support custom manifestPlaceholders in Dependencies.xml

Open jkasten2 opened this issue 5 years ago • 11 comments

Issue

Some plugins have custom manifestPlaceholders that must be set, otherwise AndroidManifest.xml merge errors will occur at build time. These are normally set in the app/build.gradle for an Android Studio project. Example:

android {
   defaultConfig {
      manifestPlaceholders = [
          key1: 'value1',
          key2: 'value2'
      ]
    }
 }

To be clear I am not referring to the applicationId, but custom placeholders.

Possible Solutions

Option 1

<dependencies>
  <androidPackages>
    <androidPackage spec="com.plugin.example:example-module:[1.0.0, 2.0.0[">
       <manifestPlaceholders>
         <item name="key1">value1<entry>
         <item name="key2">value2<entry>
       </manifestPlaceholders>
    </androidPackage>
</dependencies>

Since there will be a PluginNameDependencies.xml for each plugin this would a straightforward spot to put them.

Option 2

A new PluginNameManifestPlaceholders.xml file each plugin could define. Since manifestPlaceholders are scoped to the gradle project this fits better into how gradle really defines manifestPlaceholders

Option 3

Something build into Unity I am missing? There does seem to be some kind of settingsTemplate.gradle file. https://docs.unity3d.com/Manual/android-gradle-overview.html However it seems like this does not automatic get imported according to this note from the link above.

You can also use your own settings.gradle file by providing a settingsTemplate.gradle file in your Plugins directory in the same way (although there is currently no automatic way to do this). Regardless I did try creating a settingsTemplate.gradle file however Unity didn't seem to pick it up.

@stewartmiles Maybe you have some experience with with this Unity feature already?

jkasten2 avatar Jul 10 '19 07:07 jkasten2

@jkasten2 I have no experience with the settingsTemplate.gradle Unity feature. I'm wondering how this would work in older versions of Unity as I would expect you would like to reach the widest possible audience with this feature?

stewartmiles avatar Jul 10 '19 16:07 stewartmiles

@stewartmiles hmm, that is ture, I think settingsTemplate.gradle is a somewhat newer feature for Unity. I actually didn't know it existed until I did some searching yesterday. Since this plugin does handle injecting applicationId in the AndroidManifest.xml from what I have found in ReplaceVariablesInAndroidManifest it seems like adding support for any manifestPlaceholders could be done here as well.

jkasten2 avatar Jul 10 '19 18:07 jkasten2

@jkasten2 that's true. TBH Adding support for merging an arbitrary set of information into the main application manifest would be a very cool feature. Plugins like Firebase Cloud Messaging, Facebook etc. all ship with a main application manifest under Plugins/Android/AndroidManifest.xml that developers need to merge in addition to customizing the activity entry point on Android. As I'm sure you're aware, many Unity developers simply can't do this, it's outside of their area of expertise so making this transparent to developers when using plugins that target Android would be pretty neat.

stewartmiles avatar Jul 10 '19 20:07 stewartmiles

I'm developing a unity plugin which is depending on my native-android plugin.. And I'm facing the same issue,, is there any working solution for that?

Thank you!

@stewartmiles @jkasten2

ibrahimAlii avatar Feb 28 '20 17:02 ibrahimAlii

@stewartmiles @jkasten2 @ibrahimAlii Hey! What about this? Sound good? If yes I can create a plugin or create a pull request to integrate this. What do you think?

Having this placeholder in your mainTemplate.gradle

android {
    compileSdkVersion **APIVERSION**
    buildToolsVersion '**BUILDTOOLS**'

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    defaultConfig {
        minSdkVersion **MINSDKVERSION**
        targetSdkVersion **TARGETSDKVERSION**
        applicationId '**APPLICATIONID**'
        ndk {
            abiFilters **ABIFILTERS**
        }
        versionCode **VERSIONCODE**
        versionName '**VERSIONNAME**'
     
        ##MANIFEST_PLACEHOLDERS## <---- THIS ONE           
    }
...

Having this placeHolders in my AndroidManifest.xml under Plugins/Android

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools"
    android:installLocation="preferExternal">
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>

    <application
        android:theme="@style/UnityThemeSelector"
        android:icon="@mipmap/app_icon"
        android:label="@string/app_name">
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />

            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="http"
                      android:host="${hostName}"/> <----THIS ONE
            </intent-filter>

            <meta-data android:name="facebookIdExample" android:value="${facebookId}" /> <---- THIS ONE
        </activity>
    </application>
</manifest>

Having something like @jkasten2 said:

MyGameAndroidManifestPlaceholders.xml

    <placeholders>
        <key name="hostName">"www.example.com"</key>
    </placeholders>

FacebookAndroidManifestPlaceholders.xml

    <placeholders>
        <key name="facebookId">1286656789</key>
    </placeholders>

A postprocess will result exporting a project with a build.gradle like this

...
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 29
        applicationId 'com.test.gradle'
        ndk {
            abiFilters 'armeabi-v7a', 'x86'
        }
        versionCode 1
        versionName '0.1'
     
        manifestPlaceholders = [
            hostName:www.example.com
             facebookId:1286656789
        ]           
    }
...

Having an .apk with this AndroidManifest

image

MartinGonzalez avatar Feb 28 '20 22:02 MartinGonzalez

Check this and tell me if it helps: https://github.com/MartinGonzalez/unity-android-manifest-placeholders-resolver

In Package directory, modify your manifest.json and add

"com.martingonzalez.androidmanifestplaceholdersresolver": "[email protected]:MartinGonzalez/unity-android-manifest-placeholders-resolver.git",

This will install a dependency in the Editor. Then create an xml anywhere inside Assets for example: MyGameAndroidManifestPlaceholders.xml (it's important the AndroidManifestPlaceholders.xml part)

Inside add info like this:

<placeholders>
    <hostName>www.example.com</hostName>
    <scheme>mygame</scheme>
</placeholders>

Go to your mainTemplate.gradle and add this placeholder under

android {
    defaultConfig {     
        ##MANIFEST_PLACEHOLDERS##
    }
...

In your AndroidManifest.xml under Assets/Plugins/Android add those placeholders somewhere. ex:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.unity3d.player"
    xmlns:tools="http://schemas.android.com/tools"
    android:installLocation="preferExternal">
    <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>
    
    <application
        android:theme="@style/UnityThemeSelector"
        android:icon="@mipmap/app_icon"
        android:label="@string/app_name">
        <activity android:name="com.unity3d.player.UnityPlayerActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />

            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="http"
                      android:host="${hostName}"/>
            </intent-filter>
            
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="${scheme}"
                      android:host="gizmos" />
            </intent-filter>
        </activity>
    </application>
</manifest>

And build an apk. You can see it AndroidManifest with AndroidStudio

MartinGonzalez avatar Feb 29 '20 00:02 MartinGonzalez

@MartinGonzalez the approach looks neat, any thoughts on how this would work with old versions of Unity that folks are still actively using e.g 5.6 ?

stewartmiles avatar Mar 04 '20 16:03 stewartmiles

Today I can do a test, perhaps with a preprocess where I can just replace strings for older versions. But also thinking about, tell me if I’m missing something, why not having that preprocess for any unity version instead of using gradle?

I mean, just using the same palaceholder standard but intead of using the mainTemplatr.gradle we can just read AndroidManifest as a string and replace holders with values and rewrite the modified Manifest

MartinGonzalez avatar Mar 04 '20 16:03 MartinGonzalez

@MartinGonzalez yeah I think preprocessing Plugins/Android/AndroidManifest.xml is a reasonable way to go. The only issue is that modifying a file in-place in the project is a little harder to back out the changes, check out https://github.com/googlesamples/unity-jar-resolver/blob/master/source/PlayServicesResolver/src/GradleTemplateResolver.cs to see what I mean.

If I remember correctly - I may be wrong - with older versions of Unity (e.g 5.6) it's hard / not possible to patch components that are staged in the build output directory when the internal build system is selected, it's only possible to patch files in project that are sucked up by the build system.

stewartmiles avatar Mar 04 '20 17:03 stewartmiles

Good point. I will do some tests over Unity 5.x to see how we can achieve that. Thanks!

MartinGonzalez avatar Mar 04 '20 17:03 MartinGonzalez

It would be useful to have such functionality!

leni8ec avatar Mar 10 '23 03:03 leni8ec