Skip to content

Commit

Permalink
Merge pull request #1768 from OneSignal/user-model/user-events
Browse files Browse the repository at this point in the history
[User model] Push Subscription Changed Event
  • Loading branch information
emawby authored and jinliu9508 committed Feb 6, 2024
2 parents d394089 + 8576f50 commit fc8d2eb
Show file tree
Hide file tree
Showing 14 changed files with 127 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
import android.view.View;
import android.view.ViewTreeObserver;
Expand Down Expand Up @@ -54,14 +55,15 @@
import com.onesignal.sdktest.util.ProfileUtil;
import com.onesignal.sdktest.util.Toaster;
import com.onesignal.user.subscriptions.ISubscription;
import com.onesignal.user.subscriptions.ISubscriptionChangedHandler;
import com.onesignal.user.subscriptions.IPushSubscriptionObserver;
import com.onesignal.user.subscriptions.PushSubscriptionChangedState;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

@RequiresApi(api = Build.VERSION_CODES.N)
public class MainActivityViewModel implements ActivityViewModel, ISubscriptionChangedHandler {
public class MainActivityViewModel implements ActivityViewModel, IPushSubscriptionObserver {

private Animate animate;
private Dialog dialog;
Expand Down Expand Up @@ -290,7 +292,7 @@ public ActivityViewModel onActivityCreated(Context context) {
triggerSet = new HashMap<>();
triggerArrayList = new ArrayList<>();

OneSignal.getUser().getPushSubscription().addChangeHandler(this);
OneSignal.getUser().getPushSubscription().addObserver(this);
return this;
}

Expand Down Expand Up @@ -494,6 +496,11 @@ public void run() {
});
}

@Override
public void onPushSubscriptionChange(@NonNull PushSubscriptionChangedState state) {
refreshSubscriptionState();
}

private class DummySubscription implements ISubscription {

private String _id;
Expand All @@ -506,16 +513,6 @@ public DummySubscription(String id) {
public String getId() {
return _id;
}

@Override
public void addChangeHandler(@NonNull ISubscriptionChangedHandler handler) {

}

@Override
public void removeChangeHandler(@NonNull ISubscriptionChangedHandler handler) {

}
}

private void setupEmailLayout() {
Expand Down Expand Up @@ -825,19 +822,6 @@ private void setupPromptPushButton() {
});
}

@Override
public void onSubscriptionChanged(@NonNull ISubscription subscription) {
if(subscription instanceof IPushSubscription) {
refreshSubscriptionState();
}
else if(subscription instanceof IEmailSubscription) {
refreshEmailRecyclerView();
}
else if(subscription instanceof ISmsSubscription) {
refreshSMSRecyclerView();
}
}

