Skip to content

Commit

Permalink
Merge pull request #1673 from OneSignal/user-model/notification-types…
Browse files Browse the repository at this point in the history
…-model-event-rework

[User Model] notification_types and model event rework
  • Loading branch information
brismithers authored and jinliu9508 committed Jan 31, 2024
2 parents 0c1a655 + ee0760c commit 8cffbaf
Show file tree
Hide file tree
Showing 60 changed files with 532 additions and 316 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.onesignal.core.internal.common.IDManager
import com.onesignal.core.internal.common.OneSignalUtils
import com.onesignal.core.internal.debug.DebugManager
import com.onesignal.core.internal.logging.Logging
import com.onesignal.core.internal.modeling.ModelChangeTags
import com.onesignal.core.internal.models.ConfigModel
import com.onesignal.core.internal.models.ConfigModelStore
import com.onesignal.core.internal.models.IdentityModel
Expand Down Expand Up @@ -69,7 +70,7 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
// check again now that we are synchronized.
if (!_hasCreatedBackendUser) {
// Create the new user in the backend as an operation
_operationRepo!!.enqueue(CreateUserOperation(_configModel!!.appId, _identityModelStore!!.get().onesignalId, _identityModelStore!!.get().externalId))
_operationRepo!!.enqueue(CreateUserOperation(_configModel!!.appId, _identityModelStore!!.model.onesignalId, _identityModelStore!!.model.externalId))
_hasCreatedBackendUser = true
}
}
Expand Down Expand Up @@ -122,8 +123,8 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
(_services.getService<IApplicationService>() as ApplicationService).start(context)

// get the current config model, if there is one
_configModel = _services.getService<ConfigModelStore>().get()
_sessionModel = _services.getService<SessionModelStore>().get()
_configModel = _services.getService<ConfigModelStore>().model
_sessionModel = _services.getService<SessionModelStore>().model

