41

OBSOLETED: this old question refers to obsoleted Google Maps v1 API. When using v2 API, you can use multiple certificate fingerprints in one Google API Console entry. API Key is no longer stored in Manifest nor code.


Is it possible to automatically detect, which certificate was used for signing APK? I'd like to have both debug and release Maps certificates in application and pass valid one to MapView constructor.

With such setup I will not make mistake while releasing application - I'm using debug certificate on emulator and my device, then sign with release one before sending app to Market.

I was thinking about detecting my particular device or whether debugger is connected but it is not perfect. Maybe some file marking need for debug certificate? Is there any better way?

tomash
  • 12,742
  • 15
  • 64
  • 81
  • You might find the answer provided [here](http://stackoverflow.com/questions/1743683/distinguishing-development-mode-and-release-mode-environment-settings-on-android/3204444#3204444) suitable as well. – Viktor Brešan Jul 08 '10 at 14:13
  • For Map Api V2, Way how to use keys dynamically. check my answer http://stackoverflow.com/a/40462174/1348522 – Zumry Mohamed Dec 09 '16 at 04:31

11 Answers11

48

There is a new way to determine is it a debug build or release one in SDK Tools, Revision 17. An excerpt from new features overview:

Builds now generate a class called BuildConfig containing a DEBUG constant that is automatically set according to your build type. You can check the (BuildConfig.DEBUG) constant in your code to run debug-only functions.

So now you can simply write something like this:

if (BuildConfig.DEBUG)
{
    //Your debug code goes here
}
else
{
    //Your release code goes here
}

UPDATE: I've encountered bug in ADT: sometimes BuildConfig.DEBUG is true after exporting application package. Description is here: http://code.google.com/p/android/issues/detail?id=27940

Alexander Mironov
  • 3,095
  • 26
  • 28
  • Yes this looks like the right way now. I will be moving over once I get rev 17, just giving it a few days before downloading first. – Ifor Mar 24 '12 at 09:02
  • Changing accepted answer as SDK rev 17 is available, in some cases other high-voted answers can be still valuable. – tomash Mar 25 '12 at 19:39
  • Just wanted to replace the previous debug detection code with this one, but the mentioned bug still as of now seems to exist. – Bachi Feb 26 '13 at 11:28
  • [Issue 27940](https://code.google.com/p/android/issues/detail?id=27940) was **closed** in March 2012. – JJD Nov 07 '13 at 12:58
27

Had the same hassle with the API key. Here's a full solution, based on the above link and example from Bijarni (which somehow didn't work for me), I use now this method:

// Define the debug signature hash (Android default debug cert). Code from sigs[i].hashCode()
protected final static int DEBUG_SIGNATURE_HASH = <your hash value>;

// Checks if this apk was built using the debug certificate
// Used e.g. for Google Maps API key determination (from: http://whereblogger.klaki.net/2009/10/choosing-android-maps-api-key-at-run.html)
public static Boolean isDebugBuild(Context context) {
    if (_isDebugBuild == null) {
        try {
            _isDebugBuild = false;
            Signature [] sigs = context.getPackageManager().getPackageInfo(context.getPackageName(), PackageManager.GET_SIGNATURES).signatures;
            for (int i = 0; i < sigs.length; i++) {
                if (sigs[i].hashCode() == DEBUG_SIGNATURE_HASH) {
                    Log.d(TAG, "This is a debug build!");
                    _isDebugBuild = true;
                    break;
                }
            }
        } catch (NameNotFoundException e) {
            e.printStackTrace();
        }      
    }
    return _isDebugBuild;
}

You have to find out your debug signature's hashValue() once, just output sigs[i].hashCode().

Then, I didn't want to dynamically add the MapView, but rather use the xml file. You cannot set the api key attribute in the code and use an xml layout, so I use this simple method (though copying the xml layout isn't so beautiful):

