Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Unifiedpush support #3

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/client/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ dependencies {

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.unifiedpush)
}
14 changes: 13 additions & 1 deletion api/client/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,2 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<receiver android:enabled="true" android:exported="true" android:name="global.covesa.sdk.api.client.internal.PushReceiver">
<intent-filter>
<action android:name="org.unifiedpush.android.connector.MESSAGE"/>
<action android:name="org.unifiedpush.android.connector.UNREGISTERED"/>
<action android:name="org.unifiedpush.android.connector.NEW_ENDPOINT"/>
<action android:name="org.unifiedpush.android.connector.REGISTRATION_FAILED"/>
</intent-filter>
</receiver>
</application>
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package global.covesa.sdk.api.client.internal

import android.content.Context
import android.content.Intent
import android.util.Log
import global.covesa.sdk.api.client.push.FailedReason
import global.covesa.sdk.api.client.push.PushEndpoint
import global.covesa.sdk.api.client.push.PushService
import org.unifiedpush.android.connector.FailedReason as UFailedReason
import org.unifiedpush.android.connector.MessagingReceiver
import global.covesa.sdk.api.client.push.PushMessage
import org.unifiedpush.android.connector.data.PushEndpoint as UPushEndpoint
import org.unifiedpush.android.connector.data.PushMessage as UPushMessage

