Rxp module integration - rezolved/rezolve_sdk_sampleapp_android GitHub Wiki
RxpSdk
is a common SDK client which allows android developers to use following features:
- authentication handling
- updating access token
- manage and handle geofence engagements
- monitoring location updates
- configuration of push notifications
- and others
Smart Triggers uses Firebase Cloud Messaging to send push notifications to users' devices.
Consumer of RxpSdk
needs to provide Firebase Server Key in order to let Rezolve systems send push notifications. Please get in touch with your Rezolve contact in order to do so.
These dependencies are required by the Rxp module. They should be added in a project build.gradle
:
dependencies {
def rezolveSdk = "2616-bitplaces-5452b67" // TODO: Update it with proper, tagged version after we do the official release of new sdk.
implementation "com.rezolve.sdk:rxp-android:$rezolveSdk"
}
Below the table describes Rxp
interfaces are required to handle its state and features:
Interface or class name | Description |
---|---|
Authenticator | OkHttp3 interface which allows to update access token in case request fails with "token expired" error. Performs either preemptive authentication before connecting to a proxy server, or reactive authentication after receiving a challenge from either an origin web server or proxy server. |
NotificationProperties | Notification properties wrapper class which describes notification-related fields: channelId , smallIcon , color , priority , defaults , vibrationPattern , sound , autoCancel . |
PushNotificationProvider | Allows to manage and configure push notifications. |
NotificationHelper | Helper class to provide better control of notifications. |
SspGeofenceDetector | Abstraction class for geofence detector. |
SspActManager | A manager class that handles Rxp backend communication. |
Key variables
Variable name | Description |
---|---|
APP_CONTEXT | Android application context |
SDK_API_KEY | Rezolve SDK Api Key |
SMART_TRIGGERS_ENDPOINT | Backend URL address which is used for networking communication |
RxpSdk
should be initialized in Android application class onCreate()
method.
Initialization of SspActManager was described here.
@Override
public void onCreate() {
super.onCreate();
initRxpSdk()
}
fun initRxpSdk() {
val geofenceEngagementAlerts = NotificationProperties(
geofenceAlertsNotificationChannelId, // channel id
R.mipmap.ic_launcher, // small icon
ContextCompat.getColor(APP_CONTEXT, R.color.blue), // color
NotificationCompat.PRIORITY_HIGH, // priority
Notification.DEFAULT_ALL, // notification options. The value should be one or more of the following fields combined
// with bitwise-or: Notification.DEFAULT_SOUND, Notification.DEFAULT_VIBRATE, Notification.DEFAULT_LIGHTS.
// For all default values, use Notification.DEFAULT_ALL.
longArrayOf(1000, 1000, 1000, 1000, 1000), // vibration pattern
Settings.System.DEFAULT_NOTIFICATION_URI, // sound
true // auto cancel
)
// you can find PushNotificationProvider example in the next section
val pushNotificationProvider: PushNotificationProvider = object : PushNotificationProvider {
override fun resetCache() {
}
override val messages: MutableSharedFlow<PushMessage>
get() = yourMessagesFlow
override val pushToken: MutableSharedFlow<PushToken>
get() = yourPushTokenFlow
}
PushNotificationDIProvider.pushNotificationProvider = pushNotificationProvider
val authenticator = object : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
// your implementation of okhttp3 Authenticator that refreshes expired Rezolve Core SDK access token. Check section below for an example.
}
}
val tokenHolder: TokenHolder = object : TokenHolder {
// implement methods
}
val accessTokenFlowable = tokenHolder.observeAccessTokenAsFlow()
val notificationHelper = object : NotificationHelper {
// implement methods
}
val rxpSdk = RxpSdk.Builder(APP_CONTEXT)
.authenticator(authenticator)
.accessTokenFlowable(accessTokenFlowable)
.notificationAlerts(geofenceEngagementAlerts)
.pushNotificationProvider(pushNotificationProvider)
.notificationHelper(notificationHelper) // you can use NotificationHelperImpl(APP_CONTEXT) or provide your own implementation
.apiKey(SDK_API_KEY)
.endpoint(SMART_TRIGGERS_ENDPOINT)
.geofenceDetector(geofenceDetector)
.sspActManager(sspActManager)
.build()
RxpSdkProvider.sdk = rxpSdk
}
object FCMManagerProvider {
lateinit var manager: FCMManager
}
Initialize FCMManager
in Android application class onCreate()
method.
class FCMManager constructor(context: Context) : PushNotificationProvider {
private val _token = MutableSharedFlow<PushToken>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
private val _messages = MutableSharedFlow<PushMessage>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
override val pushToken: Flow<PushToken>
get() = _token.distinctUntilChanged()
override val messages: Flow<PushMessage>
get() = _messages.distinctUntilChanged()
init {
FirebaseApp.initializeApp(context)
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.e(TAG, "Fetching FCM registration token failed", task.exception)
_token.tryEmit(PushToken.None)
return@OnCompleteListener
}
val token = task.result
if( token != null ){
_token.tryEmit(PushToken.FCM(token))
} else {
Log.e(TAG, "FCM token is null")
_token.tryEmit(PushToken.None)
return@OnCompleteListener
}
})
}
fun updateToken(newToken: String) {
_token.tryEmit(PushToken.FCM(newToken))
}
fun onMessageReceived(message: RemoteMessage) {
println("$TAG.onMessageReceived: $message")
_messages.tryEmit(message.toPushMessage())
}
@OptIn(ExperimentalCoroutinesApi::class)
override fun resetCache(){
_messages.resetReplayCache()
}
companion object {
const val TAG = "FCM_Manager"
}
}
class RezolveFirebaseMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
println("RezolveFirebaseMessagingService.onMessageReceived: $message")
FCMManagerProvider.manager.onMessageReceived(message)
}
override fun onNewToken(token: String) {
Log.d(TAG, "Refreshed token: $token")
FCMManagerProvider.manager.updateToken(token)
}
companion object {
const val TAG = "RezolveFMS"
}
}
private fun RemoteMessage.toPushMessage() = PushMessage(this.data)
Don't forget to register messaging service in your manifest:
<application>
<service
android:name="com.yourpackagename.RezolveFirebaseMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
An example of RezolveSDK.AuthRequestProvider was described here
class RezolveAuthenticator(private val authRequestProvider: RezolveSDK.AuthRequestProvider) : Authenticator {
override fun authenticate(route: Route?, response: Response): Request? {
if (responseCount(response) > 1) {
return null // If it've already been tried, give up.
}
val getAuthRequest: RezolveSDK.GetAuthRequest = authRequestProvider.authRequest
if (getAuthRequest.isSuccessful) {
val headersMap: MutableMap<String, String>? = getAuthRequest.headersMap
val builder = response.request.newBuilder()
if (headersMap != null) {
for ((key, value) in headersMap) {
builder.header(key, value)
}
}
return builder.build()
}
return null
}
private fun responseCount(response: Response?): Int {
return if (response == null) 0 else 1 + responseCount(response.priorResponse)
}
}
There are various providers of geofence detection. It's up to you to decide which one best suits your needs. The default implementation of SspGeofenceDetector
, based on Google Geofencing Client was provided in ssp-google-geofence
module.
dependencies {
implementation "com.rezolve.sdk:ssp-google-geofence:$rezolveSdkVersion"
}
If you choose to use it, use provided Builder class to create an instance:
val geofenceDetector = GoogleGeofenceDetector.Builder()
.transitionTypes(GoogleGeofenceDetector.TRANSITION_TYPE_ENTER or GoogleGeofenceDetector.TRANSITION_TYPE_EXIT)
.build(context)
Certain system permissions are required to successfully detect geofences. Obviously to provide location updates you need to obtain device position:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
Since Android Api Level 26 in order to register geofences access to background location is required:
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
Also, since Android 33 to show notifications in a system bar another permission is needed:
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
Please keep in mind that these are runtime permissions, which means user has to opt-in after they start the app. For more information please refer to official android docummentation.