diff --git a/.gitignore b/.gitignore index 37caadee..47509c25 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ src/main/assets/index.android.bundle.meta lbry-android.keystore p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json .gitsecret/keys/random_seed + diff --git a/.gitsecret/paths/mapping.cfg b/.gitsecret/paths/mapping.cfg index dba90436..3c5cb735 100644 --- a/.gitsecret/paths/mapping.cfg +++ b/.gitsecret/paths/mapping.cfg @@ -1 +1,2 @@ lbry-android.keystore: +p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json diff --git a/app/src/component/fileDownloadButton/view.js b/app/src/component/fileDownloadButton/view.js index 985a3a26..c3260167 100644 --- a/app/src/component/fileDownloadButton/view.js +++ b/app/src/component/fileDownloadButton/view.js @@ -73,8 +73,8 @@ class FileDownloadButton extends React.PureComponent { text={(isPlayable ? 'Play' : (isViewable ? 'View' : 'Download'))} onLayout={onButtonLayout} style={[style, fileDownloadButtonStyle.container]} onPress={() => { - if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('Purchase Uri', { Uri: uri }); + if (NativeModules.Firebase) { + NativeModules.Firebase.track('purchase_uri', { uri: uri }); } purchaseUri(uri, onStartDownloadFailed); if (isPlayable && onPlay) { diff --git a/app/src/component/fileItem/view.js b/app/src/component/fileItem/view.js index 3ca2f773..c4b60f9b 100644 --- a/app/src/component/fileItem/view.js +++ b/app/src/component/fileItem/view.js @@ -36,8 +36,8 @@ class FileItem extends React.PureComponent { navigateToFileUri = () => { const { navigation, uri } = this.props; const normalizedUri = normalizeURI(uri); - if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('Discover Tap', { Uri: normalizeURI }); + if (NativeModules.Firebase) { + NativeModules.Firebase.track('explore_click', { uri: normalizedUri }); } navigateToUri(navigation, normalizedUri); } diff --git a/app/src/component/searchInput/index.js b/app/src/component/searchInput/index.js index a7cefcb8..5469f359 100644 --- a/app/src/component/searchInput/index.js +++ b/app/src/component/searchInput/index.js @@ -5,8 +5,8 @@ import SearchInput from './view'; const perform = dispatch => ({ search: search => { - if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('Search', { Query: search }); + if (NativeModules.Firebase) { + NativeModules.Firebase.track('search', { query: search }); } return dispatch(doSearch(search)); }, diff --git a/app/src/index.js b/app/src/index.js index f8dff4e2..6c5892a5 100644 --- a/app/src/index.js +++ b/app/src/index.js @@ -43,8 +43,8 @@ import thunk from 'redux-thunk'; const globalExceptionHandler = (error, isFatal) => { - if (error && NativeModules.Mixpanel) { - NativeModules.Mixpanel.logException(isFatal, error.message ? error.message : "No message", error); + if (error && NativeModules.Firebase) { + NativeModules.Firebase.logException(isFatal, error.message ? error.message : "No message", error); } }; setJSExceptionHandler(globalExceptionHandler, true); diff --git a/app/src/page/discover/view.js b/app/src/page/discover/view.js index 32e8674b..4bf95f03 100644 --- a/app/src/page/discover/view.js +++ b/app/src/page/discover/view.js @@ -34,9 +34,9 @@ class DiscoverPage extends React.PureComponent { AsyncStorage.getItem('firstLaunchSuspended').then(suspended => { AsyncStorage.removeItem('firstLaunchSuspended'); const appSuspended = (suspended === 'true'); - if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('First Run Time', { - 'Total Seconds': delta, 'App Suspended': appSuspended + if (NativeModules.Firebase) { + NativeModules.Firebase.track('first_run_time', { + 'total_seconds': delta, 'app_suspended': appSuspended }); } }); diff --git a/app/src/page/file/view.js b/app/src/page/file/view.js index f032d5db..d24d6145 100644 --- a/app/src/page/file/view.js +++ b/app/src/page/file/view.js @@ -88,8 +88,8 @@ class FilePage extends React.PureComponent { this.fetchFileInfo(this.props); this.fetchCostInfo(this.props); - if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('Open File Page', { Uri: uri }); + if (NativeModules.Firebase) { + NativeModules.Firebase.track('open_file_page', { uri: uri }); } if (NativeModules.UtilityModule) { NativeModules.UtilityModule.keepAwakeOn(); @@ -301,12 +301,12 @@ class FilePage extends React.PureComponent { const { uri } = navigation.state.params; this.logFileView(uri, fileInfo, timeToStartMillis); - let payload = { 'Uri': uri }; + let payload = { 'uri': uri }; if (!isNaN(timeToStart)) { - payload['Time to Start (seconds)'] = timeToStart; - payload['Time to Start (ms)'] = timeToStartMillis; + payload['time_to_start_seconds'] = timeToStart; + payload['time_to_start_ms'] = timeToStartMillis; } - NativeModules.Mixpanel.track('Play', payload); + NativeModules.Firebase.track('play', payload); } onPlaybackFinished = () => { diff --git a/app/src/page/splash/view.js b/app/src/page/splash/view.js index 3365adcf..1df1e394 100644 --- a/app/src/page/splash/view.js +++ b/app/src/page/splash/view.js @@ -254,8 +254,8 @@ class SplashScreen extends React.PureComponent { } componentDidMount() { - if (NativeModules.Mixpanel) { - NativeModules.Mixpanel.track('App Launch', null); + if (NativeModules.Firebase) { + NativeModules.Firebase.track('app_launch', null); } Linking.getInitialURL().then((url) => { diff --git a/buildozer.spec.sample b/buildozer.spec.sample index cb4aec9d..2a10e128 100644 --- a/buildozer.spec.sample +++ b/buildozer.spec.sample @@ -148,7 +148,7 @@ android.react_src = ./app # (list) Gradle dependencies to add (currently works only with sdl2_gradle # 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 #p4a.branch = stable diff --git a/buildozer.spec.travis b/buildozer.spec.travis index cb4aec9d..2a10e128 100644 --- a/buildozer.spec.travis +++ b/buildozer.spec.travis @@ -148,7 +148,7 @@ android.react_src = ./app # (list) Gradle dependencies to add (currently works only with sdl2_gradle # 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 #p4a.branch = stable diff --git a/buildozer.spec.vagrant b/buildozer.spec.vagrant index cb4aec9d..2a10e128 100644 --- a/buildozer.spec.vagrant +++ b/buildozer.spec.vagrant @@ -148,7 +148,7 @@ android.react_src = ./app # (list) Gradle dependencies to add (currently works only with sdl2_gradle # 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 #p4a.branch = stable diff --git a/lbry-android.keystore.secret b/lbry-android.keystore.secret index 3768f7c9..ae36c4be 100644 Binary files a/lbry-android.keystore.secret and b/lbry-android.keystore.secret differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/build.py b/p4a/pythonforandroid/bootstraps/lbry/build/build.py index 6c64175d..71e5e7c9 100755 --- a/p4a/pythonforandroid/bootstraps/lbry/build/build.py +++ b/p4a/pythonforandroid/bootstraps/lbry/build/build.py @@ -405,10 +405,10 @@ main.py that loads it.''') android_api=android_api, build_tools_version=build_tools_version) - render( - 'settings.tmpl.gradle', - 'settings.gradle' - ) + render('settings.gradle', 'settings.gradle') + + ## google-services.json for firebase + render('google-services.json', 'google-services.json') # copy icon drawables for folder in ('drawable-hdpi', 'drawable-mdpi', 'drawable-xhdpi', 'drawable-xxhdpi', 'drawable-xxxhdpi'): diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/templates/build.tmpl.gradle b/p4a/pythonforandroid/bootstraps/lbry/build/templates/build.tmpl.gradle index fcde802a..744f259c 100644 --- a/p4a/pythonforandroid/bootstraps/lbry/build/templates/build.tmpl.gradle +++ b/p4a/pythonforandroid/bootstraps/lbry/build/templates/build.tmpl.gradle @@ -6,6 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:3.0.0' + classpath 'com.google.gms:google-services:4.0.1' } } @@ -112,3 +113,6 @@ dependencies { {%- endfor %} {%- endif %} } + +apply plugin: 'com.google.gms.google-services' +com.google.gms.googleservices.GoogleServicesPlugin.config.disableVersionCheck = true diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json.secret b/p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json.secret new file mode 100644 index 00000000..faf326fb Binary files /dev/null and b/p4a/pythonforandroid/bootstraps/lbry/build/templates/google-services.json.secret differ diff --git a/p4a/pythonforandroid/bootstraps/lbry/build/templates/settings.tmpl.gradle b/p4a/pythonforandroid/bootstraps/lbry/build/templates/settings.gradle similarity index 100% rename from p4a/pythonforandroid/bootstraps/lbry/build/templates/settings.tmpl.gradle rename to p4a/pythonforandroid/bootstraps/lbry/build/templates/settings.gradle diff --git a/src/main/java/io/lbry/browser/reactmodules/FirebaseModule.java b/src/main/java/io/lbry/browser/reactmodules/FirebaseModule.java new file mode 100644 index 00000000..7804091d --- /dev/null +++ b/src/main/java/io/lbry/browser/reactmodules/FirebaseModule.java @@ -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 payloadMap = payload.toHashMap(); + for (Map.Entry 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 payloadMap = payload.toHashMap(); + for (Map.Entry 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(); + } + } +} \ No newline at end of file diff --git a/src/main/java/io/lbry/browser/reactmodules/MixpanelModule.java b/src/main/java/io/lbry/browser/reactmodules/MixpanelModule.java deleted file mode 100644 index 57874d14..00000000 --- a/src/main/java/io/lbry/browser/reactmodules/MixpanelModule.java +++ /dev/null @@ -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 payloadMap = payload.toHashMap(); - for (Map.Entry 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 payloadMap = payload.toHashMap(); - for (Map.Entry 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(); - } - } -} diff --git a/src/main/java/io/lbry/browser/reactpackages/LbryReactPackage.java b/src/main/java/io/lbry/browser/reactpackages/LbryReactPackage.java index 6ec81d00..fb6862c6 100644 --- a/src/main/java/io/lbry/browser/reactpackages/LbryReactPackage.java +++ b/src/main/java/io/lbry/browser/reactpackages/LbryReactPackage.java @@ -9,7 +9,7 @@ import io.lbry.browser.reactmodules.BackgroundMediaModule; import io.lbry.browser.reactmodules.DaemonServiceControlModule; import io.lbry.browser.reactmodules.DownloadManagerModule; 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.VersionInfoModule; import io.lbry.browser.reactmodules.UtilityModule;; @@ -32,7 +32,7 @@ public class LbryReactPackage implements ReactPackage { modules.add(new DaemonServiceControlModule(reactContext)); modules.add(new DownloadManagerModule(reactContext)); modules.add(new FirstRunModule(reactContext)); - modules.add(new MixpanelModule(reactContext)); + modules.add(new FirebaseModule(reactContext)); modules.add(new ScreenOrientationModule(reactContext)); modules.add(new UtilityModule(reactContext)); modules.add(new VersionInfoModule(reactContext));