Switch to firebase analytics

* Remove ScratchJrApplication class - it was only being used to initialize the old Google Analytics.
* Change AppUsage (home, school, other, noanswer) from being a prefix on `label` to being a Firebase user property.
* Set the user property when loading the index page - it shouldn’t change for the rest of the session.
This commit is contained in:
Chris Garrity 2019-10-21 09:24:37 -04:00
parent 1be3feb791
commit b965440709
9 changed files with 61 additions and 60 deletions

View file

@ -30,9 +30,10 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.google.firebase:firebase-core:17.2.0'
implementation 'com.google.firebase:firebase-analytics:17.2.0'
implementation 'com.google.android.gms:play-services-location:17.0.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.gms:play-services-analytics:17.0.0'
}
def appModuleRootFolder = '.'

View file

@ -17,7 +17,6 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme"
android:name="ScratchJrApplication"
android:hardwareAccelerated="true">
<provider android:name="ShareContentProvider"
android:grantUriPermissions="true"

View file

@ -30,9 +30,6 @@ import android.webkit.JavascriptInterface;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
/**
* The methods in this inner class are exposed directly to JavaScript in the HTML5 pages
* as AndroidInterface.
@ -44,7 +41,6 @@ public class JavaScriptDirectInterface {
/** Activity hosting the webview running the JavaScript */
private final ScratchJrActivity _activity;
private final ScratchJrApplication _application;
/** Current camera view, if active */
private CameraView _cameraView;
@ -55,9 +51,8 @@ public class JavaScriptDirectInterface {
/**
* @param scratchJrActivity
*/
JavaScriptDirectInterface(ScratchJrActivity scratchJrActivity, ScratchJrApplication application) {
JavaScriptDirectInterface(ScratchJrActivity scratchJrActivity) {
_activity = scratchJrActivity;
_application = application;
}
@JavascriptInterface
@ -623,7 +618,13 @@ public class JavaScriptDirectInterface {
// Analytics
@JavascriptInterface
public void analyticsEvent(String category, String action, String label, long value) {
_application.getDefaultTracker().send(new HitBuilders.EventBuilder()
.setCategory(category).setAction(action).setLabel(label).setValue(value).build());
_activity.logAnalyticsEvent(category, action, label);
}
@JavascriptInterface
public void setAnalyticsUser(String place) {
if (place != null) {
_activity.setAnalyticsUser(place);
}
}
}

View file

@ -33,8 +33,7 @@ import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.RelativeLayout;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import com.google.firebase.analytics.FirebaseAnalytics;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
@ -55,10 +54,10 @@ public class ScratchJrActivity
{
/** Milliseconds to pan when showing the soft keyboard */
private static final int SOFT_KEYBOARD_PAN_MS = 250;
/** Log tag for Scratch Jr. app */
private static final String LOG_TAG = "ScratchJr";
/** Bundle key in which the current url is stored */
private static final String BUNDLE_KEY_URL = "url";
@ -91,7 +90,7 @@ public class ScratchJrActivity
/** Set to true when the splash screen is done loading. This is used for unit testing. */
private boolean _splashDone = false;
/** Y starting and ending coordinate for soft keyboard scroll position */
private int _softKeyboardScrollPosY0;
private int _softKeyboardScrollPosY1;
@ -104,8 +103,8 @@ public class ScratchJrActivity
public int cameraPermissionResult = PackageManager.PERMISSION_DENIED;
public int micPermissionResult = PackageManager.PERMISSION_DENIED;
/** Analytics tracker */
private Tracker _tracker;
/* Firebase analytics tracking */
private FirebaseAnalytics _FirebaseAnalytics;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -153,8 +152,7 @@ public class ScratchJrActivity
receiveProject(it.getData());
}
ScratchJrApplication application = (ScratchJrApplication) getApplication();
_tracker = application.getDefaultTracker();
_FirebaseAnalytics = FirebaseAnalytics.getInstance(this);
// When System UI bar is displayed, wait one second and then re-assert immersive mode.
getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(new OnSystemUiVisibilityChangeListener() {
@ -283,7 +281,7 @@ public class ScratchJrActivity
_soundManager.close();
_soundRecorderManager.close();
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
@ -368,7 +366,7 @@ public class ScratchJrActivity
webSettings.setLoadWithOverviewMode(false);
webSettings.setUseWideViewPort(false);
// Uncomment to enable remote Chrome debugging on a physical Android device
//WebView.setWebContentsDebuggingEnabled(true);
// WebView.setWebContentsDebuggingEnabled(true);
// Enable cookie persistence
CookieManager.setAcceptFileSchemeCookies(true);
@ -381,7 +379,7 @@ public class ScratchJrActivity
CookieSyncManager.createInstance(this);
/* Object exposed to the JavaScript that makes it easy to bridge JavaScript and Java */
JavaScriptDirectInterface javaScriptDirectInterface = new JavaScriptDirectInterface(this, (ScratchJrApplication) getApplication());
JavaScriptDirectInterface javaScriptDirectInterface = new JavaScriptDirectInterface(this);
_webView.addJavascriptInterface(javaScriptDirectInterface, "AndroidInterface");
_webView.setWebViewClient(new WebViewClient() {
@Override
@ -406,8 +404,8 @@ public class ScratchJrActivity
// Track page load
String[] parts = url.split("/");
_tracker.setScreenName(parts[parts.length - 1]);
_tracker.send(new HitBuilders.ScreenViewBuilder().build());
String page = parts[parts.length - 1].split("\\?")[0];
_FirebaseAnalytics.setCurrentScreen((Activity) view.getContext(), page, null);
}
});
_webView.requestFocus(View.FOCUS_DOWN);
@ -478,6 +476,28 @@ public class ScratchJrActivity
});
}
/**
* log a Firebase analytics event for the app
* @param category
* @param action
* @param label
*/
public void logAnalyticsEvent(String category, String action, String label) {
Bundle params = new Bundle();
params.putString(FirebaseAnalytics.Param.ITEM_ID, action);
params.putString(FirebaseAnalytics.Param.ITEM_CATEGORY, category);
params.putString(FirebaseAnalytics.Param.ITEM_NAME, label);
_FirebaseAnalytics.logEvent(FirebaseAnalytics.Event.VIEW_ITEM, params);
}
/**
* Record the preferred place for the user: home, school, other, noanswer
* @param place
*/
public void setAnalyticsUser(String place) {
_FirebaseAnalytics.setUserProperty("place_preference", place);
}
public void translateAndScaleRectToContainerCoords(RectF rect, float devicePixelRatio) {
float wx = _webView.getX();
float wy = _webView.getY();
@ -489,7 +509,7 @@ public class ScratchJrActivity
_softKeyboardScrollPosY0 = topYPx;
_softKeyboardScrollPosY1 = bottomYPx;
}
/**
* Height of the status bar at the top of the screen
*/