/**
* @hide
*
* Receive UnifiedPush events and forward to the implemented [PushService]
*/
class PushReceiver: MessagingReceiver() {
override fun onUnregistered(context: Context, instance: String) {
sendToService(context, instance, PushService.PushEventType.UNREGISTERED)
}

override fun onMessage(context: Context, message: UPushMessage, instance: String) {
if (!message.decrypted) {
Log.w(TAG, "Received a message that can't be decrypted.")
return
}
sendToService(context, instance, PushService.PushEventType.MESSAGE) { intent ->
intent.putExtra("message", PushMessage(message))
}
}

override fun onNewEndpoint(context: Context, endpoint: UPushEndpoint, instance: String) {
sendToService(context, instance, PushService.PushEventType.NEW_ENDPOINT) { intent ->
intent.putExtra("endpoint", PushEndpoint(endpoint))
}
}

override fun onRegistrationFailed(context: Context, reason: UFailedReason, instance: String) {
sendToService(context, instance, PushService.PushEventType.NEW_ENDPOINT) { intent ->
intent.putExtra("reason", FailedReason.fromUp(reason))
}
}

private fun sendToService(context: Context, instance: String, type: PushService.PushEventType, processIntent: (Intent) -> Any = {}) {
Intent().apply {
`package` = context.packageName
action = PushService.ACTION_PUSH_EVENT
putExtra("instance", instance)
putExtra("type", type)
processIntent(this)
}.also {
context.startService(it)
}
}

companion object {
private const val TAG = "PushReceiver"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package global.covesa.sdk.api.client.push

import org.unifiedpush.android.connector.FailedReason as UFailedReason

/**
* A [registration request][TODO()] may fail for different reasons.
*/
enum class FailedReason {
/**
* This is a generic error type, you can try to register again directly.
*/
INTERNAL_ERROR,

/**
* The registration failed because of missing network connection, try again when network is back.
*/
NETWORK,

/**
* The distributor requires a user action to work. For instance, the distributor may be log out of the push server and requires the user to log in. The user must interact with the distributor or sending a new registration will fail again.
*/
ACTION_REQUIRED,

/**
* The distributor requires a VAPID key and you didn't provide one during [registration][UnifiedPush.registerApp].
*/
VAPID_REQUIRED, ;

companion object {
internal fun fromUp(reason: UFailedReason): FailedReason {
return when (reason) {
UFailedReason.NETWORK -> NETWORK
UFailedReason.ACTION_REQUIRED -> ACTION_REQUIRED
UFailedReason.VAPID_REQUIRED -> VAPID_REQUIRED
else -> INTERNAL_ERROR
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package global.covesa.sdk.api.client.push

import android.app.Activity
import android.content.Intent
import org.unifiedpush.android.connector.LinkActivityHelper as ULinkActivityHelper


/**
* Helper with functions to request the distributor's link activity for result and process the result
*
* ## Usage
*
* In your activity, define a new LinkActivityHelper, override onActivityResult to use
* [onLinkActivityResult] then use [startLinkActivityForResult] to start activity on the
* distributor.
*
* ```
* class MyActivity: Activity() {
* /* ... */
* private val helper = LinkActivityHelper(this)
*
* override fun onCreate(savedInstanceState: Bundle?) {
* super.onCreate(savedInstanceState)
* if (!helper.startLinkActivityForResult()) {
* // No distributor found
* }
* }
*
* override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
* if (helper.onLinkActivityResult(requestCode, resultCode, data)) {
* // The distributor is saved, you can request registrations with UnifiedPush.registerApp now
* } else {
* // An error occurred, consider no distributor found for the moment
* }
* }
* /* ... */
* }
* ```
*/
class LinkActivityHelper(activity: Activity) {
private val uLinkActivityHelper = ULinkActivityHelper(activity)

/**
* Start distributor's link activity for result.
*
* @return `true` if the activity has been requested else no distributor can handle the request
*/
fun startLinkActivityForResult(): Boolean = uLinkActivityHelper.startLinkActivityForResult()

/**
* Process result from the distributor's activity
*
* You have to call [PushManager.register] for all your registrations if this returns `true`.
*
* @return `true` if the [requestCode] matches the one of the request and the [resultCode]
* is OK and the [data] contains the PendingIntent to identify the distributor packageName.
*/
fun onLinkActivityResult(
requestCode: Int,
resultCode: Int,
data: Intent?,
): Boolean = uLinkActivityHelper.onLinkActivityResult(requestCode, resultCode, data)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package global.covesa.sdk.api.client.push

import android.os.Parcel
import android.os.Parcelable
import org.unifiedpush.android.connector.data.PublicKeySet as UPublicKeySet

/**
* Contains Web Push (public) keys information necessary for the application server
* to encrypt notification for this instance, following [RFC8291](https://www.rfc-editor.org/rfc/rfc8291)
*/
class PublicKeySet(
/** P-256 Public key, in uncompressed format, base64url encoded without padding. */
val pubKey: String,
/** Auth secret, base64url encoded without padding. */
val auth: String,
) : Parcelable {

internal constructor(uPublicKeySet: UPublicKeySet): this(
uPublicKeySet.pubKey,
uPublicKeySet.auth
)

override fun writeToParcel(
parcel: Parcel,
flags: Int,
) {
parcel.writeString(pubKey)
parcel.writeString(auth)
}

override fun describeContents(): Int {
return 0
}

companion object CREATOR : Parcelable.Creator<PublicKeySet> {
override fun createFromParcel(parcel: Parcel): PublicKeySet? {
val pubKey = parcel.readString()
val auth = parcel.readString()
return PublicKeySet(
pubKey ?: return null,
auth ?: return null,
)
}

override fun newArray(size: Int): Array<PublicKeySet?> {
return arrayOfNulls(size)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package global.covesa.sdk.api.client.push

import android.os.Parcel
import android.os.Parcelable
import org.unifiedpush.android.connector.data.PushEndpoint as UPushEndpoint

/**
* Contains the push endpoint and the associated [PublicKeySet].
*/
class PushEndpoint(
/** URL to push notifications to. */
val url: String,
/** Web Push public key set. */
val pubKeySet: PublicKeySet?,
) : Parcelable {

internal constructor(uPushEndpoint: UPushEndpoint) : this(
uPushEndpoint.url,
uPushEndpoint.pubKeySet?.let { PublicKeySet(it) }
)

override fun writeToParcel(
parcel: Parcel,
flags: Int,
) {
parcel.writeString(url)
parcel.writeInt(pubKeySet?.let { 1 } ?: 0)
pubKeySet?.writeToParcel(parcel, flags)
}

override fun describeContents(): Int {
return 0
}

companion object CREATOR : Parcelable.Creator<PushEndpoint> {
override fun createFromParcel(parcel: Parcel): PushEndpoint? {
val url = parcel.readString()
val pubKeySet =
if (parcel.readInt() == 1) {
PublicKeySet.createFromParcel(parcel)
} else {
null
}
return PushEndpoint(
url ?: return null,
pubKeySet,
)
}

override fun newArray(size: Int): Array<PushEndpoint?> {
return arrayOfNulls(size)
}
}
}
Loading