Skip to content

Repository files navigation

AnalyticsMobile

AnalyticsMobile Releases-TagsMIT License
AnalyticsWeb

Mobile Analytics Implementation Playground

Table of Contents

Introduction

Google Analytics 4 (GA4) is a powerful analytics tool that enables to track user interactions and behaviors on our mobile app. Implementing GA4 on our mobile app requires a few steps:

  1. Set up a GA4 property in the Google Analytics interface.
  2. Install the Google Analytics for Firebase SDK in the mobile app.
  3. Configure the app to use the Firebase SDK, including registering the app with Firebase.
  4. Enable the GA4 property for our Firebase project.
  5. Update our app's code to use the GA4 tracking code.

This is a playground of analytic implementation for a Android Mobile App using Firebase SDK, GTM, and GA4 mobile data stream that allows to explore the implementation of:

Figure 1 - AnalyticsMobile App Screenshot

Before we start with the playground set up and back end, we should already have a Firebase project created and Google Analytics 4 mobile data stream. Having them created and configured will facilitate the use of the playground as a data source for the tools.

This Android Mobile App is in JAVA programming language with the following Project Structure:

  • Project
    • Android Gradle Plugin Version: 8.2.0
    • Gradle Version: 8.2
  • Modules
    • Properties
      • Compile SDK Version: 34
      • Source Compatibility: $JavaVersion.VERSION_11
      • Target Compatibility: $JavaVersion.VERSION_11
    • Default Config
      • Application ID: com.arsari.analyticsmobile
      • Version Code 310240115
      • Varsion Name: 3.10.240115
      • Target SDK Version: 34
      • Min SDK Version: 24
  • Built Variants
    • Minify Enabled: false

Fundamentals: Setup Firebase SDK and Analytics SDK

In Android Studio, first click the Android label to open the drop-down menu for the project navigator view, and choose Project to see the full root directory structure of the project. Expand the project directory and the /app/ directory within. Drag-and-drop the google-services.json file, downloaded from the Firebase console, into the /app/ directory.

Now we need to modify the following files to add some dependencies:

  1. In the project root directory look for build.gradle file, opened, and add the following before the plugins object, if any:

    buildscript {
    repositories {
    // Make sure that you have the following two repositoriesgoogle() // Google's Maven repositorymavenCentral() // Maven Central repository
    }
    dependencies {
    // Add the dependency for the Google services Gradle pluginclasspath'com.google.gms:google-services:4.3.15'
    }
    }

    Note! If a newer version is available, this will be indicated by a yellow highlight around the classpath code.

  2. In the /app/ directory look for build.gradle file and add the following:

    plugins {
    ...
    // Firebase: Add the Google services Gradle pluginid'com.google.gms.google-services'
    }
    ...
    dependencies {
    ...
    // Firebase: Import the Firebase BoMimplementationplatform('com.google.firebase:firebase-bom:32.5.0')
    // Firebase Analytics (Java)implementation'com.google.firebase:firebase-analytics:21.5.0'// TODO: Add the dependencies for Firebase products you want to use/** * When using the BoM, don't specify versions in Firebase dependencies * Add the dependencies for any other desired Firebase products * https://firebase.google.com/docs/android/setup#available-libraries */// GTM: Import the tag manager servicesimplementation'com.google.android.gms:play-services-tagmanager:18.0.4'
    }

    Note! By using the Firebase Android BoM, your app will always use compatible versions of Firebase Android libraries. If there are newer versions of the Firebase BOM, Firebase Analytics, and Google Tag Manager dependencies available, this will be indicated with a yellow highlight around the implementation statements.

    GTM was added as a dependency in the script above but we still need to configure the container. See GTM setup for additional steps.

  3. In your MainActivity.java file, declare the com.google.firebase.analytics.FirebaseAnalytics object at the top of the MainActivity class:

    publicclassMainActivityextendsAppCompatActivity {
    ...
    // Declare FirebaseAnalyticsprivateFirebaseAnalyticsmFirebaseAnalytics;
    ...
    }
  4. Initialize the FirebaseAnalytics in the onCreate() method:

    protectedvoidonCreate() {
    ...
    // Obtain the FirebaseAnalytics instance.mFirebaseAnalytics = FirebaseAnalytics.getInstance(MainActivity.this);
    ...
    }

Tagging Implementation

