3

On StackOverflow, I noticed a number of people having trouble catching INSTALL_REFERRER over the years, and a number of bug reports on the Android forum (all closed due to "wrong forum" -- but, I'm not seeing any public issue tracker). I'm wondering if anyone has a way to get INSTALL_REFERRER to work in the current 2014 version of Google Play.

Edit: I did find an issue tracker and created an issue: https://code.google.com/p/play-games-platform/issues/detail?id=202

Edit 2: (replace com.myapp with the app's name)

Here's what I did for a test of the response. This is the same as on Google's Analytics help docs. This works. adb shell am broadcast -a com.android.vending.INSTALL_REFERRER -n com.myapp/com.flyingsoftgames.googleplayquery.QueryReceiver --es "referrer" "utm_source=testSource&utm_medium=testMedium&utm_term=testTerm&utm_content=testContent&utm_campaign=testCampaign"``

Here's what I did for a test of the response from Google's Play Store. This is also the same as Google's Analytics help docs, from the link generation utility. (Yes, the keys are different, but that's per the docs, and not what I'm testing -- I just want ANY response, and the docs indicate that referrer should work...) I installed from this rank and ran, listening for QueryReceiver with logcat (adb logcat -s QueryReceiver). This doesn't work.

https://play.google.com/store/apps/details?id=com.myapp&referrer=utm_source%3Dgoogle%26utm_medium%3Dcpc%26utm_term%3Dpodcast%252Bapps%26utm_content%3DdisplayAd1%26utm_campaign%3Dpodcast%252Bgeneralkeywords

Here is my (Cordova plugin) code, which works perfectly when triggering a manual broadcast:

AndroidManifest.xml:

<receiver android:exported="true" android:name="com.flyingsoftgames.googleplayquery.QueryReceiver">
 <intent-filter>
  <action android:name="com.android.vending.INSTALL_REFERRER" />
 </intent-filter>
</receiver>

GooglePlayQuery.java: package com.flyingsoftgames.googleplayquery;

import com.flyingsoftgames.googleplayquery.QueryReceiver;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaPlugin;

import android.content.Intent;
import android.content.IntentFilter;
import android.content.Context;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;

import org.json.JSONArray;
import org.json.JSONException;

public class GooglePlayQuery extends CordovaPlugin {
 public static CallbackContext queryCallback = null;
 public static CordovaInterface cordova = null;

 @Override public void initialize (CordovaInterface initCordova, CordovaWebView webView) {
  // Create a static cordova reference so that QueryReceiver can access it.
  cordova = initCordova;

  // Enable the broadcast receiver in case it isn't enabled.
  Activity activity = cordova.getActivity ();
  ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
  PackageManager pm = activity.getPackageManager ();
  pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
  super.initialize (cordova, webView);
 }

 public boolean execute (String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
  if ("getURI".equals(action)) {this.queryCallback = callbackContext;}
  return true;
 }
}


QueryReceiver.java:

package com.flyingsoftgames.googleplayquery;

import com.flyingsoftgames.googleplayquery.GooglePlayQuery;
import org.apache.cordova.PluginResult;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;

import android.util.Log;

public class QueryReceiver extends BroadcastReceiver {
 @Override public void onReceive (Context context, Intent intent) {
  if (GooglePlayQuery.queryCallback != null) {
   Log.d ("QueryReceiver", intent.toURI());
   GooglePlayQuery.queryCallback.sendPluginResult (new PluginResult (PluginResult.Status.OK, intent.toURI()));
  }

  // Now disable the broadcast receiver since we don't need it anymore.
  Activity activity = GooglePlayQuery.cordova.getActivity ();
  ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
  PackageManager pm = activity.getPackageManager ();
  pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
 }
}
Agamemnus
  • 1,279
  • 2
  • 16
  • 38
  • what kind of link are you using to add a referer ? I'm retrieving this data with : 'intent.getStringExtra("referrer")' – Hacketo Dec 12 '14 at 15:17
  • I tried all. Using ``Log.d ("QueryReceiver", intent.toURI());`` .. nothing. What API version are you using? – Agamemnus Dec 12 '14 at 15:41
  • something like this : http://stackoverflow.com/questions/4093150/get-referrer-after-installing-app-from-android-market – Hacketo Dec 12 '14 at 15:54
  • I don't understand. I had read through all of that earlier, not sure which part in that link you are uh, referring, to. – Agamemnus Dec 12 '14 at 15:56
  • I was referring on how you were retrieving the "referrer" data. I don't know what you mean by which version of Api I'm using, the implementation to get a "referrer" in android app should be the same in any android app. The INSTALL_REFERRER thing is not related with play-games, it is an intent that Google Play is sending when a user has installed the app (can see com.android.vending.INSTALL_REFERRER). The intent may also not be sent by GooglePlay if you start the app just at the end of the install. Can you add the "manual broadcast" intent you sent, to your question ? – Hacketo Dec 12 '14 at 17:04
  • I meant the Android SDK version number that you use to build your app/game. Let me add the broadcast test that I did manually, hold on. (which DOES work) – Agamemnus Dec 12 '14 at 19:58
  • Ok, I updated the question. Have a look! – Agamemnus Dec 12 '14 at 20:19
  • I always use the latest android skd to compile. Is your test intent working if the app is signed ? (could be the path of your receiver in your manifest) – Hacketo Dec 12 '14 at 23:01
  • Yes. I am working with a signed release version to test a manual broadcast. Can you give me an template of a url that will generate a broadcast? Maybe the problem is something really simple... maybe it doesn't like a receiver that is not the same path as the app itself? (``/com.flyingsoftgames.googleplayquery.QueryReceiver`` vs. just ``/.QueryReceiver``) – Agamemnus Dec 12 '14 at 23:43
  • You can remove the -n param (path of your receiver) on your test intent to check that the receiver is found by the broadcast. Could be this, yes. – Hacketo Dec 13 '14 at 10:13
  • Without the "-n com.myapp/com.flyingsoftgames.googleplayquery.QueryReceiver", the QueryReceiver function still runs and has that S.referrer value. – Agamemnus Dec 14 '14 at 22:23

1 Answers1

0

I believe I finally found the reason that it does not work. There is a silent error where GooglePlayQuery.cordova is null. QueryReceiver.onReceive runs before GooglePlayQuery.initialize, but only in production mode. Insane.

I still need a few hours to test it (as my game is published) to verify, but here's my new code. It's also available at https://github.com/agamemnus/cordova-plugin-google-play-query-receiver.

AndroidManifest.xml:

<receiver android:exported="true" android:name="com.flyingsoftgames.googleplayquery.QueryReceiver">
 <intent-filter>
  <action android:name="com.android.vending.INSTALL_REFERRER" />
 </intent-filter>
</receiver>


GooglePlayQuery.java:

package com.flyingsoftgames.googleplayquery;

import com.flyingsoftgames.googleplayquery.QueryReceiver;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaInterface;
import org.apache.cordova.CordovaWebView;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.PluginResult;

import android.content.Intent;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;

import org.json.JSONArray;
import org.json.JSONException;

public class GooglePlayQuery extends CordovaPlugin {
 public static CallbackContext queryCallback = null;
 public static CordovaInterface cordova = null;
 public static String referrer_uri = "";

 public static Intent QueryReceiverCachedIntent = null;

 @Override public void initialize (CordovaInterface initCordova, CordovaWebView webView) {
  // Create a static cordova reference so that QueryReceiver can access it.
  cordova = initCordova;

  // Enable the broadcast receiver in case it isn't enabled.
  Activity activity = cordova.getActivity ();
  ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
  PackageManager pm = activity.getPackageManager ();
  pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

  // If the QueryReceiver's onReceive already ran, run the cached data.
  if (QueryReceiver.cachedIntent != null) {QueryReceiver.runCachedOnReceive (QueryReceiver.cachedIntent);}

  super.initialize (cordova, webView);
 }

 public boolean execute (String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException {
  if ("getURI".equals(action)) {
   if (referrer_uri != "") {
    callbackContext.sendPluginResult (new PluginResult (PluginResult.Status.OK, referrer_uri));
    referrer_uri = "";
    return true;
   }
   this.queryCallback = callbackContext;
  }
  return true;
 }
}


QueryReceiver.java:

package com.flyingsoftgames.googleplayquery;

import com.flyingsoftgames.googleplayquery.GooglePlayQuery;
import org.apache.cordova.PluginResult;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;

import android.util.Log;

public class QueryReceiver extends BroadcastReceiver {

 public static Intent cachedIntent = null;

 @Override public void onReceive (Context context, Intent intent) {
  // If the onReceive occurred before the GooglePlayQuery initialize function ran: cache the intent. Otherwise, run as intended.
  if (GooglePlayQuery.cordova == null) {cachedIntent = intent;} else {runCachedOnReceive (intent);}
 }

 public static void runCachedOnReceive (Intent intent) {
  if (cachedIntent != null) cachedIntent = null;
  Log.e ("QueryReceiver", intent.toURI());
  if (GooglePlayQuery.queryCallback != null) {
   GooglePlayQuery.queryCallback.sendPluginResult (new PluginResult (PluginResult.Status.OK, intent.toURI()));
  } else {
   GooglePlayQuery.referrer_uri = intent.toURI();
  }

  // Now disable the broadcast receiver since we don't need it anymore.
  Activity activity = GooglePlayQuery.cordova.getActivity ();
  ComponentName receiver = new ComponentName (activity, QueryReceiver.class);
  PackageManager pm = activity.getPackageManager ();
  pm.setComponentEnabledSetting (receiver, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
 }
}
Agamemnus
  • 1,279
  • 2
  • 16
  • 38