Skip to content
This repository has been archived by the owner on Sep 1, 2023. It is now read-only.

Integrate Firebase for crash reporting, analytics, and performance monitoring #39

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ jobs:
- restore_cache:
key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }}

- run:
name: Setup Firebase configuration (google-services.json)
command: echo $GOOGLE_SERVICES_JSON_BASE64 | base64 -di > app/google-services.json

- run:
name: Download Dependencies
command: ./gradlew androidDependencies
Expand Down
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/build
/crashlytics.properties
/fabric.properties
/google-services.json

5 changes: 5 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'realm-android'
apply plugin: 'io.fabric'
apply plugin: 'com.google.firebase.firebase-perf'

android {
compileSdkVersion 26
Expand Down Expand Up @@ -151,6 +152,8 @@ dependencies {
implementation("com.crashlytics.sdk.android:answers:1.3.7@aar") {
transitive = true
}
implementation 'com.google.firebase:firebase-core:16.0.7'
implementation 'com.google.firebase:firebase-perf:16.2.3'
implementation "com.github.hotchemi:permissionsdispatcher:$rootProject.ext.permissionsDispatcherVersion"
kapt "com.github.hotchemi:permissionsdispatcher-processor:$rootProject.ext.permissionsDispatcherVersion"
implementation "com.github.slugify:slugify:2.1.9"
Expand Down Expand Up @@ -183,3 +186,5 @@ dependencies {
androidTestImplementation "com.android.support.test.espresso:espresso-intents:$rootProject.ext.espressoVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$rootProject.ext.espressoVersion"
}

apply plugin: 'com.google.gms.google-services'
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,6 @@ public class SpectreApplication extends Application {
protected OkHttpClient mOkHttpClient = null;
protected Picasso mPicasso = null;

@SuppressWarnings("FieldCanBeLocal")
private AnalyticsService mAnalyticsService = null;

// FIXME hacks
private LoginOrchestrator.HACKListener mHACKListener;

Expand All @@ -61,6 +58,7 @@ public void onCreate() {
Log.i(TAG, "APP LAUNCHED");

BusProvider.getBus().register(this);
AnalyticsService.start(this, BusProvider.getBus());
sInstance = this;

RxJavaPlugins.setErrorHandler(this::uncaughtRxException);
Expand All @@ -73,9 +71,6 @@ public void onCreate() {
NetworkService networkService = new NetworkService();
mHACKListener = networkService;
networkService.start(mOkHttpClient);

mAnalyticsService = new AnalyticsService(BusProvider.getBus());
mAnalyticsService.start();
}

private void setupMetadataRealm() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package me.vickychijwani.spectre.analytics;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

import com.crashlytics.android.answers.Answers;
import com.crashlytics.android.answers.CustomEvent;
import com.crashlytics.android.answers.LoginEvent;
import com.google.firebase.analytics.FirebaseAnalytics;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;

Expand All @@ -15,43 +17,39 @@
import me.vickychijwani.spectre.event.LoginDoneEvent;
import me.vickychijwani.spectre.event.LoginErrorEvent;
import me.vickychijwani.spectre.event.LogoutStatusEvent;
import me.vickychijwani.spectre.util.BundleBuilder;
import me.vickychijwani.spectre.util.log.Log;

public class AnalyticsService {

private static final String TAG = AnalyticsService.class.getSimpleName();

private final Bus mEventBus;
private static FirebaseAnalytics sFirebaseAnalytics;

public AnalyticsService(Bus eventBus) {
mEventBus = eventBus;
}

public void start() {
getBus().register(this);
getBus().post(new LoadGhostVersionEvent(true));
}
private static Bus sEventBus;

public void stop() {
getBus().unregister(this);
}
private static Listener sEventBusListener;

@Subscribe
public void onLoginDoneEvent(LoginDoneEvent event) {
logLogin(event.blogUrl, true);
private AnalyticsService() {}

// user just logged in, now's a good time to check this
getBus().post(new LoadGhostVersionEvent(true));
}
/**
* Ideally the `context` parameter should be an Activity context in order for page views to be
* recorded automatically, but we don't care about that particular feature, and initializing
* with the app context is certainly more convenient.
* @param context - to initialize Firebase Analytics. NOTE: NO REFERENCE to the object is stored
* @param eventBus - to listen for analytics triggers and respond
*/
public static void start(Context context, Bus eventBus) {
sFirebaseAnalytics = FirebaseAnalytics.getInstance(context);
sEventBus = eventBus;
sEventBusListener = new Listener(sEventBus);

@Subscribe
public void onLoginErrorEvent(LoginErrorEvent event) {
logLogin(event.blogUrl, false);
sEventBus.register(sEventBusListener);
sEventBus.post(new LoadGhostVersionEvent(true));
}

@Subscribe
public void onGhostVersionLoadedEvent(GhostVersionLoadedEvent event) {
logGhostVersion(event.version);
public static void stop() {
sEventBus.unregister(sEventBusListener);
}

private static void logGhostVersion(@Nullable String ghostVersion) {
Expand All @@ -61,6 +59,10 @@ private static void logGhostVersion(@Nullable String ghostVersion) {
Log.i(TAG, "GHOST VERSION = %s", ghostVersion);
Answers.getInstance().logCustom(new CustomEvent("Ghost Version")
.putCustomAttribute("version", ghostVersion));
sFirebaseAnalytics.logEvent("ghost_version", new BundleBuilder()
.put("version", ghostVersion)
.build());
sFirebaseAnalytics.setUserProperty("ghost_version", ghostVersion);
}

private static void logLogin(@Nullable String blogUrl, boolean success) {
Expand All @@ -72,31 +74,45 @@ private static void logLogin(@Nullable String blogUrl, boolean success) {
Answers.getInstance().logLogin(new LoginEvent()
.putCustomAttribute("URL", blogUrl)
.putSuccess(success));
sFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, new BundleBuilder()
.put("url", blogUrl)
// Param.SUCCESS expects a long value: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param#SUCCESS
.put(FirebaseAnalytics.Param.SUCCESS, success ? 1L : 0L)
.build());
}

public static void logGhostV0Error() {
Log.i(TAG, "GHOST VERSION 0.x ERROR - UPGRADE REQUIRED");
Answers.getInstance().logCustom(new CustomEvent("Ghost v0.x error"));
}

@Subscribe
public void onLogoutStatusEvent(LogoutStatusEvent logoutEvent) {
if (logoutEvent.succeeded) {
private static void logLogout(boolean logoutSucceeded) {
if (logoutSucceeded) {
Log.i(TAG, "LOGOUT SUCCEEDED");
Answers.getInstance().logCustom(new CustomEvent("Logout"));
sFirebaseAnalytics.logEvent("logout", new BundleBuilder().build());
}
}

public static void logGhostV0Error() {
Log.i(TAG, "GHOST VERSION 0.x ERROR - UPGRADE REQUIRED");
Answers.getInstance().logCustom(new CustomEvent("Ghost v0.x error"));
sFirebaseAnalytics.logEvent("ghost_v0_error", new BundleBuilder().build());
}

public static void logMetadataDbSchemaVersion(@NonNull String metadataDbSchemaVersion) {
Log.i(TAG, "METADATA DB SCHEMA VERSION = %s", metadataDbSchemaVersion);
Answers.getInstance().logCustom(new CustomEvent("Metadata DB Schema Version")
.putCustomAttribute("version", metadataDbSchemaVersion));
sFirebaseAnalytics.logEvent("metadata_db", new BundleBuilder()
.put("schema_version", metadataDbSchemaVersion)
.build());
sFirebaseAnalytics.setUserProperty("metadata_db_version", metadataDbSchemaVersion);
}

public static void logDbSchemaVersion(@NonNull String dbSchemaVersion) {
Log.i(TAG, "DB SCHEMA VERSION = %s", dbSchemaVersion);
Answers.getInstance().logCustom(new CustomEvent("DB Schema Version")
.putCustomAttribute("version", dbSchemaVersion));
sFirebaseAnalytics.logEvent("data_db", new BundleBuilder()
.put("schema_version", dbSchemaVersion)
.build());
sFirebaseAnalytics.setUserProperty("blog_db_schema_version", dbSchemaVersion);
}


Expand Down Expand Up @@ -153,26 +169,57 @@ public static void logConflictResolved() {
logPostAction("Conflict resolved", null);
}

@Subscribe
public void onFileUploadedEvent(FileUploadedEvent event) {
logPostAction("Image uploaded", null);
}

private static void logPostAction(@NonNull String postAction, @Nullable String postUrl) {
CustomEvent postStatsEvent = new CustomEvent("Post Actions")
CustomEvent postActionEvent = new CustomEvent("Post Actions")
.putCustomAttribute("Scenario", postAction);
BundleBuilder postActionBundle = new BundleBuilder()
.put("scenario", postAction);
if (postUrl != null) {
// FIXME this is a huge hack, also Fabric only shows 10 of these per day
postStatsEvent.putCustomAttribute("URL", postUrl);
postActionEvent.putCustomAttribute("URL", postUrl);
postActionBundle.put("url", postUrl);
}
Log.i(TAG, "POST ACTION: %s", postAction);
Answers.getInstance().logCustom(postStatsEvent);
Answers.getInstance().logCustom(postActionEvent);

sFirebaseAnalytics.logEvent("post_action", postActionBundle.build());
}


// misc private methods
private Bus getBus() {
return mEventBus;
private static class Listener {
private final Bus mEventBus;

Listener(Bus eventBus) {
mEventBus = eventBus;
}

@Subscribe
public void onLoginDoneEvent(LoginDoneEvent event) {
logLogin(event.blogUrl, true);

// user just logged in, now's a good time to check this
mEventBus.post(new LoadGhostVersionEvent(true));
}

@Subscribe
public void onLoginErrorEvent(LoginErrorEvent event) {
logLogin(event.blogUrl, false);
}

@Subscribe
public void onGhostVersionLoadedEvent(GhostVersionLoadedEvent event) {
logGhostVersion(event.version);
}

@Subscribe
public void onLogoutStatusEvent(LogoutStatusEvent logoutEvent) {
logLogout(logoutEvent.succeeded);
}

@Subscribe
public void onFileUploadedEvent(FileUploadedEvent event) {
logPostAction("Image uploaded", null);
}
}

}
Loading