diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java
index 74dac0df66..f3ee1e67e0 100644
--- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java
+++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/model/MainActivityViewModel.java
@@ -86,7 +86,8 @@ public class MainActivityViewModel implements ActivityViewModel, ISubscriptionCh
private RelativeLayout appIdRelativeLayout;
private TextView appIdTitleTextView;
private TextView appIdTextView;
- private Button switchUserButton;
+ private Button loginUserButton;
+ private Button logoutUserButton;
// Alias
private TextView aliasTitleTextView;
@@ -213,7 +214,8 @@ public ActivityViewModel onActivityCreated(Context context) {
appIdTitleTextView = getActivity().findViewById(R.id.main_activity_account_details_app_id_title_text_view);
appIdTextView = getActivity().findViewById(R.id.main_activity_account_details_app_id_text_view);
revokeConsentButton = getActivity().findViewById(R.id.main_activity_app_revoke_consent_button);
- switchUserButton = getActivity().findViewById(R.id.main_activity_switch_user_button);
+ loginUserButton = getActivity().findViewById(R.id.main_activity_login_user_button);
+ logoutUserButton = getActivity().findViewById(R.id.main_activity_logout_user_button);
aliasTitleTextView = getActivity().findViewById(R.id.main_activity_aliases_title_text_view);
noAliasesTextView = getActivity().findViewById(R.id.main_activity_aliases_no_aliases_text_view);
@@ -301,7 +303,8 @@ public ActivityViewModel setupInterfaceElements() {
font.applyFont(privacyConsentAllowButton, font.saralaBold);
font.applyFont(appIdTitleTextView, font.saralaBold);
font.applyFont(appIdTextView, font.saralaRegular);
- font.applyFont(switchUserButton, font.saralaBold);
+ font.applyFont(loginUserButton, font.saralaBold);
+ font.applyFont(logoutUserButton, font.saralaBold);
font.applyFont(aliasTitleTextView, font.saralaBold);
font.applyFont(noAliasesTextView, font.saralaBold);
font.applyFont(emailHeaderTextView, font.saralaBold);
@@ -401,37 +404,28 @@ public void onScrollChanged() {
private void setupAppLayout() {
revokeConsentButton.setOnClickListener(v -> togglePrivacyConsent(false));
- if(SharedPreferenceUtil.getCachedIsLoggedIn(context)) {
- switchUserButton.setText(R.string.logout_user);
- }
-
- switchUserButton.setOnClickListener(v -> {
- if(SharedPreferenceUtil.getCachedIsLoggedIn(context)) {
- OneSignal.logout(Continue.with(r -> {
- SharedPreferenceUtil.cacheIsLoggedIn(context, false);
- switchUserButton.setText(R.string.login_user);
- refreshState();
- }));
- }
- else {
- dialog.createUpdateAlertDialog("", Dialog.DialogAction.LOGIN, ProfileUtil.FieldType.EXTERNAL_USER_ID, new UpdateAlertDialogCallback() {
- @Override
- public void onSuccess(String update) {
- if (update != null && !update.isEmpty()) {
- OneSignal.login(update, Continue.with(r -> {
- SharedPreferenceUtil.cacheIsLoggedIn(context, true);
- switchUserButton.setText(R.string.logout_user);
- refreshState();
- }));
- }
+ loginUserButton.setOnClickListener(v -> {
+ dialog.createUpdateAlertDialog("", Dialog.DialogAction.LOGIN, ProfileUtil.FieldType.EXTERNAL_USER_ID, new UpdateAlertDialogCallback() {
+ @Override
+ public void onSuccess(String update) {
+ if (update != null && !update.isEmpty()) {
+ OneSignal.login(update, Continue.with(r -> {
+ refreshState();
+ }));
}
+ }
- @Override
- public void onFailure() {
+ @Override
+ public void onFailure() {
- }
- });
- }
+ }
+ });
+ });
+
+ logoutUserButton.setOnClickListener(v -> {
+ OneSignal.logout(Continue.with(r -> {
+ refreshState();
+ }));
});
}
diff --git a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java
index 28c84ccd7f..5cb286d7bf 100644
--- a/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java
+++ b/Examples/OneSignalDemo/app/src/main/java/com/onesignal/sdktest/util/SharedPreferenceUtil.java
@@ -14,7 +14,6 @@ public class SharedPreferenceUtil {
public static final String USER_EXTERNAL_USER_ID_SHARED_PREF = "USER_EXTERNAL_USER_ID_SHARED_PREF";
private static final String LOCATION_SHARED_PREF = "LOCATION_SHARED_PREF";
private static final String IN_APP_MESSAGING_PAUSED_PREF = "IN_APP_MESSAGING_PAUSED_PREF";
- private static final String IS_LOGGED_IN = "IS_LOGGED_IN";
private static SharedPreferences getSharedPreference(Context context) {
return context.getSharedPreferences(APP_SHARED_PREFS, Context.MODE_PRIVATE);
@@ -44,10 +43,6 @@ public static boolean getCachedInAppMessagingPausedStatus(Context context) {
return getSharedPreference(context).getBoolean(IN_APP_MESSAGING_PAUSED_PREF, true);
}
- public static boolean getCachedIsLoggedIn(Context context) {
- return getSharedPreference(context).getBoolean(IS_LOGGED_IN, false);
- }
-
public static void cacheOneSignalAppId(Context context, String appId) {
getSharedPreference(context).edit().putString(OS_APP_ID_SHARED_PREF, appId).apply();
}
@@ -67,8 +62,4 @@ public static void cacheLocationSharedStatus(Context context, boolean subscribed
public static void cacheInAppMessagingPausedStatus(Context context, boolean paused) {
getSharedPreference(context).edit().putBoolean(IN_APP_MESSAGING_PAUSED_PREF, paused).apply();
}
-
- public static void cacheIsLoggedIn(Context context, boolean isLoggedIn) {
- getSharedPreference(context).edit().putBoolean(IS_LOGGED_IN, isLoggedIn).apply();
- }
}
diff --git a/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml b/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml
index 5b65e5b0f5..ee4ce93cb5 100644
--- a/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml
+++ b/Examples/OneSignalDemo/app/src/main/res/layout/main_activity_layout.xml
@@ -245,6 +245,8 @@
android:visibility="visible"/>
+
+
+
+
+
+
+
+
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt
index 86ffec33ad..f29900bb42 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/ConfigModel.kt
@@ -12,6 +12,13 @@ class ConfigModel : Model() {
get() = getStringProperty(::appId.name)
set(value) { setStringProperty(::appId.name, value) }
+ /**
+ * This device's push subscription ID.
+ */
+ var pushSubscriptionId: String?
+ get() = getOptStringProperty(::pushSubscriptionId.name)
+ set(value) { setOptStringProperty(::pushSubscriptionId.name, value) }
+
/**
* The API URL String.
*/
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt
index 3fba7f679a..cba57a51f3 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/config/impl/ConfigModelStoreListener.kt
@@ -62,40 +62,40 @@ internal class ConfigModelStoreListener(
var success = false
do {
try {
- val params = _paramsBackendService.fetchParams(
- appId,
- _subscriptionManager.subscriptions.push.id.ifEmpty { null }
- )
+ val params = _paramsBackendService.fetchParams(appId, _subscriptionManager.subscriptions.push.id.ifEmpty { null })
+ // copy current model into new model, then override with what comes down.
val config = ConfigModel()
+ config.initializeFromModel(null, _configModelStore.model)
+
+ // these are always copied from the backend params
config.appId = appId
- config.enterprise = params.enterprise ?: _configModelStore.model.enterprise
- config.useIdentityVerification = params.useIdentityVerification ?: _configModelStore.model.useIdentityVerification
config.notificationChannels = params.notificationChannels
- config.firebaseAnalytics = params.firebaseAnalytics ?: _configModelStore.model.firebaseAnalytics
- config.restoreTTLFilter = params.restoreTTLFilter ?: _configModelStore.model.restoreTTLFilter
config.googleProjectNumber = params.googleProjectNumber
- config.clearGroupOnSummaryClick = params.clearGroupOnSummaryClick ?: _configModelStore.model.clearGroupOnSummaryClick
- config.receiveReceiptEnabled = params.receiveReceiptEnabled ?: _configModelStore.model.receiveReceiptEnabled
- 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.opRepoExecutionInterval = params.opRepoExecutionInterval ?: _configModelStore.model.opRepoExecutionInterval
- config.givenPrivacyConsent = _configModelStore.model.givenPrivacyConsent
-
- config.influenceParams.notificationLimit = params.influenceParams.notificationLimit ?: _configModelStore.model.influenceParams.notificationLimit
- config.influenceParams.indirectNotificationAttributionWindow = params.influenceParams.indirectNotificationAttributionWindow ?: _configModelStore.model.influenceParams.indirectNotificationAttributionWindow
- config.influenceParams.iamLimit = params.influenceParams.iamLimit ?: _configModelStore.model.influenceParams.iamLimit
- config.influenceParams.indirectIAMAttributionWindow = params.influenceParams.indirectIAMAttributionWindow ?: _configModelStore.model.influenceParams.indirectIAMAttributionWindow
- config.influenceParams.isDirectEnabled = params.influenceParams.isDirectEnabled ?: _configModelStore.model.influenceParams.isDirectEnabled
- config.influenceParams.isIndirectEnabled = params.influenceParams.isIndirectEnabled ?: _configModelStore.model.influenceParams.isIndirectEnabled
- config.influenceParams.isUnattributedEnabled = params.influenceParams.isUnattributedEnabled ?: _configModelStore.model.influenceParams.isUnattributedEnabled
-
config.fcmParams.projectId = params.fcmParams.projectId
config.fcmParams.appId = params.fcmParams.appId
config.fcmParams.apiKey = params.fcmParams.apiKey
+ // these are only copied from the backend params when the backend has set them.
+ params.enterprise?.let { config.enterprise = it }
+ params.useIdentityVerification?.let { config.useIdentityVerification = it }
+ params.firebaseAnalytics?.let { config.firebaseAnalytics = it }
+ params.restoreTTLFilter?.let { config.restoreTTLFilter = it }
+ params.clearGroupOnSummaryClick?.let { config.clearGroupOnSummaryClick = it }
+ params.receiveReceiptEnabled?.let { config.receiveReceiptEnabled = it }
+ params.disableGMSMissingPrompt?.let { config.disableGMSMissingPrompt = it }
+ params.unsubscribeWhenNotificationsDisabled?.let { config.unsubscribeWhenNotificationsDisabled = it }
+ params.locationShared?.let { config.locationShared = it }
+ params.requiresUserPrivacyConsent?.let { config.requiresPrivacyConsent = it }
+ params.opRepoExecutionInterval?.let { config.opRepoExecutionInterval = it }
+ params.influenceParams.notificationLimit?.let { config.influenceParams.notificationLimit = it }
+ params.influenceParams.indirectNotificationAttributionWindow?.let { config.influenceParams.indirectNotificationAttributionWindow = it }
+ params.influenceParams.iamLimit?.let { config.influenceParams.iamLimit = it }
+ params.influenceParams.indirectIAMAttributionWindow?.let { config.influenceParams.indirectIAMAttributionWindow = it }
+ params.influenceParams.isDirectEnabled?.let { config.influenceParams.isDirectEnabled = it }
+ params.influenceParams.isIndirectEnabled?.let { config.influenceParams.isIndirectEnabled = it }
+ params.influenceParams.isUnattributedEnabled?.let { config.influenceParams.isUnattributedEnabled }
+
_configModelStore.replace(config, ModelChangeTags.HYDRATE)
success = true
} catch (ex: BackendException) {
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt
index ad5fe8a180..501faca671 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/internal/OneSignalImp.kt
@@ -266,6 +266,10 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
// only allow one login/logout at a time
_loginMutex.withLock {
+ if (_identityModelStore!!.model.externalId == null) {
+ return
+ }
+
createAndSwitchToNewUser()
_operationRepo!!.enqueue(LoginUserOperation(_configModel!!.appId, _identityModelStore!!.model.onesignalId, _identityModelStore!!.model.externalId))
// TODO: remove JWT Token for all future requests.
@@ -291,22 +295,29 @@ internal class OneSignalImp : IOneSignal, IServiceProvider {
val subscriptions = mutableListOf()
// Create the push subscription for this device under the new user, copying the current
- // user's push subscription if one exists.
- val currentPushSubscription = _subscriptionModelStore!!.list().firstOrNull { it.type == SubscriptionType.PUSH }
+ // user's push subscription if one exists. We also copy the ID. If the ID is local there
+ // will already be a CreateSubscriptionOperation on the queue. If the ID is remote the subscription
+ // will be automatically transferred over to this new user being created. If there is no
+ // current push subscription we do a "normal" replace which will drive adding a CreateSubscriptionOperation
+ // to the queue.
+ val currentPushSubscription = _subscriptionModelStore!!.list().firstOrNull { it.id == _configModel!!.pushSubscriptionId }
val newPushSubscription = SubscriptionModel()
- newPushSubscription.id = IDManager.createLocalId()
+ newPushSubscription.id = currentPushSubscription?.id ?: IDManager.createLocalId()
newPushSubscription.type = SubscriptionType.PUSH
newPushSubscription.optedIn = currentPushSubscription?.optedIn ?: true
newPushSubscription.address = currentPushSubscription?.address ?: ""
newPushSubscription.status = currentPushSubscription?.status ?: SubscriptionStatus.NO_PERMISSION
+ // ensure we always know this devices push subscription ID
+ _configModel!!.pushSubscriptionId = newPushSubscription.id
+
subscriptions.add(newPushSubscription)
// The next 4 lines makes this user the effective user locally. We clear the subscriptions
- // first as an internal change because we don't want to drive deleting the cleared subscriptions
+ // first as a `NO_PROPOGATE` 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 as a "normal" change, which will drive changes to the backend.
+ // 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)
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt
index fffc3c9b9d..e483ee126c 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/ISubscriptionBackendService.kt
@@ -31,7 +31,17 @@ interface ISubscriptionBackendService {
* Delete an existing subscription.
*
* @param appId The ID of the OneSignal application this subscription exists under.
- * @param subscriptionId The ID of the subscription to update.
+ * @param subscriptionId The ID of the subscription to delete.
*/
suspend fun deleteSubscription(appId: String, subscriptionId: String)
+
+ /**
+ * Transfer an existing subscription to the user specified.
+ *
+ * @param appId The ID of the OneSignal application this subscription exists under.
+ * @param subscriptionId The ID of the subscription to transfer.
+ * @param aliasLabel The alias label of the user to transfer the subscription under.
+ * @param aliasValue The identifier within the [aliasLabel] that identifies the user to transfer under.
+ */
+ suspend fun transferSubscription(appId: String, subscriptionId: String, aliasLabel: String, aliasValue: String)
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt
index 529f93f6c5..74570f7ae1 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/backend/impl/SubscriptionBackendService.kt
@@ -69,4 +69,15 @@ internal class SubscriptionBackendService(
throw BackendException(response.statusCode, response.payload)
}
}
+
+ override suspend fun transferSubscription(appId: String, subscriptionId: String, aliasLabel: String, aliasValue: String) {
+ val requestJSON = JSONObject()
+ .put("identity", JSONObject().put(aliasLabel, aliasValue))
+
+ val response = _httpClient.patch("apps/$appId/subscriptions/$subscriptionId/owner", requestJSON)
+
+ if (!response.isSuccess) {
+ throw BackendException(response.statusCode, response.payload)
+ }
+ }
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt
new file mode 100644
index 0000000000..3fa42c3c34
--- /dev/null
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/TransferSubscriptionOperation.kt
@@ -0,0 +1,55 @@
+package com.onesignal.user.internal.operations
+
+import com.onesignal.common.IDManager
+import com.onesignal.core.internal.operations.GroupComparisonType
+import com.onesignal.core.internal.operations.Operation
+import com.onesignal.user.internal.operations.impl.executors.SubscriptionOperationExecutor
+
+/**
+ * An [Operation] to transfer a subscription to a new owner on the OneSignal backend.
+ */
+class TransferSubscriptionOperation() : Operation(SubscriptionOperationExecutor.TRANSFER_SUBSCRIPTION) {
+ /**
+ * The application ID this subscription will be transferred under.
+ */
+ var appId: String
+ get() = getStringProperty(::appId.name)
+ private set(value) { setStringProperty(::appId.name, value) }
+
+ /**
+ * The subscription ID that is to be transferred. This ID *may* be locally generated
+ * and can be checked via [IDManager.isLocalId] to ensure correct processing.
+ */
+ var subscriptionId: String
+ get() = getStringProperty(::subscriptionId.name)
+ private set(value) { setStringProperty(::subscriptionId.name, value) }
+
+ /**
+ * The user ID this subscription will be transferred to. This ID *may* be locally generated
+ * and can be checked via [IDManager.isLocalId] to ensure correct processing.
+ */
+ var onesignalId: String
+ get() = getStringProperty(::onesignalId.name)
+ private set(value) { setStringProperty(::onesignalId.name, value) }
+
+ override val createComparisonKey: String get() = "$appId.Subscription.$subscriptionId.Transfer"
+ override val modifyComparisonKey: String get() = "$appId.Subscription.$subscriptionId.Transfer"
+ override val groupComparisonType: GroupComparisonType = GroupComparisonType.NONE
+ override val canStartExecute: Boolean get() = !IDManager.isLocalId(onesignalId) && !IDManager.isLocalId(subscriptionId)
+
+ constructor(appId: String, subscriptionId: String, onesignalId: String) : this() {
+ this.appId = appId
+ this.subscriptionId = subscriptionId
+ this.onesignalId = onesignalId
+ }
+
+ override fun translateIds(map: Map) {
+ if (map.containsKey(subscriptionId)) {
+ subscriptionId = map[subscriptionId]!!
+ }
+
+ if (map.containsKey(onesignalId)) {
+ onesignalId = map[onesignalId]!!
+ }
+ }
+}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt
index d60c07bbd9..601dfdf092 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/LoginUserOperationExecutor.kt
@@ -8,6 +8,7 @@ import com.onesignal.common.RootToolsInternalMethods
import com.onesignal.common.exceptions.BackendException
import com.onesignal.common.modeling.ModelChangeTags
import com.onesignal.core.internal.application.IApplicationService
+import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.device.IDeviceService
import com.onesignal.core.internal.operations.ExecutionResponse
import com.onesignal.core.internal.operations.ExecutionResult
@@ -42,7 +43,8 @@ internal class LoginUserOperationExecutor(
private val _userBackend: IUserBackendService,
private val _identityModelStore: IdentityModelStore,
private val _propertiesModelStore: PropertiesModelStore,
- private val _subscriptionsModelStore: SubscriptionModelStore
+ private val _subscriptionsModelStore: SubscriptionModelStore,
+ private val _configModelStore: ConfigModelStore
) : IOperationExecutor {
override val operations: List
@@ -153,6 +155,10 @@ internal class LoginUserOperationExecutor(
idTranslations[subscriptionList[index].id] = backendSubscription.id
+ if (_configModelStore.model.pushSubscriptionId == subscriptionList[index].id) {
+ _configModelStore.model.pushSubscriptionId = backendSubscription.id
+ }
+
val subscriptionModel = _subscriptionsModelStore.get(subscriptionList[index].id)
subscriptionModel?.setStringProperty(SubscriptionModel::id.name, backendSubscription.id, ModelChangeTags.HYDRATE)
}
@@ -206,15 +212,6 @@ internal class LoginUserOperationExecutor(
DeviceUtils.getCarrierName(_application.appContext)
)
- // TODO: These are no longer captured?
- // subscriptionObject.put("sdk_type", OneSignalUtils.sdkType)
- // subscriptionObject.put("type", SubscriptionObjectType.fromDeviceType(_deviceService.deviceType))
- // subscriptionObject.put("android_package", _application.appContext.packageName)
-// try {
-// subscriptionObject.put("game_version", _application.appContext.packageManager.getPackageInfo(_application.appContext.packageName, 0).versionCode)
-// } catch (e: PackageManager.NameNotFoundException) {
-// }
-
return mutableSubscriptions
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt
index 0519cd10eb..1263732ebe 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/RefreshUserOperationExecutor.kt
@@ -3,6 +3,7 @@ package com.onesignal.user.internal.operations.impl.executors
import com.onesignal.common.NetworkUtils
import com.onesignal.common.exceptions.BackendException
import com.onesignal.common.modeling.ModelChangeTags
+import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.operations.ExecutionResponse
import com.onesignal.core.internal.operations.ExecutionResult
import com.onesignal.core.internal.operations.IOperationExecutor
@@ -26,7 +27,8 @@ internal class RefreshUserOperationExecutor(
private val _userBackend: IUserBackendService,
private val _identityModelStore: IdentityModelStore,
private val _propertiesModelStore: PropertiesModelStore,
- private val _subscriptionsModelStore: SubscriptionModelStore
+ private val _subscriptionsModelStore: SubscriptionModelStore,
+ private val _configModelStore: ConfigModelStore
) : IOperationExecutor {
override val operations: List
@@ -101,7 +103,11 @@ internal class RefreshUserOperationExecutor(
}
}
subscriptionModel.optedIn = subscriptionModel.status != SubscriptionStatus.UNSUBSCRIBE
- subscriptionModels.add(subscriptionModel)
+
+ // We only add a push subscription if it is this device's push subscription.
+ if (subscriptionModel.type != SubscriptionType.PUSH || subscriptionModel.id == _configModelStore.model.pushSubscriptionId) {
+ subscriptionModels.add(subscriptionModel)
+ }
}
_identityModelStore.replace(identityModel, ModelChangeTags.HYDRATE)
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
index 54ecf0c4eb..9f6ef60607 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/executors/SubscriptionOperationExecutor.kt
@@ -8,6 +8,7 @@ import com.onesignal.common.RootToolsInternalMethods
import com.onesignal.common.exceptions.BackendException
import com.onesignal.common.modeling.ModelChangeTags
import com.onesignal.core.internal.application.IApplicationService
+import com.onesignal.core.internal.config.ConfigModelStore
import com.onesignal.core.internal.device.IDeviceService
import com.onesignal.core.internal.operations.ExecutionResponse
import com.onesignal.core.internal.operations.ExecutionResult
@@ -21,6 +22,7 @@ import com.onesignal.user.internal.backend.SubscriptionObject
import com.onesignal.user.internal.backend.SubscriptionObjectType
import com.onesignal.user.internal.operations.CreateSubscriptionOperation
import com.onesignal.user.internal.operations.DeleteSubscriptionOperation
+import com.onesignal.user.internal.operations.TransferSubscriptionOperation
import com.onesignal.user.internal.operations.UpdateSubscriptionOperation
import com.onesignal.user.internal.subscriptions.SubscriptionModel
import com.onesignal.user.internal.subscriptions.SubscriptionModelStore
@@ -30,11 +32,12 @@ internal class SubscriptionOperationExecutor(
private val _subscriptionBackend: ISubscriptionBackendService,
private val _deviceService: IDeviceService,
private val _applicationService: IApplicationService,
- private val _subscriptionModelStore: SubscriptionModelStore
+ private val _subscriptionModelStore: SubscriptionModelStore,
+ private val _configModelStore: ConfigModelStore
) : IOperationExecutor {
override val operations: List
- get() = listOf(CREATE_SUBSCRIPTION, UPDATE_SUBSCRIPTION, DELETE_SUBSCRIPTION)
+ get() = listOf(CREATE_SUBSCRIPTION, UPDATE_SUBSCRIPTION, DELETE_SUBSCRIPTION, TRANSFER_SUBSCRIPTION)
override suspend fun execute(operations: List): ExecutionResponse {
Logging.log(LogLevel.DEBUG, "SubscriptionOperationExecutor(operations: $operations)")
@@ -47,6 +50,8 @@ internal class SubscriptionOperationExecutor(
deleteSubscription(operations.first { it is DeleteSubscriptionOperation } as DeleteSubscriptionOperation)
} else if (startingOp is UpdateSubscriptionOperation) {
updateSubscription(startingOp, operations)
+ } else if (startingOp is TransferSubscriptionOperation) {
+ transferSubscription(startingOp)
} else {
throw Exception("Unrecognized operation: $startingOp")
}
@@ -91,6 +96,10 @@ internal class SubscriptionOperationExecutor(
val subscriptionModel = _subscriptionModelStore.get(createOperation.subscriptionId)
subscriptionModel?.setStringProperty(SubscriptionModel::id.name, backendSubscriptionId, ModelChangeTags.HYDRATE)
+ if (_configModelStore.model.pushSubscriptionId == createOperation.subscriptionId) {
+ _configModelStore.model.pushSubscriptionId = backendSubscriptionId
+ }
+
return ExecutionResponse(ExecutionResult.SUCCESS, mapOf(createOperation.subscriptionId to backendSubscriptionId))
} catch (ex: BackendException) {
return if (ex.statusCode == 409) {
@@ -133,6 +142,20 @@ internal class SubscriptionOperationExecutor(
return ExecutionResponse(ExecutionResult.SUCCESS)
}
+ private suspend fun transferSubscription(startingOperation: TransferSubscriptionOperation): ExecutionResponse {
+ try {
+ _subscriptionBackend.transferSubscription(startingOperation.appId, startingOperation.subscriptionId, IdentityConstants.ONESIGNAL_ID, startingOperation.onesignalId)
+ } catch (ex: BackendException) {
+ return if (NetworkUtils.shouldRetryNetworkRequest(ex.statusCode)) {
+ ExecutionResponse(ExecutionResult.FAIL_RETRY)
+ } else {
+ ExecutionResponse(ExecutionResult.FAIL_NORETRY)
+ }
+ }
+
+ return ExecutionResponse(ExecutionResult.SUCCESS)
+ }
+
private fun convert(subscriptionType: SubscriptionType): SubscriptionObjectType {
return when (subscriptionType) {
SubscriptionType.SMS -> {
@@ -168,5 +191,6 @@ internal class SubscriptionOperationExecutor(
const val CREATE_SUBSCRIPTION = "create-subscription"
const val UPDATE_SUBSCRIPTION = "update-subscription"
const val DELETE_SUBSCRIPTION = "delete-subscription"
+ const val TRANSFER_SUBSCRIPTION = "transfer-subscription"
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt
index c4a1ada5a0..6f34442d4c 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/operations/impl/listeners/SubscriptionModelStoreListener.kt
@@ -20,22 +20,8 @@ internal class SubscriptionModelStoreListener(
) : ModelStoreListener(store, opRepo) {
override fun getAddOperation(model: SubscriptionModel): Operation {
- val status: SubscriptionStatus
- val enabled: Boolean
-
- if (model.optedIn && model.status == SubscriptionStatus.SUBSCRIBED && model.address.isNotEmpty()) {
- enabled = true
- status = SubscriptionStatus.SUBSCRIBED
- } else {
- enabled = false
- status = if (!model.optedIn) {
- SubscriptionStatus.UNSUBSCRIBE
- } else {
- model.status
- }
- }
-
- return CreateSubscriptionOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, model.id, model.type, enabled, model.address, status)
+ val enabledAndStatus = getSubscriptionEnabledAndStatus(model)
+ return CreateSubscriptionOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, model.id, model.type, enabledAndStatus.first, model.address, enabledAndStatus.second)
}
override fun getRemoveOperation(model: SubscriptionModel): Operation {
@@ -43,21 +29,28 @@ internal class SubscriptionModelStoreListener(
}
override fun getUpdateOperation(model: SubscriptionModel, path: String, property: String, oldValue: Any?, newValue: Any?): Operation {
- val status: SubscriptionStatus
- val enabled: Boolean
-
- if (model.optedIn && model.status == SubscriptionStatus.SUBSCRIBED && model.address.isNotEmpty()) {
- enabled = true
- status = SubscriptionStatus.SUBSCRIBED
- } else {
- enabled = false
- status = if (!model.optedIn) {
- SubscriptionStatus.UNSUBSCRIBE
+ val enabledAndStatus = getSubscriptionEnabledAndStatus(model)
+ return UpdateSubscriptionOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, model.id, model.type, enabledAndStatus.first, model.address, enabledAndStatus.second)
+ }
+
+ companion object {
+ fun getSubscriptionEnabledAndStatus(model: SubscriptionModel): Pair {
+ val status: SubscriptionStatus
+ val enabled: Boolean
+
+ if (model.optedIn && model.status == SubscriptionStatus.SUBSCRIBED && model.address.isNotEmpty()) {
+ enabled = true
+ status = SubscriptionStatus.SUBSCRIBED
} else {
- model.status
+ enabled = false
+ status = if (!model.optedIn) {
+ SubscriptionStatus.UNSUBSCRIBE
+ } else {
+ model.status
+ }
}
- }
- return UpdateSubscriptionOperation(_configModelStore.model.appId, _identityModelStore.model.onesignalId, model.id, model.type, enabled, model.address, status)
+ return Pair(enabled, status)
+ }
}
}
diff --git a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt
index cda0ff375f..f04a1334bf 100644
--- a/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt
+++ b/OneSignalSDK/onesignal/core/src/main/java/com/onesignal/user/internal/subscriptions/impl/SubscriptionManager.kt
@@ -61,12 +61,13 @@ internal class SubscriptionManager(
if (pushSub is UninitializedPushSubscription) {
addSubscriptionToModels(SubscriptionType.PUSH, pushToken ?: "", pushTokenStatus)
} else {
- updateSubscriptionModel(pushSub) {
- if (pushToken != null) {
- it.address = pushToken
- }
- it.status = pushTokenStatus
+ val pushSubModel = (pushSub as Subscription).model
+
+ if (pushToken != null) {
+ pushSubModel.address = pushToken
}
+
+ pushSubModel.status = pushTokenStatus
}
}
@@ -105,11 +106,6 @@ internal class SubscriptionManager(
_subscriptionModelStore.remove(subscription.id)
}
- private fun updateSubscriptionModel(subscription: ISubscription, update: (SubscriptionModel) -> Unit) {
- val subscriptionModel = _subscriptionModelStore.get(subscription.id) ?: return
- update(subscriptionModel)
- }
-
override fun subscribe(handler: ISubscriptionChangedHandler) = _events.subscribe(handler)
override fun unsubscribe(handler: ISubscriptionChangedHandler) = _events.unsubscribe(handler)
diff --git a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/data/impl/NotificationRepository.kt b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/data/impl/NotificationRepository.kt
index 9c291e9907..8a96693330 100644
--- a/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/data/impl/NotificationRepository.kt
+++ b/OneSignalSDK/onesignal/notifications/src/main/java/com/onesignal/notifications/internal/data/impl/NotificationRepository.kt
@@ -352,7 +352,7 @@ internal class NotificationRepository(
) {
if (it.moveToFirst()) {
groupId =
- it.getString(OneSignalDbContract.NotificationTable.COLUMN_NAME_GROUP_ID)
+ it.getOptString(OneSignalDbContract.NotificationTable.COLUMN_NAME_GROUP_ID)
}
}
}