DbTradeAlert for Android: Integrate Google Play Services

First post in this series: Introduction to DbTradeAlert

Previous post: Enter the Play Store


Google offers various services one might want to add to an app. For example:

  • Firebase Analytics provides insights about an app’s usage
  • Firebase Crash Reporting provides insights about app stability
  • Firebase Remote Config allows to change an app’s behavior and look without performing an update; it also allows for example a/b testing
  • Google AdMob allows to monetize an app

Firebase Analytics replaces Google Analytics for mobile apps on both Android and iOS. This was formally declared at Google I/O 2016 just 3 months ago and many examples still show the old way of doing things. At least for now Google Analytics is still used for web apps which adds to the confusion.

Users can opt out of tracking

Users can opt out of tracking

This post lays the foundation for integrating Google Play Services and adds tracking of user behaviour through Firebase Analytics. A later post will add Google AdMob and Firebase Crash Reporting to the app.

In this case the first step is to add app flavors because that enables providing multiple versions of an app – like a paid and a free version. Regarding that “flavors” terminology:

  • Flavors of an app differ by functionality like a paid and a free flavor
  • Build types control the packaging of an app like a debug and a release build
  • Each combination of a flavor and a build type is a variant

As you can have multiple flavor dimensions – like paid vs. free and normal vs. mock – things can get complicated. And if you don’t define flavors you get a default one with no name.

1. Create App Flavors

DbTradeAlert will have three flavors:

  1. The current flavor – called “naked” from here on because Gradle doesn’t like “default”
  2. A playStore flavor which adds tracking
  3. A withAds flavor which builds on the playStore flavor  and adds advertisements – to be added in a later post

The naked flavor serves as a reference to debug flavor specific problems and will not be available in the Play Store. The playStore flavor will fulfill my curiosity about the feedback one gets from the Play Store. In addition to that I’m interested in usage scenarios and add tracking for them.

App flavors are controlled by Gradle. One reason is that Java has no preprocessor and therefore no conditional compiles. The general steps to create a flavor are:

  1. Add the flavors to build.gradle – only specify what is flavor specific to override defaultConfig settings
  2. Keep flavor specific functionality in separate classes in a flavor specific directory structure
  3. Handle flavor specific resources similar to functionality

A common misconception is that flavors override or overwrite methods or classes of the main directory structure. They don’t but instead are added to the code in main creating so-called source sets. So while classes in a flavor can extend those in main you cannot have two casses with the same name.

In contrast to that resources are merged. That means for example a flavor specific resource will replace a general resource.

I read “somewhere” that Gradle has a problem if flavor names start with a capital letter. As Gradle seems to be a moving target with bugs and functionality coming and going anyway I’m playing it safe and use only lowercase flavor names.

1.1. Add the playStore Flavor to build.gradle

The first step is to add the productFlavors closure to app/build.gradle (Module: app):

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "de.dbremes.dbtradealert"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        archivesBaseName = "${parent.name}-${android.defaultConfig.versionName}"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    productFlavors {
        naked 
        playStore {
            applicationId = "${android.defaultConfig.applicationId}.playStore"
        }
    }
}
// ...

Before adding the productFlavors closure there was an automatic default flavor. Now each flavor has to be explicitly specified.

Note that each flavor has its own applicationId. That’s required if multiple APKs will be uploaded to the Play Store.

Build both flavors to test if the changes work as expected:

  1. In Android Studio click Build | Generate Signed APK
  2. In the Generate Signed APK window, step 1:
    1. The Key store path should be set
    2. Enter the store’s and the key’s passwords and click Next
  3. In the Generate Signed APK window, step 2:
    1. Build type should default to release
    2. Flavors list should contain naked and playStore – select both
    3. Click Finish

The output path should now contain a file named “DbTradeAlert-1.0-naked-release.apk” and one named “DbTradeAlert-1.0-playStore-release.apk”. Both will be identical of course.

1.2 Create the playStore Specific Directory Structure

