Option to handle Maps v2 keys in gradle file - android

Since we need to have 2 maps keys for an application (debug, release), I would like handle them in the gradle file.
Is there an option to put the keys into the gradle file buildVariants and insert a variable in the manifest, that it uses the key depending on the current variant?
If yes, how?

Since you are using gradle you can do the following:
build.gradle
android {
.. .. ...
buildTypes {
debug {
resValue "string", "google_maps_api_key", "[YOUR DEV KEY]"
}
release {
resValue "string", "google_maps_api_key", "[YOUR PROD KEY]"
}
}
}
And in your AndroidManifest.xml
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="#string/google_maps_api_key"/>
This way you only have one AndroidManifest.xml and you set value based on your build type. Hope this helps.

This can be done pretty simply with the build system. You don't need to worry about doing variable substitution in the manifest; the build system's manifest merger can do the work for you.
Set up your project like this:
app_module
+--src
+--main
+--AndroidManifest.xml
+--debug
+--AndroidManifest.xml
+--release
+--AndroidManifest.xml
You already have the src/main/AndroidManifest.xml file. In this copy, remove the Google Maps API key <meta-data> tag. What you want to do is supply debug- and release-specific versions of this tag in the debug and release manifest files, and let the manifest merger do its thing. In the build-type-specific manifest files, you don't need to supply full manifests with redundant information, but only those parts that will be different. The merger will use the one in src/main for the bulk of it:
src/debug/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sbarta.myapplication" >
<application>
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="DEBUG_VALUE"/>
</application>
</manifest>
src/release/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.sbarta.myapplication" >
<application>
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="RELEASE_VALUE"/>
</application>
</manifest>

Related

How to deal with "${applicationid}" in Xamarin?

I'm trying to create bindings for Zendesk library and I faced with a problem.
Zendesk Belvedere library (belvedere-1.0.2.1.aar) contains a file provider in its manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
...
<application>
<provider
android:name="com.zendesk.belvedere.BelvedereFileProvider"
android:authorities="${applicationId}.belvedere.attachments"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/belvedere_attachment_storage" />
</provider>
</application>
</manifest>
When Gradle is used as build tool, it puts this aar to the APK file and it replaces ${applicationId}.belvedere.attachments with com.your_package_name_here.belvedere.attachments in the merged manifest file. It's fine.
However, Xamarin handles it differently. Here is what I found in the manifest of my final APK:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...
...
<application
...
...
<provider
android:name="com.zendesk.belvedere.BelvedereFileProvider"
android:exported="false"
android:authorities="dollar_openBracket_applicationId_closeBracket"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/belvedere_attachment_storage" />
</provider>
...
...
</application>
</manifest>
Obviously, dollar_openBracket_applicationId_closeBracket is not what I need.
Seems everything works, however it makes impossible to install several Xamarin applications that use these bindings, because all of them would have conflicting providers with the same authority (and user would get INSTALL_FAILED_CONFLICTING_PROVIDER error).
Is there a way to change dollar_openBracket_applicationId_closeBracket in the manifest?
Edit: A small sample that shows the problem: https://gitlab.com/lassana/ZendeskXamarin/
The current Xamarin.Android manifest merge build task, up to and including
version 7.1.0.19, does not provide any bundeID/ApplicationID (${}} substitution in the merged manifest like gradle does.
This is just a limitation in the manifest processing/merge task, thus you are ending up with dollar_openBracket_applicationId_closeBracket in your final manifest and will have to correct both manifests yourself.
The only current solution know to me to avoid the manifest merge task and it's limitation is to:
Remove the file provider entry from the '.aar`'s manifest
Add the complete file provider entry your app's manifest
Note: You have to do both steps
Depending upon how often the .aar is changing and where you are sourcing the .aar file from:
Manually unzip the aar, remove the entry and re-zip the aar (the quickest way)
Automated this in a build step via a shell script using bash or powershell cmds
Write a MSBuild C#-based Task to do it.
Request that the aar manifest be changed upstream (not likely to happen ;-) since it works fine w/ gradle)
FYI: Personally I have seen the ${applicationId} issue you are having a few times. I have written build scripts (bash/.ps1) to do the manifest fix-up as it seems to always be some special case in the .arr's manifest that I am dealing with.
According to Microsoft team, Xamarin.Build.Download (0.4.12-preview) finally fixes this bug.
So, you should follow these steps:
Update the Nuget Package to 0.4.12-preview3
Restart the IDE
Delete all cached locations of NuGet Packages
Delete bin/obj
Rebuild
The main manifest file of your project is the higher priority manifest, so if you add the provider element to main manifest file and apply a merge rule marker for android:authorities attribute, the merger tools replaces the android:authorities value that you declare in the main manifest.
Declare provider element in main manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...
...
<application>
<provider
android:name="com.zendesk.belvedere.BelvedereFileProvider"
android:authorities="com.example.belvedere.attachments"
tools:replace=”android:authorities” >
</provider>
</application>