After you have created a FirebaseAnalytics instance, you can begin to log events with the .logEvent() method. The event dataLayer array-object or Bundle is send to GA4 using the statement FirebaseAnalytics.logEvent(EVENT_NAME, EVENT_PARAMS_BUNDLE).

Firebase SDK have some events and parameters out-of-box which is highly recommended to use before creating custom events and parameters. The out-of-box events and parameters will be prefixed with FirebaseAnalytics.Event.NAME_OF_EVENT and FirebaseAnalytics.Param.NAME_OF_PARAMETER. Send events along with their prescribed parameters, to ensure maximum available detail in our reports and to benefit from future features and integration's as they become available. We can find implementation details for recommended event types in the following locations:

User ID and others user properties are set using FirebaseAnalytics.setUserId(USER_ID) and FirebaseAnalytics.setUserProperty(EVENT_NAME, STRING_VALUE). For more information see Set User Properties and Set User ID.

On our implementation, we create a userAuthenticated(BOOLEAN_VALUE) method which fires the corresponding user properties attributes that describes the app-user. After setting a user ID, all future events will be automatically tagged with this value.

mFirebaseAnalytics.setUserId(ui);
mFirebaseAnalytics.setUserProperty("logged_in", "true");
ormFirebaseAnalytics.setUserProperty("logged_in", "false");

To facilitate identified user actions by their UserID, a custom_user_id custom dimension event, not user, is implemented as a global parameter in GA4 which allow to use it in the Explorer reports (user_id is a reserved dimension that is only available for the GA4 User Explorer report). The custom_user_id was set as event in GA4 because if it is set as a user property we need to fire it using mFirebaseAnalytics.setUserProperty() method and could not be append to each fired event.

The tagging implementation for events log consider the followings user actions (ui interactions), system events (content tools), and errors based on a app resource click and a setOnClickListener() method to fire the corresponding events:

Note! Events and parameters predefined in Firebase SDK has been capitalized.

User ActionEventTypeParametersGA4 ScopeGA4 Definitions
Screen ViewSCREEN_VIEWcontent toolSCREEN_NAME
SCREEN_CLASS
app_name
app_desc
app_author
author_email
content_group
content_type
language_code
Event
Event
Event
Event
Event
Event
Event
Event
Event
Predefined
Predefined
Dimension
Dimension
Dimension
Dimension
Predefined
Predefined
Predefined
Sign InLOGINuser interactionmethodEventPredefined
Sign Inlogin_errorcontent toolerror_message
toast_impression
Event
Event
Dimension
Dimension
EmailGENERATE_LEADuser interactioncontact_method
CURRENCY
VALUE
Event
Event
Event
Dimension
Predefined
Predefined
Outbound Linkoutbound_linkuser interactionlink_id
link_url
link_text
outbound
Event
Event
Event
Event
Predefined
Predefined
Predefined
Predefined
PhoneGENERATE_LEADuser interactioncontact_method
CURRENCY
VALUE
Event
Event
Event
Dimension
Predefined
Predefined
PurchasePURCHASEuser interactionTRANSACTION_ID
AFFILIATION
CURRENCY
VALUE
TAX
SHIPPING
COUPON
ITEMS
Event
Event
Event
Event
Event
Event
Event
Event
Predefined
Predefined
Predefined
Predefined
Predefined
Predefined
Predefined
Predefined
Searchsearch_dialog_openeduser interaction
- okSEARCHuser interactionsearch_termEventPredefined
- cancelsearch_dialog_closeduser interaction
Searchsearch_errorcontent toolerror_message
toast_impression
Event
Event
Dimension
Dimension
Videovideo_startuser interactionvideo_duration
video_current_time
video_percent
video_status
video_provider
video_title
video_url
Event
Event
Event
Event
Event
Event
Event
Metric (sec)
Metric (sec)
Dimension
Dimension
Predefined
Predefined
Predefined
Videovideo_progresscontent toolvideo_duration
video_current_time
video_percent
video_status
video_provider
video_title
video_url
Event
Event
Event
Event
Event
Event
Event
Metric (sec)
Metric (sec)
Dimension
Dimension
Predefined
Predefined
Predefined
Videovideo_completecontent toolvideo_duration
video_current_time
video_percent
video_status
video_provider
video_title
video_url
Event
Event
Event
Event
Event
Event
Event
Metric (sec)
Metric (sec)
Dimension
Dimension
Predefined
Predefined
Predefined
Video Playingvideo_stopuser interactionvideo_duration
video_current_time
video_percent
video_status
video_provider
video_title
video_url
Event
Event
Event
Event
Event
Event
Event
Metric (sec)
Metric (sec)
Dimension
Dimension
Predefined
Predefined
Predefined
Sign Outlogoutuser interaction
Sign Outlogout_errorcontent toolerror_message
toast_impression
Event
Event
Dimension
Dimension