View file

@ -1,24 +0,0 @@
package org.scratchjr.android;
import android.app.Application;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.Logger;
import com.google.android.gms.analytics.Tracker;
public class ScratchJrApplication extends Application {
private Tracker mTracker;
/**
* Gets the default {@link Tracker} for this {@link Application}.
* @return tracker
*/
synchronized public Tracker getDefaultTracker() {
if (mTracker == null) {
GoogleAnalytics analytics = GoogleAnalytics.getInstance(this);
// To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG
mTracker = analytics.newTracker(R.xml.global_tracker);
}
return mTracker;
}
}

View file

@ -23,5 +23,6 @@ allprojects {
url "https://maven.google.com"
}
jcenter()
google()
}
}

View file

@ -84,7 +84,7 @@ function indexLoadStart (afterUsage) {
gn('blueguy').className = 'blue hide';
gn('redguy').className = 'red hide';
gn('gear').className = 'gear show';
if (afterUsage) {
gn('catface').className = 'catface show';
gn('jrlogo').className = 'jrlogo show';
@ -94,6 +94,7 @@ function indexLoadStart (afterUsage) {
gn('usageOther').className = 'usageOther hide';
gn('usageNoanswer').className = 'usageNoanswer hide';
}
iOS.setAnalyticsUser(AppUsage.currentUsage);
}
gn('gettings').className = 'gettings show';
gn('startcode').className = 'startcode show';
@ -113,13 +114,13 @@ function indexLoadUsage () {
gn('redguy').className = 'red hide';
gn('catface').className = 'catface hide';
gn('jrlogo').className = 'jrlogo hide';
gn('usageQuestion').textContent = Localization.localize('USAGE_QUESTION');
gn('useSchoolText').textContent = Localization.localize('USAGE_SCHOOL');
gn('useHomeText').textContent = Localization.localize('USAGE_HOME');
gn('useOtherText').textContent = Localization.localize('USAGE_OTHER');
gn('usageNoanswerText').textContent = Localization.localize('USAGE_NONE');
gn('usageQuestion').className = 'usageQuestion show';
gn('usageSchool').className = 'usageSchool show';
gn('usageHome').className = 'usageHome show';

View file

@ -3,7 +3,6 @@ import IO from './IO';
import Lobby from '../lobby/Lobby';
import Alert from '../editor/ui/Alert';
import ScratchAudio from '../utils/ScratchAudio';
import AppUsage from '../utils/AppUsage';
//////////////////////////////////////////////////
// Tablet interface functions
@ -356,8 +355,11 @@ export default class iOS {
if (!value) {
value = 1;
}
let usageLabel = label ? AppUsage.currentUsage + label : AppUsage.currentUsage;
tabletInterface.analyticsEvent(category, action, usageLabel, value);
tabletInterface.analyticsEvent(category, action, label, value);
}
static setAnalyticsUser (preferredPlace) {
tabletInterface.setAnalyticsUser(preferredPlace);
}
// Web Wiew delegate call backs

View file

@ -6,7 +6,7 @@ export default class AppUsage {
static get currentUsage () {
return currentUsage;
}
/**
* Initialize currentUsage for attaching to Analytics events from
* the usage cookie if it is set. currentUsage is blank if the cookie is
@ -14,9 +14,9 @@ export default class AppUsage {
*/
static initUsage () {
const usageCookie = Cookie.get('usage');
currentUsage = (usageCookie) ? usageCookie + '::' : '';
currentUsage = (usageCookie) ? usageCookie : '';
}
/**
* Check whether the App should ask for the usage data (first time launched)
* @return {boolean} True if the usage cookie has never been set
@ -25,7 +25,7 @@ export default class AppUsage {
var usageCookie = Cookie.get('usage');
return usageCookie === null;
}
/**
* Set the usage cookie for tracking Analytics Events
* @param {string} kind answer from user to the usage survey (home, school, other, noanswer)
@ -36,6 +36,6 @@ export default class AppUsage {
} else {
Cookie.set('usage', kind);
}
currentUsage = (kind === '') ? 'noanswer::' : kind + '::';
currentUsage = (kind === '') ? 'noanswer' : kind;
}
}