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?
Source: Tips4all
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:
ReplyDeleteBuilds 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
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:
ReplyDelete// 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);
}
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.
ReplyDeletepublic 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));
}
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.
ReplyDeletehttp://blog.cuttleworks.com/2011/02/android-dev-prod-builds/
I've ended up with the special file on SD card - if present, use debug key; missing - use release one. And it works.
ReplyDeleteEDIT: see new accepted answer, it works better
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:
ReplyDelete<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.
I don't know if this helps anyone but I have merged some of the other suggestions here to produce the following MapViewActivity.
ReplyDeleteIn 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 :
you don't need to write an ant target (good if you use eclipse)
the correct release key is always in map.xml (hopefully a debug key won't be checked in by mistake)
the release key is always used for a release build
multiple debug keys can be used
The disadvantages of this approach are :
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;
}
}