In my MapActivity:

    public void onCreate(Bundle savedInstanceState)
    {       
    super.onCreate(savedInstanceState);

    // Select the proper xml layout file which includes the matching Google API Key
    if (isDebugBuild(this)) {
        setContentView(R.layout.map_activity_debug);
    } else {
        setContentView(R.layout.map_activity_release);
    }
Bachi
  • 6,408
  • 4
  • 32
  • 30
  • 4
    One remark - I don't like your two layouts as it requires to do any changes simultaneously in two files, instead I'm creating MapView via `new MapView(this, isDebug?keyDebug:keyRelease );` and adding it to the current layout. – tomash Sep 30 '10 at 10:49
  • 1
    Yes, I had it dynamically created at first too, but since my map activity layout won't change and I wanted all layouts in one place (xml definition) and no mix-up, I'chosen the above. Both works fine of course. – Bachi Oct 03 '10 at 04:16
  • 4
    You could always have 3 layout files and the 2 you switch on just have two 's to your actual implementation above and below the MapView. – Blundell Oct 24 '11 at 09:14
10

Much easier way to determine whether it is a debug build is by checking the debug flag on the application info than the signature hash.

public boolean isDebugBuild() throws Exception
{
   PackageManager pm = _context.getPackageManager();
   PackageInfo pi = pm.getPackageInfo(_context.getPackageName(), 0);

   return ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
}

Once debug build is found, either you can use different resource for showing map or create the mapview within the app and add to a layout.

    if(isDebugBuild())
    {
        _mapView = new MapView(this, getString(R.string.debugmapskey));
    }
    else
    {
        _mapView = new MapView(this, getString(R.string.releasemapskey));
    }
Suriya
  • 176
  • 1
  • 5
  • This is a good solution if you always set android:debuggable=true while developing and android:debuggable=false if not. I however found that I turn off debuggable only at the very end of the development and testing, but will often send signed .apks to the client before. Hence the signature key works better for me in that case. – Bachi Aug 15 '11 at 12:05
  • 3
    Hi Bachi, From SDK Tools revision 8, http://developer.android.com/sdk/tools-notes.html (General notes) you dont need to add debuggable flag in the AndroidManifest, the tools automatically sets them based on release or development. – Suriya Sep 01 '11 at 07:32
  • I think revision 16 stops doing this... I encountered problems with this in my latest app, and had to change my approach to this: http://stackoverflow.com/questions/8480763/is-there-a-way-to-determine-whether-an-android-application-is-signed-for-product – TacB0sS Jan 17 '12 at 17:45
5

All answers here seem outdated, if you are using android studio then gradle is the way to go

Use different keys in your 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"/>

source

And if you want to save some passwords for debug and release differently then you should follow this

Community
  • 1
  • 1
penduDev
  • 4,743
  • 35
  • 37
3

I think that creating an entry in the Google API's console which includes both your release key and your debug key (both mapping to the same package) works great and is a much simpler way to not have to worry about whether you are debuging or compiling a release version. The solution is outlined here

Community
  • 1
  • 1
guibar
  • 435
  • 1
  • 5
  • 12
  • Adding multiple sha1 keys works perfectly fine for Maps API 2 - all solutions mentioned here are for the previous API 1 where there was no comfortable solution. – Bachi Dec 23 '13 at 14:59
3

I have worked around the horrendous mis-integration of the api keys into the build process and source control by making it a property stored in local.properties. I had to add the following to build.xml:

<property name="mapviewxml" value="res/layout/mapview.xml" />
<target name="-pre-build">
    <fail unless="mapsApiKey">You need to add mapsApiKey=... to local.properties</fail>
    <copy file="mapview.xml.tpl" tofile="${mapviewxml}" overwrite="true">
        <filterchain>
            <replacetokens>
                <token key="apiKey" value="${mapsApiKey}"/>
            </replacetokens>
        </filterchain>

    </copy>
</target>

Now, of course I had to create mapview.xml.tpl in my projects root (it can't go to res/layout because it will break the build process):

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.maps.MapView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true"
    android:apiKey="@apiKey@"
    />

During pre-compilation, the template is copied to the right place and @apiKey@ is replaced with the real key. Unfortunately I have not found a way to distinguish between debug and release builds in this phase, so to compile for release, I just add the release apiKey to the ant parameters:

ant -DmapsApiKey=.... release 

This approach integrates well with SCM (I do not need to check in the keys) and acceptably with the build process.

ge0rg
  • 1,816
  • 1
  • 26
  • 38
3

If you're still interested I just blogged about another way to do this. With a simple change to the Android build script, you can switch the Map API key as well as all other required release changes. What I like about this is that nothing debug-related goes into the release, and you can keep the XML layouts just the way they were before.

http://blog.cuttleworks.com/2011/02/android-dev-prod-builds/

Romain
  • 2,318
  • 1
  • 23
  • 31
1

I've ended up with the special file on SD card - if present, use debug key; missing - use release one. And it works.

EDIT: see new accepted answer, it works better

tomash
  • 12,742
  • 15
  • 64
  • 81
1

I don't know if this helps anyone but I have merged some of the other suggestions here to produce the following MapViewActivity.

In this example R.layout.map_dbg is only used if this is a debug build and the file exists (add this file to your .gitignore).

The advantages of this approach are :

  1. you don't need to write an ant target (good if you use eclipse)
  2. the correct release key is always in map.xml (hopefully a debug key won't be checked in by mistake)
  3. the release key is always used for a release build
  4. multiple debug keys can be used

The disadvantages of this approach are :

  1. you need to remember to update map_dbg.xml every time map.xml is updated

    public class MapViewActivity extends MapActivity {
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            //
            // copy the map.xml to map_dbg.xml and update the api key. 
            //
            int id = getLayoutId("map_dbg");
            if(id ==0)
                id = R.layout.map;
    
            setContentView(id);
        }
    
        int getLayoutId(String name) {
            return isDebugBuild() ? getResources().getIdentifier(name, "layout", getPackageName()) : 0;
        }
    
        public boolean isDebugBuild() 
        {
            boolean dbg = false;
            try {
                PackageManager pm = getPackageManager();
                PackageInfo pi = pm.getPackageInfo(getPackageName(), 0);
    
                dbg = ((pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0);
            } catch (Exception e) {
            }
            return dbg;
        }
    
    }
    
jimoleary
  • 1,645
  • 13
  • 11
0

I have setup a simple ant target that replaces the apikey with either a debug key or a release key. This is really simple and keeps the code free of unwanted logic.

<target name="apikey">
    <!-- Location of target layout file -->
    <first id="first">
        <fileset dir="." includes="res/layout/kondi_training_templates.xml" />
    </first>
    <property name="layout-file" value="${toString:first}"/>
    <echo>template-file: ${template-file}</echo>

    <replaceregexp file="${template-file}"
        match="android:apiKey=.*"
        replace='android:apiKey="${mapview.apikey}"'
        byline="true"
    />
</target>
slott
  • 3,266
  • 1
  • 35
  • 30
0

In Map V2 Its easy to send seperate keys using Android Studio Gradle tool. I have implemented an easy way for that. please check the link here.

Community
  • 1
  • 1
Zumry Mohamed
  • 9,318
  • 5
  • 46
  • 51