// if the app id was specified as input, update the config model with it
if (appId != null) {
Expand Down Expand Up @@ -154,8 +155,8 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
_startupService = _services.getService()
_startupService!!.bootstrap()

if (_identityModelStore!!.get().hasProperty(IdentityConstants.ONESIGNAL_ID)) {
Logging.debug("initWithContext: using cached user ${_identityModelStore!!.get().onesignalId}")
if (_identityModelStore!!.model.hasProperty(IdentityConstants.ONESIGNAL_ID)) {
Logging.debug("initWithContext: using cached user ${_identityModelStore!!.model.onesignalId}")
_hasCreatedBackendUser = true
} else {
createAndSwitchToNewUser()
Expand Down Expand Up @@ -185,7 +186,7 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
// TODO: Set JWT Token for all future requests.

// Create the new user in the backend as an operation
_operationRepo!!.execute(CreateUserOperation(_configModel!!.appId, _identityModelStore!!.get().onesignalId, _identityModelStore!!.get().externalId))
_operationRepo!!.execute(CreateUserOperation(_configModel!!.appId, _identityModelStore!!.model.onesignalId, _identityModelStore!!.model.externalId))
}

override fun logout() {
Expand Down Expand Up @@ -238,10 +239,10 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
subscriptions.add(newPushSubscription)

// The next 4 lines makes this user the effective user locally. We clear the subscriptions
// first without firing the event because we don't want to drive deleting the cleared subscriptions
// first as an internal change because we don't want to drive deleting the cleared subscriptions
// on the backend. Once cleared we can then setup the new identity/properties model, and add
// the new user's subscriptions.
_subscriptionModelStore!!.clear(fireEvent = false)
// the new user's subscriptions as a "normal" change, which will drive changes to the backend.
_subscriptionModelStore!!.clear(ModelChangeTags.NO_PROPOGATE)
_identityModelStore!!.replace(identityModel)
_propertiesModelStore!!.replace(propertiesModel)
_subscriptionModelStore!!.replaceAll(subscriptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@ internal interface ISubscriptionBackendService {
* @param type The type of subscription to create.
* @param enabled Whether this subscription is enabled.
* @param address The subscription address.
* @param status The subscription status.
*
* @return The ID of the subscription created.
*/
suspend fun createSubscription(appId: String, aliasLabel: String, aliasValue: String, type: SubscriptionObjectType, enabled: Boolean, address: String): String
suspend fun createSubscription(appId: String, aliasLabel: String, aliasValue: String, type: SubscriptionObjectType, enabled: Boolean, address: String, status: Int): String

/**
* Update an existing subscription with the properties provided.
Expand All @@ -25,8 +26,9 @@ internal interface ISubscriptionBackendService {
* @param subscriptionId The ID of the subscription to update.
* @param enabled Whether this subscription is enabled.
* @param address The subscription address.
* @param status The subscription status.
*/
suspend fun updateSubscription(appId: String, subscriptionId: String, enabled: Boolean, address: String)
suspend fun updateSubscription(appId: String, subscriptionId: String, enabled: Boolean, address: String, status: Int)

/**
* Delete an existing subscription.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ internal class SubscriptionBackendService(
private val _http: IHttpClient
) : ISubscriptionBackendService {

override suspend fun createSubscription(appId: String, aliasLabel: String, aliasValue: String, type: SubscriptionObjectType, enabled: Boolean, address: String): String {
override suspend fun createSubscription(appId: String, aliasLabel: String, aliasValue: String, type: SubscriptionObjectType, enabled: Boolean, address: String, status: Int): String {
// TODO: To Implement, temporarily using players endpoint when PUSH
if (type == SubscriptionObjectType.SMS || type == SubscriptionObjectType.EMAIL) {
return UUID.randomUUID().toString()
Expand Down Expand Up @@ -70,7 +70,7 @@ internal class SubscriptionBackendService(
return responseJSON.getString("id")
}

override suspend fun updateSubscription(appId: String, subscriptionId: String, enabled: Boolean, address: String) {
override suspend fun updateSubscription(appId: String, subscriptionId: String, enabled: Boolean, address: String, status: Int) {
// TODO: To Implement
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.onesignal.core.internal.backend.IdentityConstants
import com.onesignal.core.internal.backend.PropertiesDeltasObject
import com.onesignal.core.internal.backend.PropertiesObject
import com.onesignal.core.internal.backend.SubscriptionObject
import com.onesignal.core.internal.models.SubscriptionModel
import org.json.JSONArray
import org.json.JSONObject
import java.util.*
Expand Down Expand Up @@ -34,7 +35,7 @@ internal class UserBackendService(
// TODO: Temporarily using players endpoint via subscription backend to register the subscription, so we can drive push/IAMs.
val subscriptionIDs = mutableListOf<String>()
for (subscription in subscriptions) {
val subscriptionId = _subscriptionBackend.createSubscription(appId, "", "", subscription.type, subscription.enabled ?: true, subscription.token ?: "")
val subscriptionId = _subscriptionBackend.createSubscription(appId, "", "", subscription.type, subscription.enabled ?: true, subscription.token ?: "", subscription.notificationTypes ?: SubscriptionModel.STATUS_SUBSCRIBED)
subscriptionIDs.add(subscriptionId)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import android.os.Bundle
import android.os.Looper
import android.text.TextUtils
import androidx.annotation.Keep
import androidx.core.app.JobIntentService
import androidx.core.app.NotificationManagerCompat
import androidx.legacy.content.WakefulBroadcastReceiver
import com.onesignal.core.internal.application.IApplicationService
import com.onesignal.core.internal.logging.Logging
import java.util.Random
Expand Down Expand Up @@ -119,6 +122,33 @@ internal object AndroidUtils {
return Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1
}

fun hasJobIntentService(): Boolean {
return try {
// noinspection ConstantConditions
JobIntentService::class.java != null
} catch (e: Throwable) {
false
}
}

fun hasWakefulBroadcastReceiver(): Boolean {
return try {
// noinspection ConstantConditions
WakefulBroadcastReceiver::class.java != null
} catch (e: Throwable) {
false
}
}

fun hasNotificationManagerCompat(): Boolean {
return try {
// noinspection ConstantConditions
NotificationManagerCompat::class.java != null
} catch (e: Throwable) {
false
}
}

fun openURLInBrowser(appContext: Context, url: String) {
openURLInBrowser(appContext, Uri.parse(url.trim { it <= ' ' }))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,11 @@ internal interface IDeviceService {
val isGMSInstalledAndEnabled: Boolean
val hasAllHMSLibrariesForPushKit: Boolean
val hasFCMLibrary: Boolean
val androidSupportLibraryStatus: AndroidSupportLibraryStatus

enum class AndroidSupportLibraryStatus {
MISSING,
OUTDATED,
OK
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.onesignal.core.internal.device.impl

import android.content.pm.PackageManager
import android.os.Build
import com.google.android.gms.common.GoogleApiAvailability
import com.google.android.gms.location.LocationListener
import com.google.firebase.messaging.FirebaseMessaging
Expand Down Expand Up @@ -82,6 +83,32 @@ internal class DeviceService(private val _applicationService: IApplicationServic
return false
}

override val androidSupportLibraryStatus: IDeviceService.AndroidSupportLibraryStatus
get() {
val hasWakefulBroadcastReceiver: Boolean = AndroidUtils.hasWakefulBroadcastReceiver()
val hasNotificationManagerCompat: Boolean = AndroidUtils.hasNotificationManagerCompat()
if (!hasWakefulBroadcastReceiver && !hasNotificationManagerCompat) {
return IDeviceService.AndroidSupportLibraryStatus.MISSING
}

if (!hasWakefulBroadcastReceiver || !hasNotificationManagerCompat) {
return IDeviceService.AndroidSupportLibraryStatus.OUTDATED
}

// If running on Android O and targeting O we need version 26.0.0 for
// the new compat NotificationCompat.Builder constructor.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
AndroidUtils.getTargetSdkVersion(_applicationService.appContext) >= Build.VERSION_CODES.O
) {
// Class was added in 26.0.0-beta2
if (!AndroidUtils.hasJobIntentService()) {
return IDeviceService.AndroidSupportLibraryStatus.OUTDATED
}
}

return IDeviceService.AndroidSupportLibraryStatus.OK
}

private fun supportsGooglePush(): Boolean {
// 1. If app does not have the FCM library it won't support Google push
return if (!hasFCMLibrary) false else isGMSInstalledAndEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal class HttpClient(
cacheKey: String?
): HttpResponse {
// If privacy consent is required but not yet given, any non-GET request should be blocked.
if (method != null && _configModelStore.get().requiresPrivacyConsent == true && _configModelStore.get().givenPrivacyConsent != true) {
if (method != null && _configModelStore.model.requiresPrivacyConsent == true && _configModelStore.model.givenPrivacyConsent != true) {
Logging.warn("$method `$url` was called before the user provided privacy consent. Your application is set to require the user's privacy consent before the OneSignal SDK can be initialized. Please ensure the user has provided consent before calling this method. You can check the latest OneSignal consent status by calling OneSignal.privacyConsent")
return HttpResponse(0, null, null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,23 +124,23 @@ internal class InfluenceDataRepository(
}

override val notificationLimit: Int
get() = _configModelStore.get().influenceParams.notificationLimit
get() = _configModelStore.model.influenceParams.notificationLimit

override val iamLimit: Int
get() = _configModelStore.get().influenceParams.iamLimit
get() = _configModelStore.model.influenceParams.iamLimit

override val notificationIndirectAttributionWindow: Int
get() = _configModelStore.get().influenceParams.indirectNotificationAttributionWindow
get() = _configModelStore.model.influenceParams.indirectNotificationAttributionWindow

override val iamIndirectAttributionWindow: Int
get() = _configModelStore.get().influenceParams.indirectIAMAttributionWindow
get() = _configModelStore.model.influenceParams.indirectIAMAttributionWindow

override val isDirectInfluenceEnabled: Boolean
get() = _configModelStore.get().influenceParams.isDirectEnabled
get() = _configModelStore.model.influenceParams.isDirectEnabled

override val isIndirectInfluenceEnabled: Boolean
get() = _configModelStore.get().influenceParams.isIndirectEnabled
get() = _configModelStore.model.influenceParams.isIndirectEnabled

override val isUnattributedInfluenceEnabled: Boolean
get() = _configModelStore.get().influenceParams.isUnattributedEnabled
get() = _configModelStore.model.influenceParams.isUnattributedEnabled
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ internal class LanguageContext(
private var _deviceLanguageProvider = LanguageProviderDevice()

override var language: String
get() = _propertiesModelStore.get().language ?: _deviceLanguageProvider.language
set(value) { _propertiesModelStore.get().language = value }
get() = _propertiesModelStore.model.language ?: _deviceLanguageProvider.language
set(value) { _propertiesModelStore.model.language = value }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.onesignal.core.internal.backend.IParamsBackendService
import com.onesignal.core.internal.common.suspendifyOnThread
import com.onesignal.core.internal.logging.Logging
import com.onesignal.core.internal.modeling.ISingletonModelStoreChangeHandler
import com.onesignal.core.internal.modeling.ModelChangeTags
import com.onesignal.core.internal.modeling.ModelChangedArgs
import com.onesignal.core.internal.models.ConfigModel
import com.onesignal.core.internal.models.ConfigModelStore
import com.onesignal.core.internal.models.InfluenceConfigModel
Expand All @@ -31,19 +33,24 @@ internal class ConfigModelStoreListener(
fetchParams()
}

override fun onModelUpdated(model: ConfigModel, path: String, property: String, oldValue: Any?, newValue: Any?) {
if (property != ConfigModel::appId.name) {
override fun onModelUpdated(args: ModelChangedArgs, tag: String) {
if (args.property != ConfigModel::appId.name) {
return
}

fetchParams()
}

override fun onModelReplaced(model: ConfigModel) {
override fun onModelReplaced(model: ConfigModel, tag: String) {
if (tag != ModelChangeTags.NORMAL) {
return
}

fetchParams()
}

private fun fetchParams() {
val appId = _configModelStore.get().appId
val appId = _configModelStore.model.appId

if (appId.isEmpty()) {
return
Expand All @@ -68,10 +75,11 @@ internal class ConfigModelStoreListener(
config.googleProjectNumber = params.googleProjectNumber
config.clearGroupOnSummaryClick = params.clearGroupOnSummaryClick ?: true
config.receiveReceiptEnabled = params.receiveReceiptEnabled ?: false
config.disableGMSMissingPrompt = params.disableGMSMissingPrompt ?: _configModelStore.get().disableGMSMissingPrompt
config.unsubscribeWhenNotificationsDisabled = params.unsubscribeWhenNotificationsDisabled ?: _configModelStore.get().unsubscribeWhenNotificationsDisabled
config.locationShared = params.locationShared ?: _configModelStore.get().locationShared
config.requiresPrivacyConsent = params.requiresUserPrivacyConsent ?: _configModelStore.get().requiresPrivacyConsent
config.disableGMSMissingPrompt = params.disableGMSMissingPrompt ?: _configModelStore.model.disableGMSMissingPrompt
config.unsubscribeWhenNotificationsDisabled = params.unsubscribeWhenNotificationsDisabled ?: _configModelStore.model.unsubscribeWhenNotificationsDisabled
config.locationShared = params.locationShared ?: _configModelStore.model.locationShared
config.requiresPrivacyConsent = params.requiresUserPrivacyConsent ?: _configModelStore.model.requiresPrivacyConsent
config.givenPrivacyConsent = _configModelStore.model.givenPrivacyConsent

config.influenceParams.notificationLimit = params.influenceParams.notificationLimit ?: InfluenceConfigModel.DEFAULT_NOTIFICATION_LIMIT
config.influenceParams.indirectNotificationAttributionWindow = params.influenceParams.indirectNotificationAttributionWindow ?: InfluenceConfigModel.DEFAULT_INDIRECT_ATTRIBUTION_WINDOW
Expand All @@ -86,7 +94,7 @@ internal class ConfigModelStoreListener(
config.fcmParams.appId = params.fcmParams.appId
config.fcmParams.apiKey = params.fcmParams.apiKey

_configModelStore.replace(config)
_configModelStore.replace(config, ModelChangeTags.HYDRATE)
success = true
} catch (ex: BackendException) {
if (ex.statusCode == HttpURLConnection.HTTP_FORBIDDEN) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ internal class IdentityModelStoreListener(

override fun getUpdateOperation(model: IdentityModel, path: String, property: String, oldValue: Any?, newValue: Any?): Operation {
return if (newValue != null && newValue is String) {
SetAliasOperation(_configModelStore.get().appId, model.onesignalId, property, newValue)
SetAliasOperation(_configModelStore.model.appId, model.onesignalId, property, newValue)
} else {
DeleteAliasOperation(_configModelStore.get().appId, model.onesignalId, property)
DeleteAliasOperation(_configModelStore.model.appId, model.onesignalId, property)
}
}
}
Loading

0 comments on commit 8cffbaf

Please sign in to comment.