Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-native and the referrer at install

You can add a referrer to the play store link.

https://play.google.com/store/apps/details?id=com.myapp&referrer=foobar

How do I receive this referrer in a react-native app at the first start of the app?

like image 843
Jonny Avatar asked Feb 09 '17 16:02

Jonny


People also ask

What is install referrer?

An Install Referrer is an Android-specific ad tracking identifier. Like Device IDs and Device Fingerprinting, an install referrer is a unique string that's sent to the Play Store when a user clicks on an ad.

What is play install referrer library?

The Play Install Referrer API Client Library is written in the Java programming language and is a wrapper for the Android Interface Definition Language (AIDL) file that defines the interface to the Install Referrer service. You can use the Play Install Referrer API Client Library to simplify your development process.

How does Google install referrer work?

The Google Install Referrer is an Android-specific measurement technology that attributes clicks on Google Play Store app pages to the correlating app download. Google's Install Referrer framework sends an install referrer (or unique code string) to the Google Play store when an ad click has occurred.

What is permission play install referrer API?

The Play Install Referrer API is an AIDL Service Interface primarily used by non-Java programmers. Note: The Play Install Referrer Library provides a wrapper around the Play Install Referrer API and is designed to help Java programmers use the API.


1 Answers

Edit: details of the main problem

To get the install referrer is very easy and there is only one way: to receive the intent after the install. At the time of this event you have a problem: how to get the value from native Java to reacts JavaScript? If you were lucky the ui is open and you could send a silent notification or another intent which is captured by already existing packages. But the intent is normaly a lone wolf with no ui running at the same time.

So you have to write this value in a storage which JavaScript can read too. Hmm, the easy way is an online store; send a request with the value and a device identify and query with the same identify at the start of the ui. But is there a offline way?

Yes, but a "heavy code one". Java and JavaScript has no storage system in common, so I shifted the problem to an only Java problem. The fast solution: Find a react-native package which uses a native storage system and clone the native code into the receiver for writing the value. This is the solution of my comment at the question above. It's easy and needs only few lines of code.

But it added an unpredictable dependency. The app grow to a point where I need an android service and other native functions, so I wrote a react-native module on my own. To store options of the service I used a very simple key-value store, which I also used to save the install referrer. So I wrote the referrer in the intent receiver to this storage and JavaScript queried this value over the native bride of react native.

Perhaps somebody will make a package out of this. Or there is a more simpler solution; after all I'm only an android beginner.


The solution with an own RN module

I post my short example of my solution, because @jeevana ask for it. In my comment at the question is a more react-native-like answer. This now is the solution I used in the original app, but only the essentials. My "FooModule" has much more code, so I tried to extract the specific code.

The JavaScript part is very easy, App.js:

const FooModule = require('react-native').NativeModules.FooModule;

import React, {
    Component,
} from 'react';

export default class FooApp extends Component {
    componentDidMount () {
        FooModule.getInstallReferrer().then(value => {
            FooModule.showShortToast("INSTALL_REFERRER JS: " + String(value));
            if (value) {
                FooModule.clearInstallReferrer();
            }
        });

    }
    render() {
        return null;
    }
}

Here all the Java parts. MainApplication.java:

package tld.example.fooapp;

import android.app.Application;
import android.util.Log;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          new FooPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}

FooPackage.java:

package tld.example.fooapp;

import com.facebook.react.bridge.ReactApplicationContext;

import java.util.*;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.uimanager.ViewManager;

public class FooPackage implements ReactPackage {
    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new FooModule(reactContext));
        return modules;
    }

    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

Now the receiver for the system intent, at FooReceiver.java:

package tld.example.fooapp;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

import static android.R.attr.data;

public class FooReceiver extends BroadcastReceiver {
    private static final String TAG = "FooReceiver";
    public static String REFERRER = "";

    public FooReceiver() {

    }

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals("com.android.vending.INSTALL_REFERRER")) {
            String referrer = intent.getStringExtra("referrer");
            Log.d(TAG, "INSTALL_REFERRER: " + referrer);
            new FooTrayPreferences(context).put(FooTrayPreferences.INSTALL_REFERRER, referrer);
        }
    }
}

The "storage class" for the native part for writing and reading at the same storage at the receiver and the react-native bride, FooTrayPreferences.java:

package tld.example.fooapp;

import android.content.Context;
import net.grandcentrix.tray.TrayPreferences;

public class FooTrayPreferences extends TrayPreferences {

    public static String INSTALL_REFERRER = "INSTALL_REFERRER";

    public FooTrayPreferences(final Context context) {
        super(context, "FooContent", 1);
    }
}

The last file is the bride between native Java and ReactNative, FooModule.java:

package tld.example.fooapp;

import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;

import android.content.Intent;
import android.content.Context;
import android.widget.Toast;

import java.util.Arrays;
import java.util.Set;
import android.util.Log;


public class FooModule extends ReactContextBaseJavaModule {
    private static final String TAG = "FooModule";
    ReactApplicationContext reactContext;
    private static String prefFile = "serviceSettings";
    private FooTrayPreferences mTrayPreferences;

    public FooModule(ReactApplicationContext reactContext) {
        super(reactContext);
        this.reactContext = reactContext;
        mTrayPreferences = new FooTrayPreferences(reactContext);
    }

    @Override
    public String getName() {
        return "FooModule";
    }

    @ReactMethod
    public void getInstallReferrer(Promise promise) {
        String installReferrer = mTrayPreferences.getString(FooTrayPreferences.INSTALL_REFERRER, "");
        promise.resolve(installReferrer);
    }

    @ReactMethod
    public void clearInstallReferrer() {
        mTrayPreferences.remove(FooTrayPreferences.INSTALL_REFERRER);

    }

    @ReactMethod
    public void showShortToast(String text) {
        Toast.makeText(reactContext, text, Toast.LENGTH_SHORT).show();
    }

    @ReactMethod
    public void showLongToast(String text) {
        Toast.makeText(reactContext, text, Toast.LENGTH_LONG).show();
    }
}

The AndroidManifest.xml also needs the receiver entry:

...
        <receiver
            android:name=".FooReceiver"
            android:enabled="true"
            android:process=":remotereceiver"
            android:exported="true">
            <intent-filter>
                <action android:name="com.android.vending.INSTALL_REFERRER" />
            </intent-filter>
        </receiver>
...

So it works, but it's mostly native code. On very very slow devices it happens that the ui of the app starts faster than the referrer intent get fired. After closing the app and reopen it, the install referrer is accessable.

like image 139
Jonny Avatar answered Oct 11 '22 04:10

Jonny