Implementing the tracking starts with creating the flavor specific directory structure. You can either do that manually or enlist Android Studio:

  1. In the Project pane open the app’s context menu and select New | Folder | Java Folder
  2. In the Configure Component window select “playStore” as the Target Source Set and click Finish – this will create “…\app\src\playStore\java”
  3. In the Build Variants pane switch to the playStoreDebug variant
  4. Open the app context menu again and select New | Java Class
  5. In the Choose Destination Directory window select “..\app\src\playStore\java” and click OK
  6. In the Create New Class window enter “PlayStoreHelper” as the name and click OK
  7. Add “package de.dbremes.dbtradealert” at the top of the new java class – this will result in a red squiggly line under it
  8. Select the “Move to package ‘de.dbremes.dbtradealert'” quick fix Android Studio offers
  9. If necessary choose the right destination directory – “..\app\src\playStore\java\de\dbremes\dbtradealert” – in the Choose Destination Directory window and click OK

The Project view should now list a new package named “de.dbremes.dbtradealert (playStore)” with PlayStoreHelper.java in it.

To make the app’s various versions easier to discern let’s also add flavor specific labels for the app and its main screen.

1.3 Create playStore Specific Resources

The idea is to have different names for the app’s flavors:

  • “DbTradeAlert” for the playStore flavor
  • “DbTradeAlert N” for the naked flavor
  • “DbTradeAlert A” for the withAds flavor

These names will be used for both the app and its main activity. The app’s name shows up for example in the Settings screen’s app list while the activity’s name shows up in the launcher. That’s because activities are meant to be used to start an app – I found that only later and should have used fragments instead of activities.

Both names come from “app_name” in values/strings.xml – to see it in AndroidManifest.xml click on the respective value. The flavor specific names will make use of reource merging – the default value in strings.xml will automatically be replaced by the value in the flavor specific strings.xml.

Currently the playStoreDebug variant should be selected – otherwise select it. After that:

  1. In the app context menu select New | Android resource directory
  2. In the New Resource Directory window select the Source set “playStore” and click OK

This created the directory “… DbTradeAlert\app\src\playStore\res\values”.

Now add the new resource file:

  1. In the app context menu select New | Android resource file
  2. In the New Resource File window
    1. Enter “strings.xml” as File name
    2. Select Source “set playStore”
    3. Click OK

This created a new file which shows up as “strings.xml (playStore)” in Android Studio’s Project view.

And finally create flavor specific names:

  1. Copy the element defining app_name from the old strings.xml to the new one
  2. In the old strings.xml change app_name to “DbTradeAlert N”

Whenever you build the APK from now on both its titles will depend on the selected app variant. As the other value is missing from the new strings.xml the general one will be used irrespective of the flavor being built.

To try it out build a naked variant and install it – it should show up as “DbTradeAlert N”. The playStore variants still show as “DbTradeAlert”.

2. Integrate Google Play Services

Google Play Services provide the base for many Google and Firebase services. If an app uses Google Play services it needs:

  • The google-services.json configuration file in place
  • The dependencies specified in project and app Gradle files

The playStore flavor of DbTradeAlert will use Firebase Analytics which is part of Google Play services.

First make shure your device or emulator came with Google Play store / Google APIs. It seems to be possible to get around that requirement for Firebase Analytics but I didn’t try.

After that download the .json configuration file. That requires the Google developer account created in the sections before.

To download google-services.json and place it into your project:

  1. Navigate to https://console.firebase.google.com and log in
  2. Click Add App
  3. In the popup window click Add Firebase to my Android app
  4. In the “Enter app details” step add the applicationId for the flavor you plan to upload – “de.dbremes.dbtradealert.playStore” in my case – copy it from build.gradle to avoid typos
  5. After clicking Add App a file named google-services.json is downloaded
  6. Depending on your needs copy the file either into the project’s main folder or into the flavor specific root folder – “…\app\src\playStore” in my case

Now add one line to build.gradle (Project: DbTradeAlert):

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
        classpath 'com.google.gms:google-services:3.0.0'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

Then add two lines to app/build.gradle (Module: app):

apply plugin: 'com.android.application'

android {
    // ...
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:design:23.3.0'
    compile 'com.android.support:support-v4:23.3.0'
    compile 'com.android.support:recyclerview-v7:23.3.0'
    playStoreCompile 'com.google.firebase:firebase-core:9.2.1'
}

// Remove apply plugin: 'com.google.gms.google-services' when building naked variants.
// Those have no google-services.json file and applying the plugin would produce an error:
// "File google-services.json is missing. The Google Services Plugin cannot function without it."
// Building the R class would fail too and produce "cannot resolve symbol R" errors.
apply plugin: 'com.google.gms.google-services'