private void refreshSubscriptionState() {
boolean isPermissionEnabled = OneSignal.getNotifications().getPermission();
IPushSubscription pushSubscription = OneSignal.getUser().getPushSubscription();
Expand Down
32 changes: 30 additions & 2 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,34 @@ To resume receiving of push notifications (driving the native permission prompt

pushSubscription.optIn()

To observe changes to the push subscription a class can implement the IPushSubscriptionObserver interface, and can then be added as an observer:

**Java**

@Override
public void onPushSubscriptionChange(@NonNull PushSubscriptionChangedState state) {
...
}

pushSubscription.addObserver(this);

**Kotlin**

override fun onPushSubscriptionChange(state: PushSubscriptionChangedState) {
...
}

pushSubscription.addObserver(this)

If you would like to stop observing the subscription you can remove the observer:

**Java**

pushSubscription.removeObserver(this);

**Kotlin**

pushSubscription.removeObserver(this)

**Email/SMS Subscriptions**
Email and/or SMS subscriptions can be added or removed via:
Expand Down Expand Up @@ -186,12 +214,12 @@ The OneSignal SDK has been rewritten in Kotlin. This is typically transparent to
In Java this is surfaced on a method via an additional parameter to the method of type `Continuation`. The `Continuation` is a callback mechanism which allows a Java function to gain control when execution has resumed. If this concept is newer to your application codebase, OneSignal provides an optional java helper class to facilitate the callback model. Method `com.onesignal.Continue.none()` can be used to indicate no callback is necessary:


OneSignal.getNotifications().requestPermission(Continue.none());
OneSignal.getNotifications().requestPermission(true, Continue.none());

`com.onesignal.Continue.with()` can be used to create a callback lambda expression, which is passed a `ContinueResult` containing information on the success of the underlying coroutine, it's return data, and/or the exception that was thrown:


OneSignal.getNotifications().requestPermission(Continue.with(r -> {
OneSignal.getNotifications().requestPermission(true, Continue.with(r -> {
if (r.isSuccess()) {
if (r.getData()) {
// code to execute once requestPermission has completed successfully and the user has accepted permission.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
package com.onesignal.user.internal

import com.onesignal.common.events.EventProducer
import com.onesignal.user.internal.subscriptions.SubscriptionModel
import com.onesignal.user.internal.subscriptions.SubscriptionStatus
import com.onesignal.user.internal.subscriptions.SubscriptionType
import com.onesignal.user.subscriptions.IPushSubscription
import com.onesignal.user.subscriptions.IPushSubscriptionObserver
import com.onesignal.user.subscriptions.PushSubscriptionState

internal open class PushSubscription(
model: SubscriptionModel,
) : Subscription(model), IPushSubscription {
val changeHandlersNotifier = EventProducer<IPushSubscriptionObserver>()
var savedState = fetchState()
private set;

override val token: String
get() = model.address
Expand All @@ -24,6 +30,18 @@ internal open class PushSubscription(
override fun optOut() {
model.optedIn = false
}

override fun addObserver(observer: IPushSubscriptionObserver) = changeHandlersNotifier.subscribe(observer)
override fun removeObserver(observer: IPushSubscriptionObserver) = changeHandlersNotifier.unsubscribe(observer)

fun refreshState() : PushSubscriptionState {
savedState = fetchState()
return savedState
}

private fun fetchState() : PushSubscriptionState {
return PushSubscriptionState(id, token, optedIn)
}
}

internal class UninitializedPushSubscription() : PushSubscription(createFakePushSub()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import com.onesignal.common.IDManager
import com.onesignal.common.events.EventProducer
import com.onesignal.user.internal.subscriptions.SubscriptionModel
import com.onesignal.user.subscriptions.ISubscription
import com.onesignal.user.subscriptions.ISubscriptionChangedHandler
import com.onesignal.user.subscriptions.IPushSubscriptionObserver

/**
* An abstract subscription represents an open channel between
Expand All @@ -13,11 +13,5 @@ import com.onesignal.user.subscriptions.ISubscriptionChangedHandler
internal abstract class Subscription(
val model: SubscriptionModel,
) : ISubscription {

val changeHandlersNotifier = EventProducer<ISubscriptionChangedHandler>()

override val id: String get() = if (IDManager.isLocalId(model.id)) "" else model.id

override fun addChangeHandler(handler: ISubscriptionChangedHandler) = changeHandlersNotifier.subscribe(handler)
override fun removeChangeHandler(handler: ISubscriptionChangedHandler) = changeHandlersNotifier.unsubscribe(handler)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import com.onesignal.user.internal.properties.PropertiesModel
import com.onesignal.user.internal.properties.PropertiesModelStore
import com.onesignal.user.internal.subscriptions.ISubscriptionManager
import com.onesignal.user.subscriptions.IPushSubscription
import com.onesignal.user.subscriptions.SubscriptionList
import com.onesignal.user.internal.subscriptions.SubscriptionList

internal open class UserManager(
private val _subscriptionManager: ISubscriptionManager,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.onesignal.user.internal.subscriptions
import com.onesignal.common.events.IEventNotifier
import com.onesignal.common.modeling.ModelChangedArgs
import com.onesignal.user.subscriptions.ISubscription
import com.onesignal.user.subscriptions.SubscriptionList

interface ISubscriptionManager : IEventNotifier<ISubscriptionChangedHandler> {
var subscriptions: SubscriptionList
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
package com.onesignal.user.subscriptions
package com.onesignal.user.internal.subscriptions

import com.onesignal.user.subscriptions.IEmailSubscription
import com.onesignal.user.subscriptions.IPushSubscription
import com.onesignal.user.subscriptions.ISmsSubscription
import com.onesignal.user.subscriptions.ISubscription

/**
* A readonly list of subscriptions. Wraps a standard [List] to help navigate the list of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import com.onesignal.user.internal.subscriptions.SubscriptionModelStore
import com.onesignal.user.internal.subscriptions.SubscriptionStatus
import com.onesignal.user.internal.subscriptions.SubscriptionType
import com.onesignal.user.subscriptions.ISubscription
import com.onesignal.user.subscriptions.SubscriptionList
import com.onesignal.user.internal.subscriptions.SubscriptionList
import com.onesignal.user.subscriptions.PushSubscriptionChangedState

/**
* The subscription manager is responsible for managing the external representation of the
Expand Down Expand Up @@ -131,12 +132,15 @@ internal class SubscriptionManager(
// don't yet have a representation for it in the subscription list.
createSubscriptionAndAddToSubscriptionList(args.model as SubscriptionModel)
} else {
(subscription as Subscription).changeHandlersNotifier.fireOnMain {
it.onSubscriptionChanged(
subscription,
)
if (subscription is PushSubscription) {
subscription.changeHandlersNotifier.fireOnMain {
it.onPushSubscriptionChange(
PushSubscriptionChangedState(
subscription.savedState,
subscription.refreshState())
)
}
}

// the model has already been updated, so fire the update event
_events.fire { it.onSubscriptionChanged(subscription, args) }
}
Expand All @@ -161,7 +165,7 @@ internal class SubscriptionManager(

// There can only be 1 push subscription, always transfer any subscribers from the old one to the new
if (subscriptionModel.type == SubscriptionType.PUSH) {
val existingPushSubscription = this.subscriptions.push as Subscription
val existingPushSubscription = this.subscriptions.push as PushSubscription
((subscription as PushSubscription).changeHandlersNotifier).subscribeAll(existingPushSubscription.changeHandlersNotifier)
subscriptions.remove(existingPushSubscription)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package com.onesignal.user.subscriptions
* channel.
*/
interface IPushSubscription : ISubscription {

/**
* The token which identifies the device/app that notifications are to be sent. May
* be an empty string, indicating the push token has not yet been retrieved.
Expand Down Expand Up @@ -34,4 +33,15 @@ interface IPushSubscription : ISubscription {
* notifications, although the app permission state will not be changed.
*/
fun optOut()

/**
* Add an observer to this subscription, allowing the provider to be
* notified whenever the subscription has changed.
*/
fun addObserver(observer: IPushSubscriptionObserver)

/**
* Remove an observer from this subscription.
*/
fun removeObserver(observer: IPushSubscriptionObserver)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@ package com.onesignal.user.subscriptions

/**
* A subscription changed handler. Implement this interface and provide the implementation
* to [ISubscription.addChangeHandler] to be notified when the subscription has changed.
* to [ISubscription.addObserver] to be notified when the subscription has changed.
*/
interface ISubscriptionChangedHandler {
interface IPushSubscriptionObserver {

/**
* Called when the subscription this change handler was added to, has changed. A
* subscription can change either because of a change driven by the application, or
* by the backend.
*
* @param subscription The subscription that has been changed.
* @param state The subscription changed state.
*/
fun onSubscriptionChanged(subscription: ISubscription)
fun onPushSubscriptionChange(state: PushSubscriptionChangedState)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,4 @@ interface ISubscription {
* been successfully assigned.
*/
val id: String

/**
* Add a change handler to this subscription, allowing the provider to be
* notified whenever the subscription has changed.
*/
fun addChangeHandler(handler: ISubscriptionChangedHandler)

/**
* Remove a change handler from this subscription.
*/
fun removeChangeHandler(handler: ISubscriptionChangedHandler)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.onesignal.user.subscriptions

class PushSubscriptionChangedState(
val previous: PushSubscriptionState,
val current: PushSubscriptionState
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.onesignal.user.subscriptions

/**
* A subscription state.
*/
class PushSubscriptionState(
/**
* The unique identifier for this subscription. This will be an empty string
* until the subscription has been successfully created on the backend and
* assigned an ID. Use [addObserver] to be notified when the [id] has
* been successfully assigned.
*/
val id: String,

/**
* The token which identifies the device/app that notifications are to be sent. May
* be an empty string, indicating the push token has not yet been retrieved.
*/
val token: String,

/**
* Whether the user of this subscription is opted-in to received notifications. When true,
* the user is able to receive notifications through this subscription. Otherwise, the
* user will not receive notifications through this subscription (even when the user has
* granted app permission).
*/
val optedIn: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.onesignal.user.internal
import com.onesignal.core.internal.language.ILanguageContext
import com.onesignal.mocks.MockHelper
import com.onesignal.user.internal.subscriptions.ISubscriptionManager
import com.onesignal.user.subscriptions.SubscriptionList
import com.onesignal.user.internal.subscriptions.SubscriptionList
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.shouldBe
import io.kotest.runner.junit4.KotestTestRunner
Expand Down

0 comments on commit fc8d2eb

Please sign in to comment.