I currently use a MapActivity in my application.
I use it with 2 API Keys. One for debugging, and one for "production"
I am fed up with changing these values in the xml layout:
<view class="com.google.android.maps.MapView"
android:id="#+id/myGmap"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:enabled="true"
android:clickable="true"
android:apiKey="#string/api_key_prod" />
I am fed up trying to change the apikey each time and replace prod by debug each time.
Is that possible to change this key within the onCreate() of my application.
Imagine that I have a boolean preference that look like: isDebug.
I may check thi preference on my phone and disable it by default on user application. and make something like:
if (isDebug)
myMap.setApiKey(R.string.api_key_debug)
else
myMap.setApiKey(R.string.api_key_prod)
Thank a lot for any help.
You cannot both have the widget in your layout and set the API key in Java.
If you dynamically create the MapView via its constructor, you can supply the API key that way from Java code, but then you will need to dynamically add it to your layout.
That being said, I'd deal with the problem via your build process (e.g., based on debug/production build, copy the right XML file into the right directory).
This works for me.
This variant of MapView constructor is documented here:
https://developers.google.com/maps/documentation/android/reference/com/google/android/maps/MapView
#Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
String mapApiKey = <your choice logic here>
mMapView = new MapView(this, mapApiKey);
setContentView(mMapView);
You should use Product Flavors.
For example:
android {
...
defaultConfig {
minSdkVersion 8
versionCode 10
}
productFlavors {
dev {
resValue "string", "google_maps_api_key", "DEV_API_KEY"
}
prod {
resValue "string", "google_maps_api_key", "PROD_API_KEY"
}
}
}
You have to create google maps object dynamically. Your layout will contaion only parent layout for creating object.
For extra security put your API key and secrets in the local.properties file and access it using BuildConfig as follows:
In your top-level settings.gradle file, include the Gradle plugin portal, Google Maven repository, and Maven central repository under the pluginManagement block. The pluginManagement block must appear before any other statements in the script.
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
}
In Android Studio, open your project-level build.gradle file and add the following code to the dependencies element under buildscript:
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin' version '2.0.1' apply false
}
Open your module-level build.gradle file and add the following code to the plugins element.
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
Save the file and sync your project with Gradle.
Open the local.properties in your project level directory, and then add the following code. Replace YOUR_API_KEY with your API key.
MAPS_API_KEY=YOUR_API_KEY
Save the file.
Access the key within AndroidManifest.xml file:
In your AndroidManifest.xml file, go to com.google.android.geo.API_KEY and update the android:value attribute with MAPS_API_KEY as follows:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${MAPS_API_KEY}" />
Access the key within Java/Kotlin code: Just use BuildConfig as follows:
BuildConfig.MAPS_API_KEY
More can be found in docs.
Related
I'm new to React Native. The task at hand is to set my Google API key in AndroidManifest.xml without exposing it, when pushed to GitHub.
I set up an environment variable called API_KEY, but however I want to access it in the .xml, I get an error when trying to spin up the dev server.
So far I tried:
android:value=${env.API_KEY}
android:value="${env.API_KEY}"
android:value="${API_KEY}"
Thank you!
Based on the second comment (from kenmistry), the solution that worked for me was indeed to create a placeholder in build.gradle, but since, for whatever reason, configuring and referring a .env file did't work, I invoked my environment variables like so in build.gradle:
manifestPlaceholders = [API_KEY: "$System.env.API_KEY"]
and accessed it in the .xml as suggested by kenmistry:
android:value="${API_KEY}"
assuming that you have defined the key in your .env file, you can set that up on build.gradle as manifestPlaceholders.
android {
defaultConfig {
manifestPlaceholders = [API_KEY: "$process.env.your_key"]
}
...
}
on your AndroidManifest.xml,
android:value="${API_KEY}"
Is there way to conditionally include meta-data element in Android manifest file based on value set in Gradle. I am able to do following (using resValue to set <some_value> in build.gradle) but haven't been able to find way to include/exclude complete meta-data element.
<meta-data
android:name="<some_setting>"
android:value="#string/<some_value>" />
Somewhat of a hack but only way I've been able to address this is by having something like following (have changed names in example) in build.gradle (am using this in context of having different build flavors as well but isn't dependent on that)...I think there's probably some additional smarts that can be done around manifest merging that would make this a bit cleaner as well. myFlag is coming from a project.hasProperty() value I'm passing in as part of Fastlane script
sourceSets {
flavor1 {
if (myFlag == "true") {
manifest.srcFile "src/flavor1_flag/AndroidManifest.xml"
} else {
manifest.srcFile "src/flavor1/AndroidManifest.xml"
}
}
}
<meta-data android:name="string"
android:resource="resource specification"
android:value="string" />
I'm working on an Android application with Gradle as its build system.
My objective is to use a value (a package name) as an applicationId:
productFlavors {
orange {
applicationId "com.fruits.android.orange"
// ...
But also to expose it via BuildConfig so that Java code has access to it.
This access has to be from outside the flavor (namely, free version of the app needs to know the package name of the paid version so that it can prompt user for an upgrade in Play store).
So I'd like to do something like that:
productFlavors {
orange {
applicationId orangeProPackage
// ...
buildConfigField 'String', 'ORANGE_PRO_PACKAGE', "$orangeProPackage" // ?
Only I'm not sure how to define orangeProPackage so that it's visible in the entire build.gradle and doesn't break the script.
Since there's a few different flavors, it would be best if I could somehow group all these constants like that (I guess?):
def proPackages = [
orange: "..."
apple: "..."
banana: "..."
]
and then refer to them in a clean and descriptive manner like proPackages.orange etc.
The question is, how to accomplish that?
This is not a duplicate of Is it possible to declare a variable in Gradle usable in Java?
I've seen that question (and a few others). I know how to declare buildConfigFields, I already have plenty. My question is about reusing the same value as a buildConfigField and applicationId.
Only I'm not sure how to define orangeProPackage so that it's visible in the entire build.gradle and doesn't break the script.
You could put it in gradle.properties in your project root. Like other .properties files, it's just a key-value store:
ORANGE_PRO_PACKAGE=com.morawski.awesomeapp
You then refer to it as a simple global string variable (ORANGE_PRO_PACKAGE) in your build.gradle:
buildConfigField 'String', 'ORANGE_PRO_PACKAGE', '"' + ORANGE_PRO_PACKAGE + '"'
it would be best if I could somehow group all these constants
Anything involving .properties files won't handle that. There, you may be looking at defining globals in the top-level build.gradle file just in plain Groovy code or something.
you could use extentions like this:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.6.10'
ext.app_version_code = 1010
ext.app_version_name = '1.0.1.0'
}
When I am trying to build project with value of meta-data tag as a string reference, crashlytics fail with following error:
Crashlytics found an invalid API key: #string/crashlytics.
Check the Crashlytics plugin to make sure that the application has been added successfully!
Contact support#crashlytics.com for assistance.
Doesn't work
<meta-data
android:name="com.crashlytics.ApiKey"
android:value="#string/crashlytics"/>
Works
<meta-data
android:name="com.crashlytics.ApiKey"
android:value="1234567890..."/>
I am want to define different keys inside string.xml for different productFlavors of my android project.
Update
After writing to crashlytics support:
Currently we only are able to evaluate the AndroidManifest.xml at build time so we don't look at any strings resources so we only support a hard coded string. I'll definitely share this with the team that you're interested so we can look into supporting this in a future release.
Edit: The solution accepted is working only if you are using an old version of Crashlytics (I was using v1.1.11). If you are using Fabric SDK you will notice
the tasks of the plugin have changed considerably and the script below
will not work. Also the API secret is not needed anymore, therefore
you can just use the <meta> in the manifest to specify the API key along with a manifest placeholder defined in your flavor:
in build.gradle:
flavor1 {
...
manifestPlaceholders = [crashlyticsApiKey: CRASHLYTICS_API_SECRET_HERE]
...
}
in AndroidManifest.xml:
...
<meta-data
android:name="com.crashlytics.ApiKey"
android:value="${crashlyticsApiKey}" />
...
There is another undocumented way to specify the Crashlytics key as noted here, and it is to use the crashlytics.properties (in the root of your project) to specify that value along with the API secret:
apiKey=YOUR_API_KEY
apiSecret=YOUR_API_SECRET
Unfortuntately this will not allow you to simply specify a different crashlytics.properties for each flavor, because it needs to be in the root of your project in order to be picked correctly by the gradle plugin. That means you need to generate that file dynamically.
The idea is to add the key/secret values in your flavor as custom properties, and generate the crashlytics.properties at buildtime, using the values from the current flavor to fill the file.
The build.gradle inside your android module should look like this:
...
productFlavors {
flavor1 {
...
set("crashlyticsApiKey", CRASHLYTICS_API_KEY_HERE)
set("crashlyticsApiSecret", CRASHLYTICS_API_SECRET_HERE)
...
}
...
}
File crashlyticsProperties = new File("${project.projectDir.absolutePath}/crashlytics.properties")
applicationVariants.all { variant ->
variant.productFlavors.each { flavor ->
def variantSuffix = variant.name.capitalize()
def generateResourcesTask = project.tasks.getByName("crashlyticsGenerateResources${variantSuffix}")
def generatePropertiesTask = task("crashlyticsGenerateProperties${variantSuffix}") << {
Properties properties = new Properties()
println "...copying apiSecret for ${variant.name}"
properties.put("apiSecret", flavor.crashlyticsApiSecret)
println "...copying apiKey for ${variant.name}"
properties.put("apiKey", flavor.crashlyticsApiKey)
properties.store(new FileWriter(crashlyticsProperties), "")
}
generateResourcesTask.dependsOn generatePropertiesTask
def cleanResourcesTask = project.tasks.getByName("crashlyticsCleanupResourcesAfterUpload${variantSuffix}")
cleanResourcesTask.doLast {
println "...removing crashlytics.properties"
crashlyticsProperties.delete()
}
}
}
...
Basically the script hooks in the building process and generates/fills the properties file just before the Crashlytics gradle plugin does its magic.
With Fabric's Crashlytics 2.6.6. I'm able to simply select a Build Variant from the menu (usually located on the left in Android Studio) and run the app. It takes a minute or so to propagate through to the Fabric dashboard, but I didn't have any need for a workaround.
I'm currently working on an Android project, and learning how to use git.
I'm blocked because of a problem with git : I have my Google Maps api key declared in my android-manifest file :
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="HEREISMYKEY"/>
Now, I'd like to push my code in github, but I can't push my AndroidManifest, because it contains my api key (which is supposed to remain secret).
I'd like to know if there is a way to modify it before every push, or maybe modify it each time I compile my application?
Thank you for your help !
Create a new *.xml file in your res/values (call it api-keys.xml or something similar).
Change your manifest to point to this string:
<meta-data
android:name="com.google.android.maps.v2.API_KEY"
android:value="#string/GoogleMapsKey"/>
When you first push your changes to the public, put in a dummy key.
Then edit the file with your real API key.
Then add the file to your .gitignore file so it doesn't get pushed again.
I suggest to use Gradle to dynamically load the API key for different build types and load a dummy key if none is found. This has the advantage that also a CI can build your application without the need of a real API key, but every new developer in your team has to set up the API key.
create a properties file google-maps-api.properties in your root gradle folder. Ignore the file by adding this line gradle/google-maps-api.properties to your .gitignore and put in the following 2 properties:
googleMapsApiKeyDebug=[YOUR_DEBUG_GOOGLE_MAPS_API_KEY_HERE]
googleMapsApiKeyRelease=[YOUR_RELEASE_GOOGLE_MAPS_API_KEY_HERE]
Next create the following google-maps-api-config.gradle file in your root gradle folder. This Gradle file will load the property file you created in step 1.
ext {
googleMapsApiConfig = new GoogleMapsApiConfigLogic(project)
}
/*
Before you run your application, you need a Google Maps API key.
Using these values:
Package name:
at.ict4d.covid19map
SHA-1 certificate fingerprint:
DE:4A:F0:B1:F5:F4:00:88:B6:E0:03:3E:31:1F:11:88:3F:43:0D:8B
Follow the directions here:
https://developers.google.com/maps/documentation/android/start#get-key
Once you have your key (it starts with "AIza") create a file with the name "google-maps-api.properties" in the gradle folder:
gradle/google-maps-api.properties
Put in 2 properties:
googleMapsApiKeyDebug:[THE KEY YOU JUST CREATED]
googleMapsApiKeyRelease:[YOUR RELEASE GOOGLE MAPS KEY] (if you are just developing and not planning to publish then fill in "YOUR_RELEASE_KEY_HERE")
"gradle/google-maps-api.properties" example:
googleMapsApiKeyDebug=AIzaFJKLJKsdLFDDsjlkdfDFJKLdjkf
googleMapsApiKeyRelease=YOUR_RELEASE_KEY_HERE
Sync your project and build.
*/
class GoogleMapsApiConfigLogic {
public final keyGoogleMapsApiKeyDebug = "googleMapsApiKeyDebug"
public final keyGoogleMapsApiKeyRelease = "googleMapsApiKeyRelease"
private final configGoogleMapsApiPropsFileName = "gradle/google-maps-api.properties"
public final props
private final project
GoogleMapsApiConfigLogic(project) {
this.project = project
this.props = loadGoogleMapsApiProperties()
}
def loadGoogleMapsApiProperties() {
Properties propertiesObj
if ((propertiesObj = loadFromProperties("$project.rootDir/$configGoogleMapsApiPropsFileName")) != null) {
project.logger.quiet("google-maps-api-config: use local properties file for Google Maps API")
return propertiesObj
} else {
propertiesObj = new Properties()
propertiesObj.put(keyGoogleMapsApiKeyDebug, "YOUR_KEY_HERE")
propertiesObj.put(keyGoogleMapsApiKeyRelease, "YOUR_KEY_HERE")
}
project.logger.quiet("google-maps-api-config: no API key found for Google Maps")
return propertiesObj
}
def loadFromProperties(fileName) {
Properties props = new Properties()
File propFile = new File(fileName)
if (propFile.exists()) {
props.load(new FileInputStream(propFile))
if (props.get(keyGoogleMapsApiKeyDebug) != null &&
props.get(keyGoogleMapsApiKeyRelease) != null) {
project.logger.quiet("google-maps-api-config: use $fileName for Google Maps API")
return props
}
} else {
project.logger.quiet("google-maps-api-config: $propFile does not exist for Google Maps API")
}
return null
}
}
Add the the following to your app/build.gradle file:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
// ...
}
apply from: "$rootDir/gradle/google-maps-api-config.gradle" // handles Google Maps API key
android {
// ...
buildTypes {
release {
// other config
resValue "string", "google_maps_key", "\"${googleMapsApiConfig.props.getProperty(googleMapsApiConfig.keyGoogleMapsApiKeyRelease)}\""
}
debug {
// other config
resValue "string", "google_maps_key", "\"${googleMapsApiConfig.props.getProperty(googleMapsApiConfig.keyGoogleMapsApiKeyDebug)}\""
}
}
This will create a String Resource with the key google_maps_key and the value of your API key. You can delete any other API key references, e.g. Android Studio generate the following files: app/src/debug/res/values/google_maps_api.xml and app/src/release/res/values/google_maps_api.xml - you can delete them.
Add the Google Maps API Key to your manifest:
<application>
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="#string/google_maps_key" />
The way I have approached similar issues in the past is by using specific branches in my git repo for pubic pushes.
Say you have a local master branch with your keys in the manifest. When you are ready to push to github (or wherever else) you can make a new "release" branch with no history. You can see a bit more about that here: How to push new branch without history git branch --orphan release. Once you do that remove all private information, commit all files and push only that branch to github git push origin release.
The issue with this is you will not have a commit history, maybe someone else can come up with something better.
If you create Google Maps Activity from Android Studio as a template it will add needed plugin in your build.gradle file, and then you can put your API key in local.properties file and use it as variable in Manifest file as for example: "${API_KEY}"
For more information visit https://developers.google.com/maps/documentation/places/android-sdk/secrets-gradle-plugin