“apply plugin: ‘com.google.gms.google-services'” enables Gradle to create Google Services related resources – we’ll come back to that later for the naked flavor. It’s important to add it at the bottom of build.gradle and not at the top. Also note that Firebase Analytics which are in firebase-core are only linked to the playStore flavor.

Now let Gradle sync its files and fix any errors it reports. It will complain if google-services.json isn’t in the right place and even point out newer versions of the libraries. Should Gradle report an error like “No matching client found for package name …” when building the variant the applicationId in build.gradle doesn’t match package_name in google-services.json.

Finally add google-services.json to your project’s .gitignore file:

*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
google-services.json

This ignores google-services.json files in any directory. Like the passwords you want the keys in google-services.json to stay private.

3. Implement Tracking

Finally it’s time to actually implement the flavor specific functionality. In this case that’s informing Firebase Analytics every time the user taps the Refresh button or opens a screen. The main idea is that they shouldn’t feel the need to refresh quotes manually and if they do there’s something wrong with them the app.

To confine functionality from Firebase Analytics to the playStore flavor code calling the respective APIs needs to reside in files that only the playStore flavor contains. So create a class named PlayStoreHelper in the respective package – “de.dbremes.dbtradealert (playStore)” in my case:

package de.dbremes.dbtradealert;

import android.content.Context;
import android.os.Bundle;

import com.google.firebase.analytics.FirebaseAnalytics;

public class PlayStoreHelper {

    private PlayStoreHelper() {
        // Hide default construcor
    }

    public static void reportAction(Context context, String actionTitle, int actionId) {
        Bundle bundle = new Bundle();
        bundle.putString(FirebaseAnalytics.Param.ITEM_ID, String.valueOf(actionId));
        bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, actionTitle);
        FirebaseAnalytics.getInstance(context).logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
    } // reportAction()
} // class PlayStoreHelper

And this is the call to PlayStoreHelper.reportAction() in WatchlistListActivity.onListFragmentInteraction() and onOptionsItemSelected():

// ...
public class WatchlistListActivity extends AppCompatActivity
        implements WatchlistFragment.OnListFragmentInteractionListener {
    // ...

    @Override
    public void onListFragmentInteraction(String symbol) {
        int actionId = 0;
        PlayStoreHelper.reportAction(getApplicationContext(), "Long tap security", actionId);
        Intent intent = new Intent(this, SecurityEditActivity.class);
        long securityId = dbHelper.getSecurityIdFromSymbol(symbol);
        intent.putExtra(SecurityEditActivity.SECURITY_ID_INTENT_EXTRA, securityId);
        startActivityForResult(intent, SECURITY_EDIT_REQUEST);
    } // onListFragmentInteraction()

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        Intent intent;
        int id = item.getItemId();
        PlayStoreHelper.reportAction(
                getApplicationContext(), item.getTitle().toString(), id);
        switch (id) {
            // ...
        }
    }
    // ...
} // class WatchlistListActivity 

DbTradeAlert logs each user interaction as a Firebase SELECT_CONTENT event specifying CONTENT_TYPE and ITEM_ID. You are free to come up with your own events and parameters but the reports in Firebase console currently won’t show them. They actually only report CONTENT_TYPE for the SELECT_CONTENT event but that’s OK for now.

In a real-world scenario the marketing people would want to slice and dice the raw data anyway either in BigQuery or in a spread sheet after downloading them. Note that you cannot download raw data from Firebase directly but have to go through BigQuery which requires the pay-as-you-go Blaze plan.

Another important fact is that data will take several hours to show up in the Firebase website. For that reason it’s advised to use logcat for a first check. Enable local Firebase logging with:
adb shell setprop log.tag.FA VERBOSE
adb shell setprop log.tag.FA-SVC VERBOSE

Before running the new variant it’s a good idea to delete the old app from the device to avoid confusion. After that run the app in debug mode and tap the Refresh button. The logcat output from Firebase should look like this – some identifiers were replaced with “nnnnn” to protect the innocent:

... de.dbremes.dbtradealert.playStore I/FA: App measurement is starting up, version: 9256
...
... de.dbremes.dbtradealert.playStore V/FA: Collection enabled
... de.dbremes.dbtradealert.playStore V/FA: App package, google app id: de.dbremes.dbtradealert.playStore, 1:nnnnn:android:nnnnn
... de.dbremes.dbtradealert.playStore V/FA: Registered activity lifecycle callback
... de.dbremes.dbtradealert.playStore I/FirebaseInitProvider: FirebaseApp initialization successful
... de.dbremes.dbtradealert.playStore V/FA: State of service unknown
... de.dbremes.dbtradealert.playStore V/FA: Checking service availability
... de.dbremes.dbtradealert.playStore V/FA: Setting useService: true
... de.dbremes.dbtradealert.playStore V/FA: Using measurement service
... de.dbremes.dbtradealert.playStore V/FA: Connecting to remote service
... de.dbremes.dbtradealert.playStore V/FA: onActivityCreated
...
... de.dbremes.dbtradealert.playStore D/FA: Logging event (FE): _s, Bundle[{_o=auto}]
... de.dbremes.dbtradealert.playStore V/FA: Using measurement service
... de.dbremes.dbtradealert.playStore V/FA: Connecting to remote service
... de.dbremes.dbtradealert.playStore D/FA: Connected to remote service
... de.dbremes.dbtradealert.playStore V/FA: Processing queued up service tasks: 1
... de.dbremes.dbtradealert.playStore D/FA: Logging event (FE): select_content, Bundle[{_o=app, content_type=Refresh, item_id=2131624116}]
... de.dbremes.dbtradealert.playStore V/FA: Inactivity, disconnecting from AppMeasurementService

The next day go to https://console.firebase.google.com, select your app, and click Analytics. That will show the Dashboard with an overview of Firebase reporting.

To drill down click the Events tab and select “select_content” from the list of event types. The content graph shows the user interactions.

select_content statistics

select_content statistics

Back to Android Studio. When you switch to the nakedDebug variant it will report errors because PlayStoreHelper is missing from this source set. To fix that:

  1. Create a flavor specific directory structure similar to the previous one
  2. Add a new Java class named PlayStoreHelper to the respective package – “de.dbremes.dbtradealert (naked)” in my case
  3. Hide the default constructor and implement an empty static method reportAction()

Now Android Studio will still complain about a missing google-services.json file and an unresolved R symbol. To remedy that comment the line applying the google-services plugin in build.gradle out – I haven’t found a way to have Gradle apply plugins flavor specific.

After that let Gradle resync, remove the currently installed variant from the device, and let Android Studio install the new one. When you tap the Refresh button there should be no logcat activity pointing to Firebase Analytics.

4. Let Users Opt out of Tracking

Now that Firebase Analytics was added successfully it’s time to deactivate it – well, actually to allow users to opt out like the screenshot at the post’s beginning showed. This is actually a bit develish as the option’s setting of course has to be tracked. Implementing this consists of:

  1. Add a Firebase user property to track the option’s value for each user
  2. Add settings to control DbTradeAlert’s tracking
  3. Add code to enable / disable Firebase Analytics = control Google’s tracking
  4. Add code to set the Firebase user property
  5. Add code to check the setting before tracking anything

All code will be specific to the playStore flavor with only a placeholder implementation in naked’s PlayStoreHelper.

4.1 Add a Firebase User Property

To add a Firebase user property

  1. Go to https://console.firebase.google.com and log in
  2. Navigate to your app
  3. Navigate to its Analytics pane
  4. Navigate to the User Properties tab
  5. Add a new user property specifying a name (up to 25 characters) and a description (up to 150 characters)

Note that the name is case sensitive and it will take a few hours for user property data to show up. And maybe even more important: you cannot rename or delete a user property and they are limited to 25 per app.

Please note also that you need at least 10 users for data from user properties showing up in Firebase Analytics. That makes testing a bit harder as filters and audiences won’t be functional without that data.

4.2 Add Settings to Control DbTradeAlert’s Tracking

There are no built-in options to add a flavor specific setting to preferences.xml – unfortunately preference files don’t provide merging options or access to flavor settings like AndroidManifest.xml. In any case having an almost identical preference file for each flavor is usually the worst idea.

DbTradeAlert gets lucky here because it only needs to remove a preference at runtime. If an app has to add a preference back getting the order right can be tricky.

The isTrackingEnabled preference gets removed in SettingsFragment.onCreate():

package de.dbremes.dbtradealert;

import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.util.Log;

