diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml index d401edc866..c0e344c920 100644 --- a/app/AndroidManifest.xml +++ b/app/AndroidManifest.xml @@ -229,6 +229,12 @@ android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" /> + diff --git a/app/build.gradle b/app/build.gradle index f32e379890..57f3f5cf76 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -194,6 +194,7 @@ ext { 'ycVSQSeO2glc6XZZ+CJudAPXe8iFWLQp3kBBnBmVcBXCOQFO7aLgQMv4nqKZsLW0' + 'HaAJkjpnc165Os+aYwIDAQAB') GOOGLE_SERVICES_API_KEY = loadProp('GOOGLE_SERVICES_API_KEY', '') + FIREBASE_ENABLED = !GOOGLE_SERVICES_API_KEY.toString().isEmpty() GOOGLE_CLOUD_PROJECT_NUMBER = loadProp('GOOGLE_CLOUD_PROJECT_NUMBER', -1L) QA_BETA_APP_ID = '' STANDALONE_APP_ID = '' @@ -315,6 +316,8 @@ android { buildConfigField 'String', 'FIREBASE_DATABASE_URL', "\"${project.ext.FIREBASE_DATABASE_URL}\"" buildConfigField 'String', 'CCC_HOST', "\"connect.dimagi.com\"" buildConfigField 'long', 'GOOGLE_CLOUD_PROJECT_NUMBER', "${project.ext.GOOGLE_CLOUD_PROJECT_NUMBER}" + buildConfigField 'boolean', 'FIREBASE_ENABLED', "${project.ext.FIREBASE_ENABLED}" + manifestPlaceholders["firebaseInitEnabled"] = project.ext.FIREBASE_ENABLED testInstrumentationRunner 'org.commcare.CommCareJUnitRunner' } diff --git a/app/src/org/commcare/CommCareApplication.java b/app/src/org/commcare/CommCareApplication.java index 18d598f819..f67a3f1ee2 100644 --- a/app/src/org/commcare/CommCareApplication.java +++ b/app/src/org/commcare/CommCareApplication.java @@ -119,6 +119,7 @@ import org.commcare.utils.DeviceIdentifier; import org.commcare.utils.FileUtil; import org.commcare.utils.FirebaseMessagingUtil; +import org.commcare.utils.FirebaseUtils; import org.commcare.utils.GlobalConstants; import org.commcare.utils.MarkupUtil; import org.commcare.utils.MultipleAppsUtil; @@ -222,9 +223,10 @@ public void onCreate() { turnOnStrictMode(); CommCareApplication.app = this; + CrashUtil.init(); DataChangeLogger.init(this); - if (!BuildConfig.DEBUG) { + if (FirebaseUtils.isFirebaseEnabled()) { FirebasePerformance.getInstance().setPerformanceCollectionEnabled(true); } @@ -440,6 +442,9 @@ public SecretKey createNewSymmetricKey() { } synchronized public FirebaseAnalytics getAnalyticsInstance() { + if (!FirebaseUtils.isFirebaseEnabled()) { + return null; + } if (analyticsInstance == null) { analyticsInstance = FirebaseAnalytics.getInstance(this); } diff --git a/app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java b/app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java index 4e48993d37..9d9f580b2c 100644 --- a/app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java +++ b/app/src/org/commcare/fragments/personalId/PersonalIdPhoneVerificationFragment.java @@ -32,6 +32,7 @@ import org.commcare.dalvik.databinding.ScreenPersonalidPhoneVerifyBinding; import org.commcare.google.services.analytics.FirebaseAnalyticsUtil; import org.commcare.util.LogTypes; +import org.commcare.utils.FirebaseUtils; import org.commcare.utils.KeyboardHelper; import org.commcare.utils.OtpErrorType; import org.commcare.utils.OtpManager; @@ -151,8 +152,10 @@ public void onPersonalIdApiFailure( } }; + // Use fallback option when Firebase is not configured. // The last OTP method may be Twilio (via PersonalID) after restoring this fragment. - Boolean useOtpFallback = SMS_METHOD_PERSONAL_ID.equalsIgnoreCase(lastOtpMethod); + Boolean useOtpFallback = SMS_METHOD_PERSONAL_ID.equalsIgnoreCase(lastOtpMethod) || + !FirebaseUtils.isFirebaseEnabled(); setupOtpManager(useOtpFallback); } diff --git a/app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt b/app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt index 1af71aa0e3..7b3e80fa21 100644 --- a/app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt +++ b/app/src/org/commcare/google/services/analytics/CCPerfMonitoring.kt @@ -4,6 +4,7 @@ import com.google.firebase.perf.FirebasePerformance import com.google.firebase.perf.metrics.Trace import org.apache.commons.io.FilenameUtils import org.commcare.android.logging.ReportingUtils +import org.commcare.utils.FirebaseUtils import org.javarosa.core.services.Logger object CCPerfMonitoring { @@ -34,6 +35,9 @@ object CCPerfMonitoring { fun startTracing(traceName: String): Trace? { + if (!FirebaseUtils.isFirebaseEnabled) { + return null + } try { val trace = FirebasePerformance.getInstance().newTrace(traceName) trace.putAttribute(CCAnalyticsParam.CCHQ_DOMAIN, ReportingUtils.getDomain()) diff --git a/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java b/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java index f59e2b1ab4..33a544855e 100644 --- a/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java +++ b/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java @@ -21,6 +21,7 @@ import org.commcare.preferences.MainConfigurablePreferences; import org.commcare.suite.model.OfflineUserRestore; import org.commcare.util.EncryptionUtils; +import org.commcare.utils.FirebaseUtils; import org.commcare.utils.FormUploadResult; import org.javarosa.core.services.Logger; @@ -78,11 +79,14 @@ private static void reportEvent(String eventName, String[] paramKeys, String[] p } private static void reportEvent(String eventName, Bundle params) { - if (analyticsDisabled()) { + if (!FirebaseUtils.isAnalyticsEnabled()) { return; } FirebaseAnalytics analyticsInstance = CommCareApplication.instance().getAnalyticsInstance(); + if (analyticsInstance == null) { + return; + } setUserProperties(analyticsInstance); analyticsInstance.logEvent(eventName, params); } @@ -357,14 +361,14 @@ public static void reportInAppUpdateResult(boolean result, String failureReason) } public static void reportFeatureUsage(String feature) { - if (analyticsDisabled()) { + if (!FirebaseUtils.isAnalyticsEnabled()) { return; } reportEvent(CCAnalyticsEvent.FEATURE_USAGE, FirebaseAnalytics.Param.ITEM_CATEGORY, feature); } private static void reportFeatureUsage(String feature, String mode) { - if (analyticsDisabled()) { + if (!FirebaseUtils.isAnalyticsEnabled()) { return; } @@ -410,10 +414,6 @@ public static void reportTimedSession( } } - private static boolean analyticsDisabled() { - return !MainConfigurablePreferences.isAnalyticsEnabled(); - } - private static boolean rateLimitReporting(double percentOfEventsToReport) { return Math.random() < percentOfEventsToReport; } @@ -767,6 +767,9 @@ public static void flagPersonalIDDemoUser(Boolean isPersonalIDDemoUser) { } FirebaseAnalytics analyticsInstance = CommCareApplication.instance().getAnalyticsInstance(); + if (analyticsInstance == null) { + return; + } analyticsInstance.setUserProperty( CCAnalyticsParam.IS_PERSONAL_ID_DEMO_USER, String.valueOf(isPersonalIDDemoUser) diff --git a/app/src/org/commcare/utils/CrashUtil.java b/app/src/org/commcare/utils/CrashUtil.java index 2b6eee6ee3..110e29d4bd 100644 --- a/app/src/org/commcare/utils/CrashUtil.java +++ b/app/src/org/commcare/utils/CrashUtil.java @@ -1,7 +1,6 @@ package org.commcare.utils; import org.commcare.android.logging.ReportingUtils; -import org.commcare.dalvik.BuildConfig; import com.google.firebase.crashlytics.FirebaseCrashlytics; @@ -17,7 +16,7 @@ public class CrashUtil { private static final String DOMAIN = "domain"; private static final String DEVICE_ID = "device_id"; - private static boolean crashlyticsEnabled = BuildConfig.USE_CRASHLYTICS; + private static boolean crashlyticsEnabled = FirebaseUtils.isCrashlyticsEnabled(); public static void reportException(Throwable e) { if (crashlyticsEnabled) { diff --git a/app/src/org/commcare/utils/FirebaseMessagingUtil.java b/app/src/org/commcare/utils/FirebaseMessagingUtil.java index 02ac0763f6..c3815e60d4 100644 --- a/app/src/org/commcare/utils/FirebaseMessagingUtil.java +++ b/app/src/org/commcare/utils/FirebaseMessagingUtil.java @@ -42,6 +42,7 @@ import org.commcare.services.FCMMessageData; import org.commcare.sync.FirebaseMessagingDataSyncer; import org.commcare.util.LogTypes; +import org.commcare.utils.FirebaseUtils; import org.javarosa.core.services.Logger; import java.util.HashMap; @@ -96,12 +97,16 @@ public static void updateFCMToken(String newToken) { sharedPreferences.edit().putLong(FCM_TOKEN_TIME, System.currentTimeMillis()).apply(); } + /** + * This verification only runs if Firebase is available and configured + */ public static void verifyToken() { - // TODO: Enable FCM in debug mode - if (!BuildConfig.DEBUG) { - // Retrieve the current Firebase Cloud Messaging (FCM) registration token - FirebaseMessaging.getInstance().getToken().addOnCompleteListener(handleFCMTokenRetrieval()); + if (!FirebaseUtils.isFirebaseEnabled()) { + return; } + + // Retrieve the current Firebase Cloud Messaging (FCM) registration token + FirebaseMessaging.getInstance().getToken().addOnCompleteListener(handleFCMTokenRetrieval()); } private static OnCompleteListener handleFCMTokenRetrieval() { diff --git a/app/src/org/commcare/utils/FirebaseUtils.kt b/app/src/org/commcare/utils/FirebaseUtils.kt new file mode 100644 index 0000000000..0a317603d3 --- /dev/null +++ b/app/src/org/commcare/utils/FirebaseUtils.kt @@ -0,0 +1,22 @@ +package org.commcare.utils + +import org.commcare.dalvik.BuildConfig +import org.commcare.preferences.MainConfigurablePreferences + +object FirebaseUtils { + @JvmStatic + val isFirebaseEnabled: Boolean + /** + * Returns whether Firebase services are available and configured. Firebase is disabled in debug mode + * When false, all Firebase API calls must be skipped. + */ + get() = !BuildConfig.DEBUG && BuildConfig.FIREBASE_ENABLED + + @JvmStatic + val isCrashlyticsEnabled: Boolean + get() = isFirebaseEnabled && BuildConfig.USE_CRASHLYTICS + + @JvmStatic + val isAnalyticsEnabled: Boolean + get() = isFirebaseEnabled && MainConfigurablePreferences.isAnalyticsEnabled() +} diff --git a/app/unit-tests/src/org/commcare/android/tests/firebase/FirebaseUtilsTest.java b/app/unit-tests/src/org/commcare/android/tests/firebase/FirebaseUtilsTest.java new file mode 100644 index 0000000000..11d8922456 --- /dev/null +++ b/app/unit-tests/src/org/commcare/android/tests/firebase/FirebaseUtilsTest.java @@ -0,0 +1,21 @@ +package org.commcare.android.tests.firebase; + +import static org.junit.Assert.assertEquals; + +import org.commcare.dalvik.BuildConfig; +import org.commcare.utils.FirebaseUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import org.commcare.CommCareTestApplication; +import org.robolectric.annotation.Config; + +@Config(application = CommCareTestApplication.class) +@RunWith(AndroidJUnit4.class) +public class FirebaseUtilsTest { + + @Test + public void testIsFirebaseEnabled_matchesBuildConfig() { + assertEquals(BuildConfig.FIREBASE_ENABLED, FirebaseUtils.isFirebaseEnabled()); + } +}