diff --git a/OmetriaSDK/build.gradle.kts b/OmetriaSDK/build.gradle.kts index 5eb9e97..f7232b3 100644 --- a/OmetriaSDK/build.gradle.kts +++ b/OmetriaSDK/build.gradle.kts @@ -1,4 +1,4 @@ -val versionName = "1.6.1" +val versionName = "1.6.2" plugins { id("com.android.library") diff --git a/OmetriaSDK/src/main/AndroidManifest.xml b/OmetriaSDK/src/main/AndroidManifest.xml index 960021d..f5ebb42 100644 --- a/OmetriaSDK/src/main/AndroidManifest.xml +++ b/OmetriaSDK/src/main/AndroidManifest.xml @@ -8,5 +8,17 @@ android:usesCleartextTraffic="true" tools:targetApi="m"> + + + + + + + \ No newline at end of file diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/LocalCache.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/LocalCache.kt index 04d67dc..0a128ef 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/LocalCache.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/LocalCache.kt @@ -20,6 +20,7 @@ private const val ARE_NOTIFICATIONS_ENABLED_KEY = "ARE_NOTIFICATIONS_ENABLED_KEY private const val IS_FIRST_PERMISSION_UPDATE_EVENT_KEY = "IS_FIRST_PERMISSION_UPDATE_EVENT_KEY" private const val JSON_ARRAY = "[]" private const val SDK_VERSION_RN_KEY = "SDK_VERSION_RN_KEY" +private const val API_TOKEN_KEY = "API_TOKEN_KEY" internal class LocalCache(private val context: Context) { @@ -165,4 +166,12 @@ internal class LocalCache(private val context: Context) { fun getSdkVersionRN(): String? { return localCacheEncryptedPreferences.getString(SDK_VERSION_RN_KEY, null) } + + fun saveApiToken(apiToken: String) { + localCacheEncryptedPreferences.edit().putString(API_TOKEN_KEY, apiToken).apply() + } + + fun getApiToken(): String? { + return localCacheEncryptedPreferences.getString(API_TOKEN_KEY, null) + } } \ No newline at end of file diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/Ometria.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/Ometria.kt index d7005f4..a64bbf2 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/Ometria.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/Ometria.kt @@ -2,6 +2,7 @@ package com.android.ometriasdk.core import android.app.Application import android.app.Notification.COLOR_DEFAULT +import android.content.Context import android.content.Intent import android.net.Uri import android.webkit.URLUtil @@ -39,8 +40,10 @@ import com.android.ometriasdk.notification.NotificationHandler import com.android.ometriasdk.notification.OMETRIA_CHANNEL_NAME import com.android.ometriasdk.notification.OmetriaNotification import com.android.ometriasdk.notification.OmetriaNotificationInteractionHandler +import com.google.android.gms.tasks.OnCompleteListener +import com.google.firebase.messaging.FirebaseMessaging import com.google.firebase.messaging.RemoteMessage -import java.util.* +import java.util.UUID /** * The primary class that allows instantiating and integrating Ometria in your application @@ -126,15 +129,49 @@ class Ometria private constructor() : OmetriaNotificationInteractionHandler { } application.registerActivityLifecycleCallbacks(it.activityLifecycleHelper) } + + if (it.localCache.getPushToken().isNullOrEmpty()) { + it.retrieveFirebaseToken() + } + + it.localCache.saveApiToken(apiToken) } + /** + * A lightweight initialization of Ometria, only for internal usage. + * Note: Not all SDK functions will be available after this initialization. + * @return An initialized Ometria instance object. + */ + internal fun initializeForInternalUsage(context: Context) = + instance.also { + it.executor = OmetriaThreadPoolExecutor() + it.localCache = LocalCache(context) + + val apiToken = it.localCache.getApiToken() + apiToken ?: return@also + + it.ometriaConfig = OmetriaConfig(apiToken, context) + it.repository = Repository( + Client(ConnectionFactory(it.ometriaConfig)), + it.localCache, + it.executor + ) + it.eventHandler = EventHandler(context, it.repository) + it.isInitialized = true + } + private fun clearOldInstanceIfNeeded() { if (instance.isInitialized) { - instance.flush() - instance.clear() + clearOldInstance() } } + internal fun clearOldInstance() { + instance.flush() + instance.clear() + instance.isInitialized = false + } + /** * Instances are safe to store, since they're immutable and always the same. * @return An existing Ometria instance @@ -149,6 +186,24 @@ class Ometria private constructor() : OmetriaNotificationInteractionHandler { } } + internal fun isReactNativeUsage(): Boolean = repository.getSdkVersionRN() != null + + private fun retrieveFirebaseToken() { + FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task -> + if (!task.isSuccessful) { + Logger.w( + Constants.Logger.EVENTS, + "Fetching FCM registration token failed." + ) + return@OnCompleteListener + } + + val token = task.result + Logger.d(Constants.Logger.PUSH_NOTIFICATIONS, "Token - $token") + instance.onNewToken(token) + }) + } + private fun shouldGenerateInstallationId(): Boolean = repository.getInstallationId() == null internal fun generateInstallationId() { @@ -195,7 +250,9 @@ class Ometria private constructor() : OmetriaNotificationInteractionHandler { } fun onNewToken(token: String) { - trackPushTokenRefreshedEvent(token) + if (localCache.getPushToken() != token) { + trackPushTokenRefreshedEvent(token) + } } private fun trackEvent(type: OmetriaEventType, data: Map? = null) { @@ -272,7 +329,7 @@ class Ometria private constructor() : OmetriaNotificationInteractionHandler { /** * Track the current app user being deidentified. - * An app user has deidentified themselves. This basically means: a user has logged out.* + * An app user has deidentified themselves. This basically means: a user has logged out. */ fun trackProfileDeidentifiedEvent() { trackEvent(OmetriaEventType.PROFILE_DEIDENTIFIED) diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/OmetriaConfig.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/OmetriaConfig.kt index d751486..a89986d 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/OmetriaConfig.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/OmetriaConfig.kt @@ -1,10 +1,10 @@ package com.android.ometriasdk.core -import android.app.Application +import android.content.Context internal class OmetriaConfig( var apiToken: String, - var application: Application + var application: Context ) { var enableDebugging: Boolean = false } \ No newline at end of file diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventExtensions.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventExtensions.kt index 8475de7..f9b128a 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventExtensions.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventExtensions.kt @@ -4,7 +4,8 @@ import com.android.ometriasdk.core.Constants import com.android.ometriasdk.core.network.model.OmetriaApiRequest import java.text.DateFormat import java.text.SimpleDateFormat -import java.util.* +import java.util.Calendar +import java.util.Locale /** * @return A hashcode used to compare and group cached events in batches when performing flush. diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventHandler.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventHandler.kt index 84bd8ff..c2a891e 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventHandler.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/EventHandler.kt @@ -12,7 +12,9 @@ import com.android.ometriasdk.core.Ometria import com.android.ometriasdk.core.Repository import java.text.DateFormat import java.text.SimpleDateFormat -import java.util.* +import java.util.Calendar +import java.util.Locale +import java.util.UUID import java.util.concurrent.TimeUnit private const val FLUSH_LIMIT = 10 diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/OmetriaEvent.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/OmetriaEvent.kt index 1ab5d7f..82af64f 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/OmetriaEvent.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/event/OmetriaEvent.kt @@ -3,7 +3,7 @@ package com.android.ometriasdk.core.event import android.os.Build import com.android.ometriasdk.BuildConfig import com.android.ometriasdk.core.Constants.Common.PLATFORM -import java.util.* +import java.util.Date internal data class OmetriaEvent( val eventId: String, diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/network/Client.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/network/Client.kt index 05ecd6e..973bb69 100644 --- a/OmetriaSDK/src/main/java/com/android/ometriasdk/core/network/Client.kt +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/core/network/Client.kt @@ -5,7 +5,12 @@ import com.android.ometriasdk.core.Logger import com.android.ometriasdk.core.network.model.OmetriaApiError import com.android.ometriasdk.core.network.model.OmetriaApiRequest import org.json.JSONException -import java.io.* +import java.io.BufferedReader +import java.io.BufferedWriter +import java.io.IOException +import java.io.InputStreamReader +import java.io.OutputStream +import java.io.OutputStreamWriter import java.net.HttpURLConnection import java.nio.charset.Charset diff --git a/OmetriaSDK/src/main/java/com/android/ometriasdk/notification/OmetriaMessagingReceiver.kt b/OmetriaSDK/src/main/java/com/android/ometriasdk/notification/OmetriaMessagingReceiver.kt new file mode 100644 index 0000000..580a82a --- /dev/null +++ b/OmetriaSDK/src/main/java/com/android/ometriasdk/notification/OmetriaMessagingReceiver.kt @@ -0,0 +1,41 @@ +package com.android.ometriasdk.notification + +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import com.android.ometriasdk.core.Ometria +import com.android.ometriasdk.core.Ometria.Companion.clearOldInstance +import com.android.ometriasdk.core.event.OmetriaEventType +import com.android.ometriasdk.core.network.toOmetriaNotificationBody +import com.google.firebase.messaging.RemoteMessage + +/** + * Used as Push notification interceptor. + * This will not replace Firebase's own BroadcastReceiver. + * The purpose of using this BroadcastReceive is to log [OmetriaEventType.NOTIFICATION_RECEIVED] event + * when the app is killed for React Native use-case. + */ +class OmetriaMessagingReceiver : BroadcastReceiver() { + + override fun onReceive(context: Context?, intent: Intent?) { + val remoteMessage = RemoteMessage(intent?.extras) + + try { + Ometria.instance() + } catch (e: IllegalStateException) { + context?.let { + Ometria.initializeForInternalUsage(context) + + if (Ometria.instance().isReactNativeUsage()) { + val ometriaNotificationBody = remoteMessage.toOmetriaNotificationBody() + val ometriaNotificationContext = ometriaNotificationBody?.context + + ometriaNotificationContext?.let { + Ometria.instance().trackNotificationReceivedEvent(it) + clearOldInstance() + } + } + } + } + } +} \ No newline at end of file diff --git a/README.md b/README.md index 0ef208f..bb63271 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ To install the library inside **Android Studio**, declare it as dependency in yo ```gradle dependencies { - implementation 'com.ometria:android-sdk:1.6.1' + implementation 'com.ometria:android-sdk:1.6.2' } ``` diff --git a/app/build.gradle.kts b/app/build.gradle.kts index a727356..da2ad3a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.android.sample" minSdk = 23 targetSdk = 33 - versionCode = 9 - versionName = "1.0.9" + versionCode = 11 + versionName = "1.2.0" } signingConfigs { getByName("debug") { diff --git a/app/src/main/java/com/android/sample/SampleApp.kt b/app/src/main/java/com/android/sample/SampleApp.kt index 8613d90..4ec443b 100644 --- a/app/src/main/java/com/android/sample/SampleApp.kt +++ b/app/src/main/java/com/android/sample/SampleApp.kt @@ -15,6 +15,7 @@ import com.android.sample.presentation.MainActivity import com.android.sample.presentation.OMETRIA_NOTIFICATION_STRING_EXTRA_KEY class SampleApp : Application(), OmetriaNotificationInteractionHandler { + companion object { lateinit var instance: SampleApp } diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index eeb21ee..adb2560 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -55,7 +55,7 @@ android:layout_margin="16dp" android:drawableEnd="@drawable/ic_arrow_right_alt_24px" android:drawablePadding="5dp" - android:text="Update API Toekn" + android:text="@string/update_api_token" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/detailsBTN" /> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 68d20d3..815a8b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,4 +16,5 @@ Email Please enter your API token Do it later + Update API Token \ No newline at end of file