public class SettingsFragment extends PreferenceFragment {
    private static final String CLASS_NAME = "SettingsFragment";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        if (BuildConfig.FLAVOR.equals("naked")) {
            CheckBoxPreference trackingPreference
                    = (CheckBoxPreference) findPreference("tracking_preference");
            if (trackingPreference != null) {
                PreferenceScreen ps = getPreferenceScreen();
                ps.removePreference(trackingPreference);
                Log.d(CLASS_NAME, "onCreate(): tracking_preference removed");
            } else {
                Log.v(CLASS_NAME, "onCreate(): tracking_preference already removed");
            }
        }
    } // onCreate()
} // class SettingsFragment

This code uses the automatically generated BuildConfig class to determine the app’s flavor. Note that getPreferenceScreen() is not available in SettingsActivity and has been deprecated there in API level 11.

4.3 Add Code to Control Google’s Tracking and to Set the Firebase User Property

Flavor specific code can now use the infrastructure built in the previous sections. The first step is to extend PlayStoreHelper for the playStore flavor:

// ...

public class PlayStoreHelper {
    public final static String IS_TRACKING_ENABLED_USERPROPERY = "isTrackingEnabled";

    private PlayStoreHelper() {
        // Hide default construcor
    }

    public static void reportAction(
            @NotNull Context context, @NotNull String actionTitle, int actionId) {
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
        boolean isTrackingEnabled = sp.getBoolean(SettingsActivity.TRACKING_PREFERENCE_KEY, true);
        if (isTrackingEnabled) {
            Bundle bundle = new Bundle();
            bundle.putString(FirebaseAnalytics.Param.ITEM_ID, String.valueOf(actionId));
            bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, actionTitle);
            FirebaseAnalytics.getInstance(context).logEvent(
                    FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
        }
    } // reportAction()

    public static void setBooleanUserProperty(
            @NotNull Context context, @NotNull String propertyName, boolean propertyValue) {
        FirebaseAnalytics.getInstance(context).setUserProperty(
                propertyName, String.valueOf(propertyValue));
        if (propertyName.equals(IS_TRACKING_ENABLED_USERPROPERY)) {
            FirebaseAnalytics.getInstance(context).setAnalyticsCollectionEnabled(propertyValue);
        }
    } // setBooleanUserProperty()
} // class PlayStoreHelper

That code is used by SettingsActivity.onSharedPreferenceChanged() – nothing special here:

// ...

public class SettingsActivity extends AppCompatActivity
        implements SharedPreferences.OnSharedPreferenceChangeListener {
    public static final String TRACKING_PREFERENCE_KEY = "tracking_preference";
    // ...

    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        final String METHOD_NAME = "onSharedPreferenceChanged";
        Log.d(CLASS_NAME, METHOD_NAME + "(): key = " + key);
        switch (key) {
            case "auto_refresh_preference": {
                Intent intent = new Intent(this, QuoteRefreshScheduler.class);
                sendBroadcast(intent);
                break;
            }
            case BUSINESS_DAYS_PREFERENCE_KEY:
            case BUSINESS_HOURS_PREFERENCE_KEY:
                setBusinessTimesPreferenceSummary(key);
                break;
            case TRACKING_PREFERENCE_KEY: {
                boolean isEnabled = sharedPreferences.getBoolean(key, true);
                Context context = getApplicationContext();
                PlayStoreHelper.setBooleanUserProperty(
                        context, PlayStoreHelper.IS_TRACKING_ENABLED_USERPROPERY, isEnabled);
                break;
            }
            default:
                Log.e(CLASS_NAME, METHOD_NAME + "(): Unexpected key = " + key);
                break;
        }
    } // onSharedPreferenceChanged()

} // class SettingsActivity

A quick test:

  1. Run the playStoreDebug variant
  2. Tapping the Refresh button should yield the same log messages as before
  3. Open the Settings screen and opt out of tracking – logcat shows:
    ... de.dbremes.dbtradealert.playStore D/FA: Setting user property (FE): isTrackingEnabled, false
    ... de.dbremes.dbtradealert.playStore D/FA: Setting app measurement enabled (FE): false
  4. Open any screen – still local FA messages but nothing is tracked

The final step is to complete the naked flavor’s PlayStoreHelper, test if this flavor still works, and that the tracking option is missing from it’s Settings screen.

4.4 Track additional values