The following global parameters apply to most of the above events:

Global ParametersGA4 ScopeGA4 Definitions
event_typeEventDimension
button_textEventDimension
resource_idEventDimension
e_timestamp (milliseconds)EventDimension
custom_timestamp (ISO 8601)EventDimension
custom_user_idEventDimension
logged_in (user property)UserDimension
user_id (user property)UserPredefined

The events dataLayer array-object or Bundle is based on Google Analytics for Firebase events recommendations.

We classified the implementation of the dataLayer array-objects into the following:

The dataLayer array-object for this five classification of event objects has been setup in the mobile app with individual tags and triggers.

Screen View Event

This screen view event fires automatically when the mobile app is initiated or user leave and came back to the App. The event Bundle is composed of:

Bundleparams = newBundle();
params.putString(FirebaseAnalytics.Param.SCREEN_NAME, screenName);
params.putString(FirebaseAnalytics.Param.SCREEN_CLASS, "MainActivity");
params.putString("app_name", "AnalyticsMobile");
params.putString("app_desc", "Mobile Analytics Implementation Playground");
params.putString("app_author", "Arturo Santiago-Rivera");
params.putString("author_email", "asantiago@arsari.com");
params.putString("content_group", "Implementation");
params.putString("content_type", "Playground");
params.putString("language_code", "en-US");
// Global Parametersparams.putString("event_type", et);
params.putString("button_text", click);
params.putString("resource_id", resourceId);
params.putString("e_timestamp", newDate().getTime()); // millisecondsparams.putString("custom_timestamp", timeStamp()); // ISO 8061params.putString("custom_user_id", ui);
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW, params);

General Events

The general events are events that have simple parameters Bundle some of those parameters predefined in the Firebase SDK.

Bundleparams = newBundle();
// loginparams.putString(FirebaseAnalytics.Param.METHOD, "google");
// generate_leadparams.putString("contact_method", cm);
params.putString(FirebaseAnalytics.Param.CURRENCY, cc);
params.putDouble(FirebaseAnalytics.Param.VALUE, ev);
// outbound_linkparams.putString("link_url", url);
params.putString("link_text", text);
params.putString("link_id", resourceId);
params.putBoolean("outbound", ol);
// searchparams.putString(FirebaseAnalytics.Param.SEARCH_TERM, st);
// Global Parametersparams.putString("event_type", et[0]);
params.putString("button_text", desc == null ? click : desc);
params.putString("resource_id", resourceId);
params.putString("e_timestamp", String.valueOf(newDate().getTime())); // millisecondsparams.putString("custom_timestamp", timeStamp()); // ISO 8061params.putString("custom_user_id", ui);
case"login":
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, params);
break;
case"generate_lead":
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.GENERATE_LEAD, params);
break;
case"search":
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SEARCH, params);
break;
default:
mFirebaseAnalytics.logEvent(en[0], params);
break;

Purchase Event

The implemented purchase eventBundle is composed of:

