Let's say I have an Android App A that is installed in the user's device and I have an AppWidget with my App where we let other Android developers publish their App promotion ads on a Cost Per Install basis. So we need to track if App B is being installed on the user's device through App A's referral.
Let's also assume Android App B has its advertisement running on Android App A's widget and the link through which we redirect App A's users to App B's user to Google Play is with all referrer data. The URL looks like this (as recommended here -
If you hit the above link in your browser or make a QR code out of it and launch it it will take you to App B in Google Play with the required referral data. Now when App B is installed on the user's device and launched for the first time the Broadcast that App A expects to receive is com.android.vending.INSTALL_REFERRER
. If the broadcast is received and the utm_source is App A then record and process the transaction. This is all we aim to do.
Here is App A's AndroidManifest.xml code snippet.
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:debuggable="true" >
<activity
android:name=".LocateMeActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.locateme.android.ReferralReceiver" android:exported="true">
<intent-filter>
<action android:name="com.android.vending.INSTALL_REFERRER" />
</intent-filter>
</receiver>
</application>
Permissions added -
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
Here is the Broadcast Receiver class ReferralReceiver.java that is supposed to process the broadcast.
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
public class ReferralReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
// Workaround for Android security issue: http://code.google.com/p/android/issues/detail?id=16006
try
{
final Bundle extras = intent.getExtras();
if (extras != null) {
extras.containsKey(null);
}
}
catch (final Exception e) {
return;
}
Map<String, String> referralParams = new HashMap<String, String>();
// Return if this is not the right intent.
if (! intent.getAction().equals("com.android.vending.INSTALL_REFERRER")) { //$NON-NLS-1$
return;
}
String referrer = intent.getStringExtra("referrer"); //$NON-NLS-1$
if( referrer == null || referrer.length() == 0) {
return;
}
try
{ // Remove any url encoding
referrer = URLDecoder.decode(referrer, "x-www-form-urlencoded"); //$NON-NLS-1$
}
catch (UnsupportedEncodingException e) { return; }
// Parse the query string, extracting the relevant data
String[] params = referrer.split("&"); // $NON-NLS-1$
for (String param : params)
{
String[] pair = param.split("="); // $NON-NLS-1$
referralParams.put(pair[0], pair[1]);
}
ReferralReceiver.storeReferralParams(context, referralParams);
}
private final static String[] EXPECTED_PARAMETERS = {
"utm_source",
"utm_medium",
"utm_term",
"utm_content",
"utm_campaign"
};
private final static String PREFS_FILE_NAME = "ReferralParamsFile";
public static void storeReferralParams(Context context, Map<String, String> params)
{
SharedPreferences storage = context.getSharedPreferences(ReferralReceiver.PREFS_FILE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = storage.edit();
for(String key : ReferralReceiver.EXPECTED_PARAMETERS)
{
String value = params.get(key);
if(value != null)
{
editor.putString(key, value);
}
}
editor.commit();
}
public static Map<String, String> retrieveReferralParams(Context context)
{
HashMap<String, String> params = new HashMap<String, String>();
SharedPreferences storage = context.getSharedPreferences(ReferralReceiver.PREFS_FILE_NAME, Context.MODE_PRIVATE);
for(String key : ReferralReceiver.EXPECTED_PARAMETERS)
{
String value = storage.getString(key, null);
if(value != null)
{
params.put(key, value);
}
}
return params;
}
}
The Debugging device is on Android OS 2.3.4
Through a simple TextView and Button in the main activity of the App I try to display the referral data captured by the receiver. Here is the call and display -
referraltext.setText(ReferralReceiver.retrieveReferralParams(this.getApplicationContext()).toString());
When I launch App B's referral link shown above, go to Google Play, install it and open it for the first time logcat doesn't show the broadcast com.android.vending.INSTALL_REFERRER at all and hence the referral data when displayed shows an empty string.
At the same time when I use adb command below the same broadcast receiver works like a charm and displays the referral data.
am broadcast -a com.android.vending.INSTALL_REFERRER -n com.locateme.android/.ReferralReceiver --es "referrer" "utm_source=tooyoou&utm_medium=banner&utm_term=foursquare&utm_content=foursquare-tooyoou&utm_campaign=foursquare-android"
So does that mean the Google Play does not broadcast the referrer data and the expected intent at all?
Or does it mean that it broadcasts the intent only for App B that is being installed?
Or am I doing something wrong?
Is this possible to achieve without App B having to insert our tracking SDK or code in their App just to advertise with us.
Thanks so much in advance for your help.
Additional Info - We are not using the Google Analytics SDK so we are using our custom Broadcast Receiver rather than Google's BR
The code above works like a charm. I just followed Lucas' advice below in the answer marked correct and replaced x-www-form-urlencoded to UTF-8. Why I didn't do this at first is because of what a mobile analytics company called Localytics posted here - http://www.localytics.com/docs/android-market-campaign-analytics/ Their code has the same issue. So basically what I have right now is that the above piece of code in an Android App that I published in Google Play and then downloaded the App from Google Play from my Android device running ICS, opened it and clicked on Retrieve Referral and it worked.Try it for free, here is the link - https://play.google.com/store/apps/details?id=com.locateme.android&referrer=utm_source%3Dtooyoou%26utm_medium%3Dbanner%26utm_term%3Dappdownload%26utm_content%3Dimage%26utm_campaign%3Dtooyooupromo
Another very important thing that I want to reiterate is that the google play campaign referral tracking this way will not work if you are installing an app remotely on your device using google play from the web. I have tested it and it is an open defect with Google.
the source code above is not correct.
the line
referrer = URLDecoder.decode(referrer, "x-www-form-urlencoded");
should be
referrer = URLDecoder.decode(referrer, "UTF-8");
the android documentation is not clear about this, but if you use x-www-form-urlencoded it will throw an unsupported encoding exception....