I also added user properties to track the number of reminders, securities, and watchlists people use. DbHelper got a new method for that:

// ...

public class DbHelper extends SQLiteOpenHelper {
    // ...

    public long getRecordCount(String tableName) {
        long result = -1;
        SQLiteDatabase db = this.getReadableDatabase();
        result = DatabaseUtils.queryNumEntries(db, tableName);
        Log.v(CLASS_NAME, tableName + " table has " + result + " records");
        return result;
    } // getRecordCount()

    // ...
} // class DbHelper 

This method is called from PlayStoreHelper which gets a new method too and defines the names of those new user properties:

// ...

public class PlayStoreHelper {
    private static final String CLASS_NAME = "PlayStoreHelper";
    public final static String IS_TRACKING_ENABLED_USERPROPERTY = "isTrackingEnabled";
    public final static String REMINDER_COUNT_USERPROPERTY = "reminderCount";
    public final static String SECURITY_COUNT_USERPROPERTY = "securityCount";
    public final static String WATCHLIST_COUNT_USERPROPERTY = "watchlistCount";

    // ...

    public static void setLongUserProperty(
            @NotNull Context context, @NotNull String propertyName, long propertyValue) {
        FirebaseAnalytics.getInstance(context).setUserProperty(
                propertyName, String.valueOf(propertyValue));
        Log.v(CLASS_NAME, String.format("%s(): %s=%s; %s=%d", "setLongUserProperty",
                "propertyName", propertyName, "propertyValue", propertyValue));
    } // setLongUserProperty()
} // class PlayStoreHelper

The PlayStoreHelper method is called from WatchlistListActivity.onActivityResult() when any of the management screens returns:

// ...

public class WatchlistListActivity extends AppCompatActivity
        implements WatchlistFragment.OnListFragmentInteractionListener {
    // ...

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        final String methodName = "onActivityResult";
        Context context = getApplicationContext();
        long recordCount;
        switch (requestCode) {
            case REMINDERS_MANAGEMENT_REQUEST:
                recordCount = this.dbHelper.getRecordCount(ReminderContract.Reminder.TABLE);
                PlayStoreHelper.setLongUserProperty(
                        context, PlayStoreHelper.REMINDER_COUNT_USERPROPERTY, recordCount);
                // Nothing else to do
                break;
            case WATCHLISTS_MANAGEMENT_REQUEST:
                // Even if user tapped Cancel in Manage Watchlists screen he may have OK'd
                // changes in Edit Watchlist screen
                watchlistListPagerAdapter.notifyDataSetChanged();
                recordCount = this.dbHelper.getRecordCount(WatchlistContract.Watchlist.TABLE);
                PlayStoreHelper.setLongUserProperty(
                        context, PlayStoreHelper.WATCHLIST_COUNT_USERPROPERTY, recordCount);
                break;
            case SECURITIES_MANAGEMENT_REQUEST:
                recordCount = this.dbHelper.getRecordCount(SecurityContract.Security.TABLE);
                PlayStoreHelper.setLongUserProperty(
                        context, PlayStoreHelper.SECURITY_COUNT_USERPROPERTY, recordCount);
            case SECURITY_EDIT_REQUEST:
                if (resultCode == RESULT_OK) {
                    refreshAllWatchlists();
                }
                break;
            default:
                Log.e(CLASS_NAME, String.format("%s(): unexpected requestCode = %d",
                        methodName, requestCode));
                break;
        }
        super.onActivityResult(requestCode, resultCode, data);
    } // onActivityResult()

    // ...
} // class WatchlistListActivity

The last step is to add the new user properties in the Firebase console like the previous sections did.

To test the new feature:

  1. Run the playStoreDebug variant
  2. Enable tracking in the Settings screen again
  3. Open the 3 management screens – each should produce a log entry showing that the respective Firebase user property was updated

Final note: if Firebase cannot connect to the mothership for some reason it will silently fail and retry after a few seconds. This is what you’ll see in logcat:

... de.dbremes.dbtradealert.playStore D/FirebaseInstanceId: background sync failed: TIMEOUT, retry in 10s
... de.dbremes.dbtradealert.playStore D/FirebaseInstanceId: background sync failed: SERVICE_NOT_AVAILABLE, retry in 20s

Next post: Add Google AdMob

Additional Resources

Advertisements
This entry was posted in Uncategorized and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s