// Item parametersBundleitem1 = newBundle();
item1.putString(FirebaseAnalytics.Param.ITEM_ID, ("SKU_" + rand.nextInt(1000) + 1));
item1.putString(FirebaseAnalytics.Param.ITEM_NAME, "jeggings");
item1.putString(FirebaseAnalytics.Param.ITEM_CATEGORY, "pants");
item1.putString(FirebaseAnalytics.Param.ITEM_VARIANT, "black");
item1.putString(FirebaseAnalytics.Param.ITEM_BRAND, "My Self");
item1.putLong(FirebaseAnalytics.Param.QUANTITY, qty);
item1.putDouble(FirebaseAnalytics.Param.PRICE, price);
item1.putLong(FirebaseAnalytics.Param.INDEX, 1);
Bundleitem2 = newBundle();
item2.putString(FirebaseAnalytics.Param.ITEM_ID, ("SKU_" + rand.nextInt(1000) + 1));
item2.putString(FirebaseAnalytics.Param.ITEM_NAME, "boots");
item2.putString(FirebaseAnalytics.Param.ITEM_CATEGORY, "shoes");
item2.putString(FirebaseAnalytics.Param.ITEM_VARIANT, "brown");
item2.putString(FirebaseAnalytics.Param.ITEM_BRAND, "My Self");
item2.putLong(FirebaseAnalytics.Param.QUANTITY, qty);
item2.putDouble(FirebaseAnalytics.Param.PRICE, price);
item2.putLong(FirebaseAnalytics.Param.INDEX, 2);
// Purchase event parametersBundleparams = newBundle();
params.putString(FirebaseAnalytics.Param.TRANSACTION_ID, transID);
params.putString(FirebaseAnalytics.Param.AFFILIATION, "My Great Store");
params.putString(FirebaseAnalytics.Param.CURRENCY, cc);
params.putDouble(FirebaseAnalytics.Param.VALUE, ev);
params.putDouble(FirebaseAnalytics.Param.TAX, tax);
params.putDouble(FirebaseAnalytics.Param.SHIPPING, shipping);
params.putString(FirebaseAnalytics.Param.COUPON, "SUMMER_FUN");
params.putParcelableArray(FirebaseAnalytics.Param.ITEMS, newParcelable[]{item1, item2});
// Global parametersparams.putString("event_type", et[0]);
params.putString("button_text", desc == null ? click : desc);
params.putString("resource_id", resourceId);
params.putString("e_timestamp", String.valueOf(newDate().getTime())); // millisecondsparams.putString("custom_timestamp", timeStamp()); // ISO 8061params.putString("custom_user_id", ui);
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.PURCHASE, params);

Error Events

The implemented error eventsBundle is composed of:

Bundleparams = newBundle();
params.putString("error_message", message);
params.putBoolean("toast_impression", true);
// Global parametersparams.putString("event_type", et[0]);
params.putString("button_text", desc == null ? click : desc);
params.putString("resource_id", resourceId);
params.putString("e_timestamp", String.valueOf(newDate().getTime())); // millisecondsparams.putString("custom_timestamp", timeStamp()); // ISO 8061params.putString("custom_user_id", ui);
mFirebaseAnalytics.logEvent(EVENT_NAME, params);

Video Events

The implemented video start and video stopBundle is composed of:

Bundleparams = newBundle();
params.putLong("video_duration", vd);
params.putLong("video_current_time", vct[0]);
params.putString("video_percent", milestone + "%");
params.putString("video_status", vs[0]);
params.putString("video_provider", vp);
params.putString("video_title", vt);
params.putString("video_url", vu);
// Global parametersparams.putString("event_type", et[0]);
params.putString("button_text", desc == null ? click : desc);
params.putString("resource_id", resourceId);
params.putString("e_timestamp", String.valueOf(newDate().getTime())); // millisecondsparams.putString("custom_timestamp", timeStamp()); // ISO 8061params.putString("custom_user_id", ui);
mFirebaseAnalytics.logEvent("video_start", params);
ormFirebaseAnalytics.logEvent("video_stop", params);

However, the implemented video progress and video completeBundle is different in their parameters variable to reduce conflicts with others parameters because it is running in the background. This Bundle is composed of:

BundleprogressParams = newBundle();
progressParams.putLong("video_duration", vd);
progressParams.putLong("video_current_time", vct[0]);
progressParams.putString("video_percent", milestone + "%");
progressParams.putString("video_status", vs[0]);
progressParams.putString("video_provider", vp);
progressParams.putString("video_title", vt);
progressParams.putString("video_url", vu);
// Global parametersprogressParams.putString("event_type", et[0]);
progressParams.putString("button_text", desc == null ? click : desc);
progressParams.putString("resource_id", resourceId);
progressParams.putString("e_timestamp", String.valueOf(newDate().getTime())); // millisecondsprogressParams.putString("custom_timestamp", timeStamp()); // ISO 8061progressParams.putString("custom_user_id", ui);
mFirebaseAnalytics.logEvent("video_progress", progressParams);
ormFirebaseAnalytics.logEvent("video_complete", progressParams);

GTM Setup

I initially considered the use of GTM as part of the implementation but it is not recommended because the limitations to setup tags versus the Web implementation, and the "ridiculous" requirement of download from the container the latest live version published and upload it into the App directory /app/src/assets/containers every time a publish of the container is made.

Reference Documentation

=====

Copyright 2022-2024 | Arturo Santiago-Rivera | MIT License

About

Mobile Analytics Implementation Playground

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages