AnalyticsMobile

Mobile Analytics Implementation Playground

View on GitHub

AnalyticsMobile

AnalyticsMobile Releases-Tags    MIT 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:

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

Figure 1 - AnalyticsMobile App Screenshot

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

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 repositories
         google()  // Google's Maven repository
         mavenCentral()  // Maven Central repository
       }
        
       dependencies {
         // Add the dependency for the Google services Gradle plugin
         classpath '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 plugin
         id 'com.google.gms.google-services'
     }
        
     ...
        
     dependencies {
         ...
        
         // Firebase: Import the Firebase BoM
         implementation platform('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 services
         implementation '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:

     public class MainActivity extends AppCompatActivity {
         ...
            
         // Declare FirebaseAnalytics
         private FirebaseAnalytics mFirebaseAnalytics;
            
         ...
     }
    
  4. Initialize the FirebaseAnalytics in the onCreate() method:

     protected void onCreate() {
         ...
            
         // 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");
or
mFirebaseAnalytics.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 Action Event Type Parameters GA4 Scope GA4 Definitions
Screen View SCREEN_VIEW content tool SCREEN_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 In LOGIN user interaction method Event Predefined
Sign In login_error content tool error_message
toast_impression
Event
Event
Dimension
Dimension
Email GENERATE_LEAD user interaction contact_method
CURRENCY
VALUE
Event
Event
Event
Dimension
Predefined
Predefined
Outbound Link outbound_link user interaction link_id
link_url
link_text
outbound
Event
Event
Event
Event
Predefined
Predefined
Predefined
Predefined
Phone GENERATE_LEAD user interaction contact_method
CURRENCY
VALUE
Event
Event
Event
Dimension
Predefined
Predefined
Purchase PURCHASE user interaction TRANSACTION_ID
AFFILIATION
CURRENCY
VALUE
TAX
SHIPPING
COUPON
ITEMS
Event
Event
Event
Event
Event
Event
Event
Event
Predefined
Predefined
Predefined
Predefined
Predefined
Predefined
Predefined
Predefined
Search search_dialog_opened user interaction      
- ok SEARCH user interaction search_term Event Predefined
- cancel search_dialog_closed user interaction      
Search search_error content tool error_message
toast_impression
Event
Event
Dimension
Dimension
Video video_start user interaction video_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 video_progress content tool video_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 video_complete content tool video_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 Playing video_stop user interaction video_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 Out logout user interaction      
Sign Out logout_error content tool error_message
toast_impression
Event
Event
Dimension
Dimension

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

Global Parameters GA4 Scope GA4 Definitions
event_type Event Dimension
button_text Event Dimension
resource_id Event Dimension
e_timestamp (milliseconds) Event Dimension
custom_timestamp (ISO 8601) Event Dimension
custom_user_id Event Dimension
logged_in (user property) User Dimension
user_id (user property) User Predefined

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:

    Bundle params = new Bundle();
    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 Parameters
    params.putString("event_type", et);
    params.putString("button_text", click);
    params.putString("resource_id", resourceId);
    params.putString("e_timestamp", new Date().getTime()); // milliseconds
    params.putString("custom_timestamp", timeStamp()); // ISO 8061
    params.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.

    Bundle params = new Bundle();
    
    // login
    params.putString(FirebaseAnalytics.Param.METHOD, "google");
    
    // generate_lead
    params.putString("contact_method", cm);
    params.putString(FirebaseAnalytics.Param.CURRENCY, cc);
    params.putDouble(FirebaseAnalytics.Param.VALUE, ev);
    
    // outbound_link
    params.putString("link_url", url);
    params.putString("link_text", text);
    params.putString("link_id", resourceId);
    params.putBoolean("outbound", ol);
    
    // search
    params.putString(FirebaseAnalytics.Param.SEARCH_TERM, st);
    
    // Global Parameters
    params.putString("event_type", et[0]);
    params.putString("button_text", desc == null ? click : desc);
    params.putString("resource_id", resourceId);
    params.putString("e_timestamp", String.valueOf(new Date().getTime())); // milliseconds
    params.putString("custom_timestamp", timeStamp()); // ISO 8061
    params.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 event Bundle is composed of:

    // Item parameters
    Bundle item1 = new Bundle();
    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);

    Bundle item2 = new Bundle();
    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 parameters
    Bundle params = new Bundle();
    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, new Parcelable[]{item1, item2});

    // Global parameters
    params.putString("event_type", et[0]);
    params.putString("button_text", desc == null ? click : desc);
    params.putString("resource_id", resourceId);
    params.putString("e_timestamp", String.valueOf(new Date().getTime())); // milliseconds
    params.putString("custom_timestamp", timeStamp()); // ISO 8061
    params.putString("custom_user_id", ui);

    mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.PURCHASE, params);

Error Events

The implemented error events Bundle is composed of:

    Bundle params = new Bundle();
    params.putString("error_message", message);
    params.putBoolean("toast_impression", true);

    // Global parameters
    params.putString("event_type", et[0]);
    params.putString("button_text", desc == null ? click : desc);
    params.putString("resource_id", resourceId);
    params.putString("e_timestamp", String.valueOf(new Date().getTime())); // milliseconds
    params.putString("custom_timestamp", timeStamp()); // ISO 8061
    params.putString("custom_user_id", ui);
    
    mFirebaseAnalytics.logEvent(EVENT_NAME, params);

Video Events

The implemented video start and video stop Bundle is composed of:

    Bundle params = new Bundle();
    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 parameters
    params.putString("event_type", et[0]);
    params.putString("button_text", desc == null ? click : desc);
    params.putString("resource_id", resourceId);
    params.putString("e_timestamp", String.valueOf(new Date().getTime())); // milliseconds
    params.putString("custom_timestamp", timeStamp()); // ISO 8061
    params.putString("custom_user_id", ui);
    
    mFirebaseAnalytics.logEvent("video_start", params);
    or
    mFirebaseAnalytics.logEvent("video_stop", params);

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

    Bundle progressParams = new Bundle();
    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 parameters
    progressParams.putString("event_type", et[0]);
    progressParams.putString("button_text", desc == null ? click : desc);
    progressParams.putString("resource_id", resourceId);
    progressParams.putString("e_timestamp", String.valueOf(new Date().getTime())); // milliseconds
    progressParams.putString("custom_timestamp", timeStamp()); // ISO 8061
    progressParams.putString("custom_user_id", ui);
    
    mFirebaseAnalytics.logEvent("video_progress", progressParams);
    or
    mFirebaseAnalytics.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-2023 Arturo Santiago-Rivera MIT License