Replace Mixpanel with Firebase (Google) analytics (#535)

* replace mixpanel with firebase analytics
* add encrypted google-services.json file
This commit is contained in:
Akinwale Ariwodola 2019-05-02 07:06:42 +01:00 committed by GitHub
parent 73d91f7268
commit 7a7e96388b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 114 additions and 115 deletions

1
.gitignore vendored
View file

@ -12,3 +12,4 @@ src/main/assets/index.android.bundle.meta
lbry-android.keystore lbry-android.keystore
p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json
.gitsecret/keys/random_seed .gitsecret/keys/random_seed

View file

@ -1 +1,2 @@
lbry-android.keystore: lbry-android.keystore:
p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json

View file

@ -73,8 +73,8 @@ class FileDownloadButton extends React.PureComponent {
text={(isPlayable ? 'Play' : (isViewable ? 'View' : 'Download'))} text={(isPlayable ? 'Play' : (isViewable ? 'View' : 'Download'))}
onLayout={onButtonLayout} onLayout={onButtonLayout}
style={[style, fileDownloadButtonStyle.container]} onPress={() => { style={[style, fileDownloadButtonStyle.container]} onPress={() => {
if (NativeModules.Mixpanel) { if (NativeModules.Firebase) {
NativeModules.Mixpanel.track('Purchase Uri', { Uri: uri }); NativeModules.Firebase.track('purchase_uri', { uri: uri });
} }
purchaseUri(uri, onStartDownloadFailed); purchaseUri(uri, onStartDownloadFailed);
if (isPlayable && onPlay) { if (isPlayable && onPlay) {

View file

@ -36,8 +36,8 @@ class FileItem extends React.PureComponent {
navigateToFileUri = () => { navigateToFileUri = () => {
const { navigation, uri } = this.props; const { navigation, uri } = this.props;
const normalizedUri = normalizeURI(uri); const normalizedUri = normalizeURI(uri);
if (NativeModules.Mixpanel) { if (NativeModules.Firebase) {
NativeModules.Mixpanel.track('Discover Tap', { Uri: normalizeURI }); NativeModules.Firebase.track('explore_click', { uri: normalizedUri });
} }
navigateToUri(navigation, normalizedUri); navigateToUri(navigation, normalizedUri);
} }

View file

@ -5,8 +5,8 @@ import SearchInput from './view';
const perform = dispatch => ({ const perform = dispatch => ({
search: search => { search: search => {
if (NativeModules.Mixpanel) { if (NativeModules.Firebase) {
NativeModules.Mixpanel.track('Search', { Query: search }); NativeModules.Firebase.track('search', { query: search });
} }
return dispatch(doSearch(search)); return dispatch(doSearch(search));
}, },

View file

@ -43,8 +43,8 @@ import thunk from 'redux-thunk';
const globalExceptionHandler = (error, isFatal) => { const globalExceptionHandler = (error, isFatal) => {
if (error && NativeModules.Mixpanel) { if (error && NativeModules.Firebase) {
NativeModules.Mixpanel.logException(isFatal, error.message ? error.message : "No message", error); NativeModules.Firebase.logException(isFatal, error.message ? error.message : "No message", error);
} }
}; };
setJSExceptionHandler(globalExceptionHandler, true); setJSExceptionHandler(globalExceptionHandler, true);

View file

@ -34,9 +34,9 @@ class DiscoverPage extends React.PureComponent {
AsyncStorage.getItem('firstLaunchSuspended').then(suspended => { AsyncStorage.getItem('firstLaunchSuspended').then(suspended => {
AsyncStorage.removeItem('firstLaunchSuspended'); AsyncStorage.removeItem('firstLaunchSuspended');
const appSuspended = (suspended === 'true'); const appSuspended = (suspended === 'true');
if (NativeModules.Mixpanel) { if (NativeModules.Firebase) {
NativeModules.Mixpanel.track('First Run Time', { NativeModules.Firebase.track('first_run_time', {
'Total Seconds': delta, 'App Suspended': appSuspended 'total_seconds': delta, 'app_suspended': appSuspended
}); });
} }
}); });

View file

@ -88,8 +88,8 @@ class FilePage extends React.PureComponent {
this.fetchFileInfo(this.props); this.fetchFileInfo(this.props);
this.fetchCostInfo(this.props); this.fetchCostInfo(this.props);
if (NativeModules.Mixpanel) { if (NativeModules.Firebase) {
NativeModules.Mixpanel.track('Open File Page', { Uri: uri }); NativeModules.Firebase.track('open_file_page', { uri: uri });
} }
if (NativeModules.UtilityModule) { if (NativeModules.UtilityModule) {
NativeModules.UtilityModule.keepAwakeOn(); NativeModules.UtilityModule.keepAwakeOn();
@ -301,12 +301,12 @@ class FilePage extends React.PureComponent {
const { uri } = navigation.state.params; const { uri } = navigation.state.params;
this.logFileView(uri, fileInfo, timeToStartMillis); this.logFileView(uri, fileInfo, timeToStartMillis);
let payload = { 'Uri': uri }; let payload = { 'uri': uri };
if (!isNaN(timeToStart)) { if (!isNaN(timeToStart)) {
payload['Time to Start (seconds)'] = timeToStart; payload['time_to_start_seconds'] = timeToStart;
payload['Time to Start (ms)'] = timeToStartMillis; payload['time_to_start_ms'] = timeToStartMillis;
} }
NativeModules.Mixpanel.track('Play', payload); NativeModules.Firebase.track('play', payload);
} }
onPlaybackFinished = () => { onPlaybackFinished = () => {

View file

@ -254,8 +254,8 @@ class SplashScreen extends React.PureComponent {
} }
componentDidMount() { componentDidMount() {
if (NativeModules.Mixpanel) { if (NativeModules.Firebase) {
NativeModules.Mixpanel.track('App Launch', null); NativeModules.Firebase.track('app_launch', null);
} }
Linking.getInitialURL().then((url) => { Linking.getInitialURL().then((url) => {

View file

@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle # (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap) # bootstrap)
android.gradle_dependencies = com.android.support:support-v4:27.1.1, com.android.support:support-media-compat:27.1.1, com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.59.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828 android.gradle_dependencies = com.android.support:support-v4:27.1.1, com.android.support:support-media-compat:27.1.1, com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.59.3, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-core:16.0.1
# (str) python-for-android branch to use, defaults to master # (str) python-for-android branch to use, defaults to master
#p4a.branch = stable #p4a.branch = stable

View file

@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle # (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap) # bootstrap)
android.gradle_dependencies = com.android.support:support-v4:27.1.1, com.android.support:support-media-compat:27.1.1, com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.59.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828 android.gradle_dependencies = com.android.support:support-v4:27.1.1, com.android.support:support-media-compat:27.1.1, com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.59.3, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-core:16.0.1
# (str) python-for-android branch to use, defaults to master # (str) python-for-android branch to use, defaults to master
#p4a.branch = stable #p4a.branch = stable

View file

@ -148,7 +148,7 @@ android.react_src = ./app
# (list) Gradle dependencies to add (currently works only with sdl2_gradle # (list) Gradle dependencies to add (currently works only with sdl2_gradle
# bootstrap) # bootstrap)
android.gradle_dependencies = com.android.support:support-v4:27.1.1, com.android.support:support-media-compat:27.1.1, com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.59.3, com.mixpanel.android:mixpanel-android:5+, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828 android.gradle_dependencies = com.android.support:support-v4:27.1.1, com.android.support:support-media-compat:27.1.1, com.android.support:appcompat-v7:27.1.1, com.facebook.react:react-native:0.59.3, com.google.android.gms:play-services-gcm:11.0.4+, com.facebook.fresco:fresco:1.9.0, com.facebook.fresco:animated-gif:1.9.0, com.squareup.picasso:picasso:2.71828, com.google.firebase:firebase-core:16.0.1
# (str) python-for-android branch to use, defaults to master # (str) python-for-android branch to use, defaults to master
#p4a.branch = stable #p4a.branch = stable

Binary file not shown.

View file

@ -405,10 +405,10 @@ main.py that loads it.''')
android_api=android_api, android_api=android_api,
build_tools_version=build_tools_version) build_tools_version=build_tools_version)
render( render('settings.gradle', 'settings.gradle')
'settings.tmpl.gradle',
'settings.gradle' ## google-services.json for firebase
) render('google-services.json', 'google-services.json')
# copy icon drawables # copy icon drawables
for folder in ('drawable-hdpi', 'drawable-mdpi', 'drawable-xhdpi', 'drawable-xxhdpi', 'drawable-xxxhdpi'): for folder in ('drawable-hdpi', 'drawable-mdpi', 'drawable-xhdpi', 'drawable-xxhdpi', 'drawable-xxxhdpi'):

View file

@ -6,6 +6,7 @@ buildscript {
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.0.0' classpath 'com.android.tools.build:gradle:3.0.0'
classpath 'com.google.gms:google-services:4.0.1'
} }
} }
@ -112,3 +113,6 @@ dependencies {
{%- endfor %} {%- endfor %}
{%- endif %} {%- endif %}
} }
apply plugin: 'com.google.gms.google-services'
com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true

View file

@ -0,0 +1,80 @@
package io.lbry.browser.reactmodules;
import android.content.Context;
import android.os.Bundle;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.google.firebase.analytics.FirebaseAnalytics;
import io.lbry.browser.BuildConfig;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
import org.json.JSONException;
public class FirebaseModule extends ReactContextBaseJavaModule {
private Context context;
private FirebaseAnalytics firebaseAnalytics;
public FirebaseModule(ReactApplicationContext reactContext) {
super(reactContext);
this.context = reactContext;
this.firebaseAnalytics = FirebaseAnalytics.getInstance(context);
}
@Override
public String getName() {
return "Firebase";
}
@ReactMethod
public void track(String name, ReadableMap payload) {
Bundle bundle = new Bundle();
if (payload != null) {
HashMap<String, Object> payloadMap = payload.toHashMap();
for (Map.Entry<String, Object> entry : payloadMap.entrySet()) {
Object value = entry.getValue();
if (value != null) {
bundle.putString(entry.getKey(), entry.getValue().toString());
}
}
}
if (firebaseAnalytics != null) {
firebaseAnalytics.logEvent(name, bundle);
}
}
@ReactMethod
public void logException(boolean fatal, String message, ReadableMap payload) {
Bundle bundle = new Bundle();
if (payload != null) {
HashMap<String, Object> payloadMap = payload.toHashMap();
for (Map.Entry<String, Object> entry : payloadMap.entrySet()) {
Object value = entry.getValue();
if (value != null) {
bundle.putString(entry.getKey(), entry.getValue().toString());
}
}
}
if (firebaseAnalytics != null) {
firebaseAnalytics.logEvent(fatal ? "exception" : "warning", bundle);
}
if (fatal) {
Toast.makeText(context,
"An application error occurred which has been automatically logged. " +
"If you keep seeing this message, please provide feedback to the LBRY " +
"team by emailing hello@lbry.io.",
Toast.LENGTH_LONG).show();
}
}
}

View file

@ -1,87 +0,0 @@
package io.lbry.browser.reactmodules;
import android.content.Context;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.mixpanel.android.mpmetrics.MixpanelAPI;
import io.lbry.browser.BuildConfig;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONObject;
import org.json.JSONException;
public class MixpanelModule extends ReactContextBaseJavaModule {
private static final String MIXPANEL_TOKEN = BuildConfig.DEBUG ?
"bc1630b8be64c5dfaa4700b3a62969f3" /* Dev Testing */ :
"93b81fb957cb0ddcd3198c10853a6a95"; /* Production */
private Context context;
private MixpanelAPI mixpanel;
public MixpanelModule(ReactApplicationContext reactContext) {
super(reactContext);
this.context = reactContext;
this.mixpanel = MixpanelAPI.getInstance(this.context, MIXPANEL_TOKEN);
}
@Override
public String getName() {
return "Mixpanel";
}
@ReactMethod
public void track(String name, ReadableMap payload) {
JSONObject props = new JSONObject();
try {
if (payload != null) {
HashMap<String, Object> payloadMap = payload.toHashMap();
for (Map.Entry<String, Object> entry : payloadMap.entrySet()) {
props.put(entry.getKey(), entry.getValue());
}
}
} catch (JSONException e) {
// Cannot use props. Stick with empty props.
}
if (mixpanel != null) {
mixpanel.track(name, props);
}
}
@ReactMethod
public void logException(boolean fatal, String message, ReadableMap payload) {
JSONObject props = new JSONObject();
try {
props.put("Message", message);
if (payload != null) {
HashMap<String, Object> payloadMap = payload.toHashMap();
for (Map.Entry<String, Object> entry : payloadMap.entrySet()) {
props.put(entry.getKey(), entry.getValue());
}
}
} catch (JSONException e) {
// Cannot use props. Stick with empty props.
}
if (mixpanel != null) {
mixpanel.track(fatal ? "Exception" : "Warning", props);
}
if (fatal) {
Toast.makeText(context,
"An application error occurred which has been automatically logged. " +
"If you keep seeing this message, please provide feedback to the LBRY " +
"team by emailing hello@lbry.io.",
Toast.LENGTH_LONG).show();
}
}
}

View file

@ -9,7 +9,7 @@ import io.lbry.browser.reactmodules.BackgroundMediaModule;
import io.lbry.browser.reactmodules.DaemonServiceControlModule; import io.lbry.browser.reactmodules.DaemonServiceControlModule;
import io.lbry.browser.reactmodules.DownloadManagerModule; import io.lbry.browser.reactmodules.DownloadManagerModule;
import io.lbry.browser.reactmodules.FirstRunModule; import io.lbry.browser.reactmodules.FirstRunModule;
import io.lbry.browser.reactmodules.MixpanelModule; import io.lbry.browser.reactmodules.FirebaseModule;
import io.lbry.browser.reactmodules.ScreenOrientationModule; import io.lbry.browser.reactmodules.ScreenOrientationModule;
import io.lbry.browser.reactmodules.VersionInfoModule; import io.lbry.browser.reactmodules.VersionInfoModule;
import io.lbry.browser.reactmodules.UtilityModule;; import io.lbry.browser.reactmodules.UtilityModule;;
@ -32,7 +32,7 @@ public class LbryReactPackage implements ReactPackage {
modules.add(new DaemonServiceControlModule(reactContext)); modules.add(new DaemonServiceControlModule(reactContext));
modules.add(new DownloadManagerModule(reactContext)); modules.add(new DownloadManagerModule(reactContext));
modules.add(new FirstRunModule(reactContext)); modules.add(new FirstRunModule(reactContext));
modules.add(new MixpanelModule(reactContext)); modules.add(new FirebaseModule(reactContext));
modules.add(new ScreenOrientationModule(reactContext)); modules.add(new ScreenOrientationModule(reactContext));
modules.add(new UtilityModule(reactContext)); modules.add(new UtilityModule(reactContext));
modules.add(new VersionInfoModule(reactContext)); modules.add(new VersionInfoModule(reactContext));