androidTest AndroidManifest.xml ignored

I have the same issue mentioned in this post AndroidTest Manifest permission not detected
and this post AndroidManifest in androidTest directory being ignored
--> If I put the test manifest in androidTest, debugAndroidTest, androidTestDebug, it never gets picked up and merged.
the answers about putting the AndroidManifest.xml in the debug folder are correct; that does seem to work. (put the test manifest in src/debug
What I want to know is why can't you put it in the androidTest directory? All the documentation I've read while trying to figure this out makes it sound like you should be able to, and that if you can't then I'm thinking that sounds like some bug in the manifest merger.
For what it's worth, I'm using Android Studio
That is correct and totally agree with you on the confusing documentation. The AndroidManifest.xml under androidTest* source sets would be packaged for the instrumentation APK that does your tests on your actual app APK. If you open the generated APKs for debug and androidTest under build/outputs/apk/ after compiling your app module with the command gradlew assembleDebugAndroidTest (assuming that you haven't changed the testBuildType in you build.gradle, more info here), you'll find that any AndroidManifest.xml configuration added under androidTest will be in the androidTest APK and not in your debug app APK.
And also as you said, in case you need test specific configurations like extra permissions, you'll have to place them in the AndroidManifest.xml under the debug source set instead of main, hence they'll only be available for testing your app but not in your release build. Of course you can always double check by opening the generated APKs after compiling to make sure that the configuration is right for each build variant.
If you need to add extra permissions for tests, you can do it.
You should set the same android:sharedUserId in default AndroidManifest.xml and androidTest/AndroidManifest.xml.
For example:
AndroidManifest.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"
android:sharedUserId="com.yourpackagename.uid">
<application
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme"
tools:replace="android:allowBackup">
</application>
</manifest>
androidTest/AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:sharedUserId="com.yourpackagename.uid">
<uses-permission android:name="android.permission.***" />
</manifest>
For details: https://stackoverflow.com/a/14196493/3901000

Localytics - Programmatically set app key

Is it possible to programmatically set the app key for Localytics? From the integration guide (https://support.localytics.com/Android_SDK_integration), it seems like you must set it in the Manifest file as meta-data.
<meta-data android:name="LOCALYTICS_APP_KEY" android:value="APP KEY FROM STEP 2"/>
From the following post, it also seems like it's impossible to dynamically set Android meta-data. How to add metadata dynamically (Not in manifest but inside code)?
I'd like to be able to set the app key dynamically based on Gradle buildType so I can have a release app key and a debug app key.
You can use manifest merging to support different app keys for your build types (e.g. debug versus release) or your product flavors (e.g. free versus paid).
To support different app keys for your builds types:
Create src/debug/AndroidManifest.xml and src/release/AndroidManifest.xml.
Remove the meta-data tag from src/main/AndroidManifest.xml.
Add the appropriate meta-data tag to your build type specific manifest.
src/debug/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app" >
<application>
<meta-data
android:name="LOCALYTICS_APP_KEY"
android:value="DEBUG_APP_KEY" />
</application>
</manifest>
src/release/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.app" >
<application>
<meta-data
android:name="LOCALYTICS_APP_KEY"
android:value="RELEASE_APP_KEY" />
</application>
</manifest>
For different app keys based on your product flavors, just replace debug and release above with your product flavor names.
There is an override on Localytics.integrate that takes an api key.
Localytics.integrate(this, "API_KEY")
If in case you are using autoIntegrate, use following API which takes Application context as first argument.
Localytics.autoIntegrate(this, "API_KEY");

Android Studio two flavors with different manifest files

I'm having issues with defining two different manifest files for my flavors in Android Studio. This is my current project structure:
The AndroidManifest.xml in the free flavor looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.example.package">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
</manifest>
The AndroidManifest.xml in the main flavor has no uses-permissions, but contains the rest of the manifest code that is shared between all flavors.
The AndroidManifest.xml in the pro flavor looks like this:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.example.package">
<uses-permission android:name="com.android.vending.CHECK_LICENSE" />
</manifest>
build.gradle defines the two flavors like
productFlavors {
free {
applicationId 'se.example.package.free'
minSdkVersion 14
targetSdkVersion 21
versionCode 1
versionName '1.0'
}
pro {
minSdkVersion 14
applicationId 'se.example.package.pro'
targetSdkVersion 21
versionCode 2
versionName '1.1'
}
}
The result that I am expecting is that the different flavors defines different uses-permissions. This is not the case. The result is currently that the both flavors only defines the <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> as defined in AndroidManifest.xml in the pro flavor.
I have tried:
Clean project
Rebuild project
Restart Android Studio
Sync gradle
But without success. How am I to fix this? Any help is appreciated.
EDIT 1
I changed the location of each flavors AndroidManifest.xml file from each of the res folders to free and pro folder. The result of this:
Pro flavor shows Licence permission as expected.
Free flavor shows permissions from both AndroidManifest.xml
files, License and network permissions (Should be only network)
This feels like an issue of project structure. What to make of this?
EDIT 2
I pulled the merge reports as Commonsware hinted, these are the reports regarding uses-permissions
Free:
uses-permission#com.android.vending.CHECK_LICENSE
ADDED from qwknoteGIT:licencing-library:unspecified:26:5
android:name
ADDED from qwknoteGIT:licencing-library:unspecified:26:22
Pro:
uses-permission#com.android.vending.CHECK_LICENSE
MERGED from qwknoteGIT:licencing-library:unspecified:26:5
Tech background:
on this link it explains the techniques and parameters that can be use for manifest merging: https://developer.android.com/studio/build/manage-manifests#merge_rule_markers
One in specific is the tools:node that points out how certain XML nodes on the manifest should behave whilst merging.
Solution:
to achieve some permisions in one and different in other manifest, add ALL permissions you need to the main and in the flavours manifest remove the ones you don't need, like the example below:
free remove the check license
<uses-permission
android:name="com.android.vending.CHECK_LICENSE"
tools:node="remove"/>
Your problem is coming from a library, not your flavors. Specifically, qwknoteGIT:licencing-library is requesting CHECK_LICENSE.
If you are not using that library in all flavors, use a flavored compile statement (e.g., proCompile) to only use that library in that flavor.
If you are using the library for all flavors, but feel confident that you do not need the permission in one flavor, that's where a tools:node attribute can be used, in the flavor's manifest, to block out that permission supplied by the library.
And the manifest merger report is your friend. :-)
This should solve the problem, at least. I find it useful in specifying the exact manifest to use for each variant. Cheers! It explicitly directs to the manifest file under each variant folder.
android {
productFlavors {
prod {
manifest.srcFile "prod/AndroidManifest.xml"
}
dev {
manifest.srcFile "dev/AndroidManifest.xml"
}
}
...
}
Specify your Manifest exclusively under sourceSets, In your App build.gradle
android {
productFlavors {
bizdartFlavourNoCallLog {
minSdkVersion 16
applicationIdSuffix '.bizdart'
targetSdkVersion 26
dimension "tier"
sourceSets {
main {
manifest.srcFile "src/bizdartFlavourNoCallLog/AndroidManifest.xml"
}
}
copy {
from 'src/bizdartFlavourNoCallLog/'
include '*.json'
into '.'
}
}
}
}
See https://developer.android.com/studio/build/manifest-merge with param tools:node="merge"
High priority manifest (Free):
<activity android:name="com.example.ActivityOne"
android:screenOrientation="portrait"
tools:node="merge">
</activity>
Low priority manifest (Main):
<activity android:name="com.example.ActivityOne"
android:windowSoftInputMode="stateUnchanged">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
Merged manifest result:
<activity android:name="com.example.ActivityOne"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateUnchanged">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
You should change your code:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="se.example.package">
for:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.your.appid">
I encountered the same problem, and I found that it was because I put "pro" and "tree" flavor folder under the lib project, and the problem was solved after I move the flavors folder under the app project

Product Variants: Multiple Activities

I'm using Product Variants in gradle/android studio to achieve the following project setup:
Two apps, that are 80% similar in one android studio project.
Each app should have its own manifest package path (they should basically behave like two independent apps - with it's own google api and push keys)
I've followed multiple tutorials in my attempt to achieve this (placeholders, multiple manifests) but nothing works.
Following this tutorial I did the following:
http://www.kevinrschultz.com/blog/2014/03/23/using-android-content-providers-with-multiple-package-names/
My build.gradle:
android {
compileSdkVersion 19
buildToolsVersion '20'
defaultConfig {
minSdkVersion 10
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
}
productFlavors {
app1 {
packageName "com.test.app1"
//applicationId "com.test.app1"
}
app2 {
packageName "com.test.app2"
//applicationId "com.test.app2"
}
}}
And here my manifest files.
src/main/manifest:
<?xml version="1.0" encoding="utf-8"?>
<application>
</application>
src/app1/manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.test.app1”
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.test.app1.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait" >
</activity>
</application>
</manifest>
src/app2/manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.test.app2”
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.test.app2.MainActivity"
android:label="#string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait" >
</activity>
</application>
</manifest>
This gives me the following error message:
Manifest merger failed : Main AndroidManifest.xml at AndroidManifest.xml manifest:package attribute is not declared
Unfortunately I can't set a package to each individual manifest tag as my app requires different package paths. If I do it anyway, the merger is not able to merge the manifest files because of different package values. Besides packageName I also tried setting "applicationId" but this doesn't work, either. Using a placeholder like this package="${applicationId}" doesn't work because it doesn't resolve the variable value.
Any ideas how to solve this problem? I'm using Android Studio 0.8.2 with gradle 0.12.2
You can safely add
<manifest xmlns:android="..."
package="com.test.app">
</manifest>
in your main manifest.
But you have to use applicationId and not packageName in your build.gradle.
The applicationId of the build.gradle will overwrite this value during your different builds.
A better way of doing this would be to use applicationIdSuffix instead of applicationId for your different product flavors.
The field have to be present in your main manifest.
In addition to your src/app1/AndroidManifest.xml and src/app2/AndroidManifest.xml you will have a src/main/AndroidManifest.xml which can contain elements both apps share.
In your flavor-specific manifests, you do not have to specify a package attribute, since that is already defined in your build.gradle.
In your main manifest however, you need to define a package attribute, to specify the package name used for your shared resources:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.app">
</manifest>
Make sure that you do not have any dependent libraries, which is also using that name, or you will get another build error.
Furthermore, you will want to specify applicationId instead of packageName in you build.gradle to decouple the package name used for identification of your app from the package name used for internal resource access.
Check this link for further information: http://tools.android.com/tech-docs/new-build-system/applicationid-vs-packagename
I resolved same issue putting the same package="com.ex.app" in every flavor's manifest and putting applicationId and useOldManifestMerger true into build.gradle file
android {
useOldManifestMerger true
productFlavors{
app1{
applicationId = "com.ex.app1"
}
app2{
applicationId = "com.ex.app2"
}
}
}
I started over fresh but this time Android Studio generated the Variant configuration for me (under project settings there's a tab for flavors, variants, etc.)
I also decided to change the package name of all three variants to the same name as the identification of each app (variant) is done by the applicationId (which has nothing to do with the actual package names of the variants).
Thanks to pdegand59 for his help!

Categories

Resources