From 67f0813eb071613e55fdf2806bdc60ac0c9809ff Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Tue, 19 Nov 2024 13:27:36 +0100 Subject: [PATCH 1/6] New push API --- docs/Changelog.md | 4 ++ docs/Using-Push-Service.md | 22 +++----- library/gradle.properties | 2 +- .../model/PushRegistrationRequestObject.kt | 29 ++++++---- .../android/mtokensdk/push/Deprecated.kt | 56 ------------------- .../android/mtokensdk/push/IPushService.kt | 20 ++++++- .../wultra/android/mtokensdk/push/PushData.kt | 49 ++++++++++++++++ .../android/mtokensdk/push/PushService.kt | 36 ++++++------ ...onTests.kt => JsonDeserializationTests.kt} | 44 ++++++++++++++- 9 files changed, 164 insertions(+), 98 deletions(-) delete mode 100644 library/src/main/java/com/wultra/android/mtokensdk/push/Deprecated.kt create mode 100644 library/src/main/java/com/wultra/android/mtokensdk/push/PushData.kt rename library/src/test/java/{OperationJsonDeserializationTests.kt => JsonDeserializationTests.kt} (93%) diff --git a/docs/Changelog.md b/docs/Changelog.md index 75b9668..061460f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,5 +1,9 @@ # Changelog +## x.x.x (TBA) + +- New push registration API that is using `FCM` and `HMS` instead of `ANDROID` and `HUAWEI` + ## 1.12.0 (Oct, 2024) - Upgraded to `PowerAuthSDK` `1.9.x` [(#165)](https://github.com/wultra/mtoken-sdk-ios/pull/165) diff --git a/docs/Using-Push-Service.md b/docs/Using-Push-Service.md index b441cdc..717cac2 100644 --- a/docs/Using-Push-Service.md +++ b/docs/Using-Push-Service.md @@ -56,25 +56,20 @@ __Optional parameters:__ All available methods of the `IPushService` API are: - `acceptLanguage` - Language settings, that will be sent along with each request. -- `register(fcmToken: String, callback: (result: Result) -> Unit)` - Registers Firebase Cloud Messaging token on the backend -- `registerHuawei(hmsToken: String, callback: (result: Result) -> Unit)` - Registers Huawei Mobile Services token on the backend - -Messaging token on the backend - -- `fcmToken` - Firebase Cloud Messaging token. -- `hmsToken` - Huawei Mobile Services token -- `callback` - Called when the request finishes. +- `register(data: PushData, callback: (result: Result) -> Unit)` - Subscribes for WMT push notification on the server ## Registering to Push Notifications -### Android (with Google Play Services) + To register an app to push notifications, you can simply call the `register` method: +### Firebase Cloud Messaging + ```kotlin // first, retrieve Firebase token (do so in the background thread) FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener { task -> if (task.isSuccessful) { task.result?.token?.let { token -> - pushService.register(token) { + pushService.register(PushData.fcm(token)) { it.onSuccess { // push notification registered }.onFailure { @@ -90,7 +85,7 @@ FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener { task -> To be able to successfully process notifications, you need to register the app to receive push notifications in the first place. For more information visit [official documentation](https://firebase.google.com/docs/cloud-messaging/android/client). -### Huawei (HarmonyOS / EMUI) +### Huawei Messaging Service (HarmonyOS / EMUI) For Huawei devices, you can also register your app to receive push notifications using Huawei Push Kit. To integrate Huawei Push Kit into your app, please refer to the Huawei Push Kit documentation. ```kotlin @@ -100,11 +95,11 @@ try { val token = HmsInstanceId.getInstance(appContext).getToken(appId, "HCM") if (token.isNotEmpty()) { - pushService.registerHuawei(token) { + pushService.register(PushData.hms(token)) { it.onSuccess { // push notification registered }.onFailure { - // push notification registration failed + // push notification registration failed } } else { // token retrieval failed @@ -114,6 +109,7 @@ try { } ``` For more information visit [official documentation](https://developer.huawei.com/consumer/en/doc/hmscore-guides/android-client-dev-0000001050042041) + ## Receiving WMT Push Notifications To process the raw notification obtained from the Firebase Cloud Messaging service (FCM) or from the HUAWEI Mobile Services (HMS), you can use the `PushParser` helper class that will parse the notification into a `PushMessage` result. diff --git a/library/gradle.properties b/library/gradle.properties index 12eb04e..ac61b02 100644 --- a/library/gradle.properties +++ b/library/gradle.properties @@ -14,6 +14,6 @@ # and limitations under the License. # -VERSION_NAME=1.12.0-SNAPSHOT +VERSION_NAME=1.12.1-SNAPSHOT GROUP_ID=com.wultra.android.mtokensdk ARTIFACT_ID=wultra-mtoken-sdk diff --git a/library/src/main/java/com/wultra/android/mtokensdk/api/push/model/PushRegistrationRequestObject.kt b/library/src/main/java/com/wultra/android/mtokensdk/api/push/model/PushRegistrationRequestObject.kt index 1a2ce93..3c3790b 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/api/push/model/PushRegistrationRequestObject.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/api/push/model/PushRegistrationRequestObject.kt @@ -26,15 +26,24 @@ internal data class PushRegistrationRequestObject( val token: String, @SerializedName("platform") - val platform: Platform = Platform.ANDROID -) -/** - * Enum representing the platform for push registration. - */ -internal enum class Platform { - @SerializedName("android") - ANDROID, + val platform: Platform +) { + /** + * Enum representing the platform for push registration. + */ + internal enum class Platform { + @SerializedName("fcm") + FCM, + + @SerializedName("hms") + HMS, + + // legacy support for Android + @SerializedName("android") + ANDROID, - @SerializedName("huawei") - HUAWEI + // legacy support for Huawei + @SerializedName("huawei") + HUAWEI, + } } diff --git a/library/src/main/java/com/wultra/android/mtokensdk/push/Deprecated.kt b/library/src/main/java/com/wultra/android/mtokensdk/push/Deprecated.kt deleted file mode 100644 index 7bb74f4..0000000 --- a/library/src/main/java/com/wultra/android/mtokensdk/push/Deprecated.kt +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright 2022 Wultra s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -@file:Suppress("DEPRECATION") - -package com.wultra.android.mtokensdk.push - -import com.wultra.android.mtokensdk.api.apiErrorForListener -import com.wultra.android.powerauth.networking.error.ApiError - -// Deprecated interface based API - -/** - * Listener for [IPushService.register] method. - */ -@Deprecated("Use function with Result callback as a replacement") // 1.5.0 -interface IPushRegisterListener { - /** - * Called when FCM token was registered on backend. - */ - fun onSuccess() - - /** - * Called when FCM token fails to register on backend. - */ - fun onFailure(e: ApiError) -} - -/** - * Registers FCM on backend to receive notifications about operations - * @param fcmToken Firebase Cloud Messaging Token - * @param listener Result listener - */ -@Deprecated("Use function with Result callback as a replacement") // 1.5.0 -fun IPushService.register(fcmToken: String, listener: IPushRegisterListener) { - register(fcmToken) { result -> - result.onSuccess { - listener.onSuccess() - }.onFailure { - listener.onFailure(it.apiErrorForListener()) - } - } -} diff --git a/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt b/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt index 5635b35..5e86055 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt @@ -41,16 +41,34 @@ interface IPushService { var okHttpInterceptor: OkHttpBuilderInterceptor? /** - * Registers FCM on backend to receive notifications about operations + * Registers token on the PowerAuth backend to receive notifications about operations and inbox. + * + * @param data Token and push platform (Firebase Cloud Messaging or Huawei Push) + * @param callback Result callback + */ + fun register(data: PushData, callback: (result: Result) -> Unit) + + // deprecated APIs + + /** + * Registers FCM on backend to receive notifications about operations. + * + * This method is compatible with server stack `1.9.x` + * * @param fcmToken Firebase Cloud Messaging Token * @param callback Result listener */ + @Deprecated("This method is deprecated since the server version 1.10.0. Use `register` with `data` parameter as a replacement") // deprecated in 1.13.0 fun register(fcmToken: String, callback: (result: Result) -> Unit) /** * Registers HMS on backend to receive notifications about operations + * + * This method is compatible with server stack `1.9.x` + * * @param hmsToken Huawei Push Token * @param callback Result listener */ + @Deprecated("This method is deprecated since the server version 1.10.0. Use `register` with `data` parameter as a replacement") //deprecated in 1.13.0 fun registerHuawei(hmsToken: String, callback: (result: Result) -> Unit) } diff --git a/library/src/main/java/com/wultra/android/mtokensdk/push/PushData.kt b/library/src/main/java/com/wultra/android/mtokensdk/push/PushData.kt new file mode 100644 index 0000000..e097181 --- /dev/null +++ b/library/src/main/java/com/wultra/android/mtokensdk/push/PushData.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, Wultra s.r.o. (www.wultra.com). + * + * All rights reserved. This source code can be used only for purposes specified + * by the given license contract signed by the rightful deputy of Wultra s.r.o. + * This source code can be used only by the owner of the license. + * + * Any disputes arising in respect of this agreement (license) shall be brought + * before the Municipal Court of Prague. + */ + +package com.wultra.android.mtokensdk.push + +/** + * Push data to register. + * + * @property token Token received from the Push Provider + * @property platform Push Provider (Firebase Cloud Messaging for Android) + */ +data class PushData( + val token: String, + val platform: PushPlatform +) { + companion object { + /** + * Creates data for Firebase Cloud Messaging. + * + * @param token FCM token + */ + @JvmStatic fun fcm(token: String) = PushData(token, PushPlatform.FCM) + + /** + * Creates data for Huawei Messaging Service. + * + * @param token HMS token + */ + @JvmStatic fun hms(token: String) = PushData(token, PushPlatform.HMS) + } +} + +/** + * Push provider which you're registering to. + */ +enum class PushPlatform { + /** Firebase Cloud Messaging */ + FCM, + /** Huawei Messaging Service */ + HMS +} diff --git a/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt b/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt index 3001c54..a17f8bb 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt @@ -19,7 +19,6 @@ package com.wultra.android.mtokensdk.push import android.content.Context import com.wultra.android.mtokensdk.api.push.PushApi import com.wultra.android.mtokensdk.api.push.PushRegistrationRequest -import com.wultra.android.mtokensdk.api.push.model.Platform import com.wultra.android.mtokensdk.api.push.model.PushRegistrationRequestObject import com.wultra.android.mtokensdk.log.WMTLogger import com.wultra.android.powerauth.networking.IApiCallResponseListener @@ -75,9 +74,18 @@ class PushService(okHttpClient: OkHttpClient, baseURL: String, powerAuthSDK: Pow private val pushApi = PushApi(okHttpClient, baseURL, powerAuthSDK, appContext, tokenProvider, userAgent) - override fun register(fcmToken: String, callback: (Result) -> Unit) { + override fun register(data: PushData, callback: (result: Result) -> Unit) { + val platform = when (data.platform) { + PushPlatform.FCM -> PushRegistrationRequestObject.Platform.FCM + PushPlatform.HMS -> PushRegistrationRequestObject.Platform.HMS + } + + register(data.token, platform, callback) + } + + private fun register(token: String, platform: PushRegistrationRequestObject.Platform, callback: (Result) -> Unit) { pushApi.registerToken( - PushRegistrationRequest(PushRegistrationRequestObject(fcmToken)), + PushRegistrationRequest(PushRegistrationRequestObject(token, platform)), object : IApiCallResponseListener { override fun onSuccess(result: StatusResponse) { callback(Result.success(Unit)) @@ -91,19 +99,15 @@ class PushService(okHttpClient: OkHttpClient, baseURL: String, powerAuthSDK: Pow ) } - override fun registerHuawei(hmsToken: String, callback: (result: Result) -> Unit) { - pushApi.registerToken( - PushRegistrationRequest(PushRegistrationRequestObject(hmsToken, Platform.HUAWEI)), - object : IApiCallResponseListener { - override fun onSuccess(result: StatusResponse) { - callback(Result.success(Unit)) - } + // deprecated API - override fun onFailure(error: ApiError) { - WMTLogger.e("Failed to register hms token for WMT push notifications.") - callback(Result.failure(ApiErrorException(error))) - } - } - ) + @Deprecated("Use register function with `data` parameter as a replacement") + override fun register(fcmToken: String, callback: (Result) -> Unit) { + register(fcmToken, PushRegistrationRequestObject.Platform.ANDROID, callback) + } + + @Deprecated("Use register function with `data` parameter as a replacement") + override fun registerHuawei(hmsToken: String, callback: (result: Result) -> Unit) { + register(hmsToken, PushRegistrationRequestObject.Platform.HUAWEI, callback) } } diff --git a/library/src/test/java/OperationJsonDeserializationTests.kt b/library/src/test/java/JsonDeserializationTests.kt similarity index 93% rename from library/src/test/java/OperationJsonDeserializationTests.kt rename to library/src/test/java/JsonDeserializationTests.kt index c24cde1..b265c0d 100644 --- a/library/src/test/java/OperationJsonDeserializationTests.kt +++ b/library/src/test/java/JsonDeserializationTests.kt @@ -21,6 +21,8 @@ import com.google.gson.GsonBuilder import com.google.gson.TypeAdapter import com.google.gson.reflect.TypeToken import com.wultra.android.mtokensdk.api.operation.model.* +import com.wultra.android.mtokensdk.api.push.PushRegistrationRequest +import com.wultra.android.mtokensdk.api.push.model.PushRegistrationRequestObject import com.wultra.android.powerauth.networking.data.StatusResponse import org.junit.Assert import org.junit.Before @@ -28,7 +30,7 @@ import org.junit.Test import org.threeten.bp.ZonedDateTime import java.math.BigDecimal -class OperationJsonDeserializationTests { +class JsonDeserializationTests { private lateinit var gson: Gson private lateinit var typeAdapter: TypeAdapter @@ -393,4 +395,44 @@ class OperationJsonDeserializationTests { Assert.assertEquals("Payment was rejected", resultTexts2?.reject) Assert.assertEquals("Payment approval failed", resultTexts2?.failure) } + + @Test + fun `test push legacy android notification`() { + val json = """ + {"requestObject":{"platform":"android","token":"testtoken"}} + """ + val o = gson.fromJson(json, PushRegistrationRequest::class.java) + Assert.assertEquals("testtoken", o.requestObject.token) + Assert.assertEquals(PushRegistrationRequestObject.Platform.ANDROID, o.requestObject.platform) + } + + @Test + fun `test push legacy huawei notification`() { + val json = """ + {"requestObject":{"platform":"huawei","token":"testtoken"}} + """ + val o = gson.fromJson(json, PushRegistrationRequest::class.java) + Assert.assertEquals("testtoken", o.requestObject.token) + Assert.assertEquals(PushRegistrationRequestObject.Platform.HUAWEI, o.requestObject.platform) + } + + @Test + fun `test push fcm notification`() { + val json = """ + {"requestObject":{"platform":"fcm","token":"testtoken"}} + """ + val o = gson.fromJson(json, PushRegistrationRequest::class.java) + Assert.assertEquals("testtoken", o.requestObject.token) + Assert.assertEquals(PushRegistrationRequestObject.Platform.FCM, o.requestObject.platform) + } + + @Test + fun `test push hms notification`() { + val json = """ + {"requestObject":{"platform":"hms","token":"testtoken"}} + """ + val o = gson.fromJson(json, PushRegistrationRequest::class.java) + Assert.assertEquals("testtoken", o.requestObject.token) + Assert.assertEquals(PushRegistrationRequestObject.Platform.HMS, o.requestObject.platform) + } } From adafc7b6882f7887b76085eaa07b1370c1172258 Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Tue, 19 Nov 2024 13:33:19 +0100 Subject: [PATCH 2/6] #166 Fixes #166: Docs typos --- docs/Using-Operations-Service.md | 2 +- .../android/mtokensdk/api/operation/model/PostApprovalScreen.kt | 2 +- .../mtokensdk/operation/expiration/ExpirableOperation.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/Using-Operations-Service.md b/docs/Using-Operations-Service.md index 7bece4c..e6ae6fb 100644 --- a/docs/Using-Operations-Service.md +++ b/docs/Using-Operations-Service.md @@ -631,7 +631,7 @@ When the `UserOperation` contains a `PreApprovalScreen.QR_SCAN`, the app should When the app is launched via a deeplink, preserve the data from the deeplink and extract the relevant data. When operations are loaded compare the operation ID from the deeplink data to the operations within the app to find a match. - Assign TOTP and Type to the Operation - Once the QR code is scanned or a match from the deeplink is found, create a `WMTProximityCheck` with: + Once the QR code is scanned or a match from the deeplink is found, create a `ProximityCheck` with: - `totp`: The actual Time-Based One-Time Password. - `type`: Set to `ProximityCheckType.QR_CODE` or `ProximityCheckType.DEEPLINK`. - `timestampReceived`: The timestamp when the QR code was scanned (by default, it is created as the current timestamp when the object is instantiated). diff --git a/library/src/main/java/com/wultra/android/mtokensdk/api/operation/model/PostApprovalScreen.kt b/library/src/main/java/com/wultra/android/mtokensdk/api/operation/model/PostApprovalScreen.kt index 74bd9aa..4731e2b 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/api/operation/model/PostApprovalScreen.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/api/operation/model/PostApprovalScreen.kt @@ -29,7 +29,7 @@ import com.wultra.android.mtokensdk.operation.JSONValue open class PostApprovalScreen( /** - * type of PostApprovalScreen is presented with different classes (Starting with `WMTPreApprovalScreen*`) + * type of PostApprovalScreen is presented with different classes (Starting with `PreApprovalScreen*`) */ val type: Type ) { diff --git a/library/src/main/java/com/wultra/android/mtokensdk/operation/expiration/ExpirableOperation.kt b/library/src/main/java/com/wultra/android/mtokensdk/operation/expiration/ExpirableOperation.kt index 4e19451..80328c7 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/operation/expiration/ExpirableOperation.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/operation/expiration/ExpirableOperation.kt @@ -37,7 +37,7 @@ interface ExpirableOperation { return if (this is IOperation && other is IOperation) { id == other.id && data == other.data && expires == other.expires } else { - WMTLogger.w("ExpirableOperation: Fallbacked to comparing `WMTExpirableOperation`s by reference.") + WMTLogger.w("ExpirableOperation: Fallbacked to comparing `ExpirableOperation`s by reference.") this === other } } From c084f9aca71ade6410f02ba9a1b5e9ef074af326 Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Tue, 19 Nov 2024 14:13:36 +0100 Subject: [PATCH 3/6] Push tests --- .github/workflows/tests.yml | 3 +- .../src/main/kotlin/ProjectConfiguration.kt | 1 + configs/integration-tests.properties | 1 + library/src/androidTest/java/InboxTests.kt | 6 +-- .../src/androidTest/java/IntegrationTests.kt | 48 ++++++++++++++++++- .../java/IntegrationTestsDeprecated.kt | 4 +- .../src/androidTest/java/IntegrationUtils.kt | 12 +++-- 7 files changed, 64 insertions(+), 11 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7a8b20c..0180a2f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -32,7 +32,8 @@ jobs: ER_URL: ${{ secrets.TESTS_ER_URL }} OP_URL: ${{ secrets.TESTS_OP_URL }} IN_URL: ${{ secrets.TESTS_IN_URL }} - run: echo -e tests.sdk.cloudServerUrl="$CL_URL"\\ntests.sdk.cloudServerLogin="$CL_LGN"\\ntests.sdk.cloudServerPassword="$CL_PWD"\\ntests.sdk.cloudApplicationId="$CL_AID"\\ntests.sdk.enrollmentServerUrl="$ER_URL"\\ntests.sdk.operationsServerUrl="$OP_URL"\\ntests.sdk.inboxServerUrl="$IN_URL"\\ntests.sdk.sdkConfig="$SDK_CONFIG" > configs/integration-tests.properties + PU_URL: ${{ secrets.TESTS_PU_URL }} + run: echo -e tests.sdk.cloudServerUrl="$CL_URL"\\ntests.sdk.cloudServerLogin="$CL_LGN"\\ntests.sdk.cloudServerPassword="$CL_PWD"\\ntests.sdk.cloudApplicationId="$CL_AID"\\ntests.sdk.enrollmentServerUrl="$ER_URL"\\ntests.sdk.operationsServerUrl="$OP_URL"\\ntests.sdk.inboxServerUrl="$IN_URL"\\ntests.sdk.pushServerUrl="$PU_URL"\\ntests.sdk.sdkConfig="$SDK_CONFIG" > configs/integration-tests.properties - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules diff --git a/buildSrc/src/main/kotlin/ProjectConfiguration.kt b/buildSrc/src/main/kotlin/ProjectConfiguration.kt index e7b0e4b..8ef09cb 100644 --- a/buildSrc/src/main/kotlin/ProjectConfiguration.kt +++ b/buildSrc/src/main/kotlin/ProjectConfiguration.kt @@ -46,6 +46,7 @@ fun loadInstrumentationTestConfigProperties(project: Project, defaultConfig: Def "tests.sdk.enrollmentServerUrl", "tests.sdk.operationsServerUrl", "tests.sdk.inboxServerUrl", + "tests.sdk.pushServerUrl", "tests.sdk.sdkConfig" ) diff --git a/configs/integration-tests.properties b/configs/integration-tests.properties index 23953bb..d968887 100644 --- a/configs/integration-tests.properties +++ b/configs/integration-tests.properties @@ -11,6 +11,7 @@ tests.sdk.cloudApplicationId=dev tests.sdk.enrollmentServerUrl=http://localhost/enrollment-server tests.sdk.operationsServerUrl=http://localhost/enrollment-server tests.sdk.inboxServerUrl=http://localhost/enrollment-server +tests.sdk.pushServerUrl=http://localhost/enrollment-server # Configuration of the PowerAuth server tests.sdk.sdkConfig= \ No newline at end of file diff --git a/library/src/androidTest/java/InboxTests.kt b/library/src/androidTest/java/InboxTests.kt index c3e88db..c488441 100644 --- a/library/src/androidTest/java/InboxTests.kt +++ b/library/src/androidTest/java/InboxTests.kt @@ -19,9 +19,9 @@ class InboxTests { fun setup() { try { val result = IntegrationUtils.prepareActivation(pin) - pa = result.first - ops = result.second - inbox = result.third + pa = result.pa + ops = result.ops + inbox = result.inbox } catch (e: Throwable) { fail("Activation preparation failed: $e") } diff --git a/library/src/androidTest/java/IntegrationTests.kt b/library/src/androidTest/java/IntegrationTests.kt index a3515c8..23bd695 100644 --- a/library/src/androidTest/java/IntegrationTests.kt +++ b/library/src/androidTest/java/IntegrationTests.kt @@ -24,6 +24,8 @@ import com.wultra.android.mtokensdk.api.operation.model.UserOperation import com.wultra.android.mtokensdk.api.operation.model.UserOperationStatus import com.wultra.android.mtokensdk.operation.* import com.wultra.android.mtokensdk.operation.RejectionData +import com.wultra.android.mtokensdk.push.IPushService +import com.wultra.android.mtokensdk.push.PushData import com.wultra.android.powerauth.networking.error.ApiError import io.getlime.security.powerauth.sdk.PowerAuthAuthentication import io.getlime.security.powerauth.sdk.PowerAuthSDK @@ -40,6 +42,7 @@ import java.util.concurrent.TimeUnit class IntegrationTests { private lateinit var ops: IOperationsService + private lateinit var push: IPushService private lateinit var pa: PowerAuthSDK private val pin = "1234" @@ -47,8 +50,9 @@ class IntegrationTests { fun setup() { try { val result = IntegrationUtils.prepareActivation(pin) - pa = result.first - ops = result.second + pa = result.pa + ops = result.ops + push = result.push } catch (e: Throwable) { Assert.fail("Activation preparation failed: $e") } @@ -324,4 +328,44 @@ class IntegrationTests { Assert.assertNotNull(opRecord) Assert.assertTrue("${opRecord?.statusReason} should be PREARRANGED_REASON", opRecord?.statusReason == "PREARRANGED_REASON") } + + @Test + fun testRegisterPushLegacyAndroid() { + val future = CompletableFuture() + push.register("testToken") { result -> + result.onSuccess { future.complete(null) } + .onFailure { future.completeExceptionally(it) } + } + Assert.assertNull(future.get(10, TimeUnit.SECONDS)) + } + + @Test + fun testRegisterPushLegacyHuawei() { + val future = CompletableFuture() + push.registerHuawei("testToken") { result -> + result.onSuccess { future.complete(null) } + .onFailure { future.completeExceptionally(it) } + } + Assert.assertNull(future.get(10, TimeUnit.SECONDS)) + } + + @Test + fun testRegisterPushFCM() { + val future = CompletableFuture() + push.register(PushData.fcm("testToken")) { result -> + result.onSuccess { future.complete(null) } + .onFailure { future.completeExceptionally(it) } + } + Assert.assertNull(future.get(10, TimeUnit.SECONDS)) + } + + @Test + fun testRegisterPushHMS() { + val future = CompletableFuture() + push.register(PushData.hms("testToken")) { result -> + result.onSuccess { future.complete(null) } + .onFailure { future.completeExceptionally(it) } + } + Assert.assertNull(future.get(10, TimeUnit.SECONDS)) + } } diff --git a/library/src/androidTest/java/IntegrationTestsDeprecated.kt b/library/src/androidTest/java/IntegrationTestsDeprecated.kt index 37dded2..f3be8e9 100644 --- a/library/src/androidTest/java/IntegrationTestsDeprecated.kt +++ b/library/src/androidTest/java/IntegrationTestsDeprecated.kt @@ -38,8 +38,8 @@ class IntegrationTestsDeprecated { fun setup() { try { val result = IntegrationUtils.prepareActivation(pin) - pa = result.first - ops = result.second + pa = result.pa + ops = result.ops } catch (e: Throwable) { Assert.fail("Activation preparation failed: $e") } diff --git a/library/src/androidTest/java/IntegrationUtils.kt b/library/src/androidTest/java/IntegrationUtils.kt index 75997f5..d4b0cc0 100644 --- a/library/src/androidTest/java/IntegrationUtils.kt +++ b/library/src/androidTest/java/IntegrationUtils.kt @@ -31,6 +31,8 @@ import com.wultra.android.mtokensdk.inbox.IInboxService import com.wultra.android.mtokensdk.inbox.createInboxService import com.wultra.android.mtokensdk.operation.IOperationsService import com.wultra.android.mtokensdk.operation.createOperationsService +import com.wultra.android.mtokensdk.push.IPushService +import com.wultra.android.mtokensdk.push.createPushService import com.wultra.android.powerauth.networking.ssl.SSLValidationStrategy import io.getlime.security.powerauth.core.ActivationCodeUtil import io.getlime.security.powerauth.networking.response.CreateActivationResult @@ -68,6 +70,8 @@ class TimestampAdapter: TypeAdapter() { class IntegrationUtils { + data class ActivationResult(val pa: PowerAuthSDK, val ops: IOperationsService, val inbox: IInboxService, val push: IPushService) + companion object { val context: Context = ApplicationProvider.getApplicationContext() private val client = OkHttpClient.Builder().build() @@ -81,12 +85,13 @@ class IntegrationUtils { private val enrollmentUrl = getInstrumentationParameter("enrollmentServerUrl") private val operationsUrl = getInstrumentationParameter("operationsServerUrl") private val inboxUrl = getInstrumentationParameter("inboxServerUrl") + private val pushUrl = getInstrumentationParameter("pushServerUrl") private val sdkConfig = getInstrumentationParameter("sdkConfig") private var activationName = "" // will be filled when activation is created private var registrationId = "" // will be filled when activation is created @Throws - fun prepareActivation(pin: String, userId: String? = null): Triple { + fun prepareActivation(pin: String, userId: String? = null): ActivationResult { // Be sure that each activation has its own user activationName = userId ?: UUID.randomUUID().toString() @@ -150,10 +155,11 @@ class IntegrationUtils { .trimIndent() makeCall(bodyCommit, "$cloudServerUrl/v2/registrations/${resp.registrationId}/commit") - return Triple( + return ActivationResult( pa, pa.createOperationsService(context, operationsUrl, SSLValidationStrategy.system()), - pa.createInboxService(context, inboxUrl, SSLValidationStrategy.system()) + pa.createInboxService(context, inboxUrl, SSLValidationStrategy.system()), + pa.createPushService(context, pushUrl, SSLValidationStrategy.system()) ) } From 599416af47f737df7e469e92bf0fa1a4f2dbbe03 Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Tue, 19 Nov 2024 14:16:59 +0100 Subject: [PATCH 4/6] Fix ktlint --- .../main/java/com/wultra/android/mtokensdk/push/IPushService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt b/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt index 5e86055..3dfb67e 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/push/IPushService.kt @@ -69,6 +69,6 @@ interface IPushService { * @param hmsToken Huawei Push Token * @param callback Result listener */ - @Deprecated("This method is deprecated since the server version 1.10.0. Use `register` with `data` parameter as a replacement") //deprecated in 1.13.0 + @Deprecated("This method is deprecated since the server version 1.10.0. Use `register` with `data` parameter as a replacement") // deprecated in 1.13.0 fun registerHuawei(hmsToken: String, callback: (result: Result) -> Unit) } From f263ac7d86873ecb2e069913d79a234a3b5b5900 Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Tue, 19 Nov 2024 16:06:31 +0100 Subject: [PATCH 5/6] Docs improvements --- docs/Using-Inbox-Service.md | 4 ++-- docs/Using-Operations-Service.md | 4 ++-- docs/Using-Push-Service.md | 4 ++-- .../android/mtokensdk/inbox/InboxService.kt | 20 +++++++++++++++++++ .../mtokensdk/operation/OperationsService.kt | 6 ++++-- .../android/mtokensdk/push/PushService.kt | 8 ++++++-- 6 files changed, 36 insertions(+), 10 deletions(-) diff --git a/docs/Using-Inbox-Service.md b/docs/Using-Inbox-Service.md index b03dc6c..3dd8e45 100644 --- a/docs/Using-Inbox-Service.md +++ b/docs/Using-Inbox-Service.md @@ -32,7 +32,7 @@ fun PowerAuthSDK.createInboxService(appContext: Context, baseURL: String, strate ``` - `appContext` - application context -- `baseURL` - address, where your operations server can be reached +- `baseURL` - address, where your operations server can be reached (ending with `/enrollment-server` in the default setup) - `strategy` - a strategy used when validating HTTPS requests. The following strategies can be used: - `SSLValidationStrategy.default` - `SSLValidationStrategy.noValidation` @@ -51,7 +51,7 @@ fun PowerAuthSDK.createInboxService(appContext: Context, baseURL: String, httpCl ``` - `appContext` - application context -- `baseURL`- address, where your operations server can be reached +- `baseURL`- address, where your operations server can be reached (ending with `/enrollment-server` in the default setup) - `httpClient` - [`OkHttpClient`](https://square.github.io/okhttp/) instance used for API requests __Optional parameters:__ diff --git a/docs/Using-Operations-Service.md b/docs/Using-Operations-Service.md index e6ae6fb..f465f12 100644 --- a/docs/Using-Operations-Service.md +++ b/docs/Using-Operations-Service.md @@ -39,7 +39,7 @@ fun PowerAuthSDK.createOperationsService(appContext: Context, baseURL: String, s ``` - `appContext` - application context -- `baseURL` - address, where your operations server can be reached +- `baseURL` - address, where your operations server can be reached (ending with `/enrollment-server` in the default setup) - `strategy` - a strategy used when validating HTTPS requests. The following strategies can be used: - `SSLValidationStrategy.default` - `SSLValidationStrategy.noValidation` @@ -59,7 +59,7 @@ fun PowerAuthSDK.createOperationsService(appContext: Context, baseURL: String, h ``` - `appContext` - application context -- `baseURL`- address, where your operations server can be reached +- `baseURL`- address, where your operations server can be reached (ending with `/enrollment-server` in the default setup) - `httpClient` - [`OkHttpClient`](https://square.github.io/okhttp/) instance used for API requests __Optional parameters:__ diff --git a/docs/Using-Push-Service.md b/docs/Using-Push-Service.md index 717cac2..6504a34 100644 --- a/docs/Using-Push-Service.md +++ b/docs/Using-Push-Service.md @@ -28,7 +28,7 @@ fun PowerAuthSDK.createPushService(appContext: Context, baseURL: String, strateg ``` - `appContext` - application context -- `baseURL` - address, where your operations server can be reached +- `baseURL` - address, where your operations server can be reached (ending with `/enrollment-server` in the default setup) - `strategy` - a strategy used when validating HTTPS requests. The following strategies can be used: - `SSLValidationStrategy.default` - `SSLValidationStrategy.noValidation` @@ -44,7 +44,7 @@ __Optional parameters:__ fun PowerAuthSDK.createPushService(appContext: Context, baseURL: String, httpClient: OkHttpClient): IPushService ``` - `appContext` - application context -- `baseURL` - address, where your operations server can be reached +- `baseURL` - address, where your operations server can be reached (ending with `/enrollment-server` in the default setup) - `httpClient` - [`OkHttpClient`](https://square.github.io/okhttp/) instance used for API requests __Optional parameters:__ diff --git a/library/src/main/java/com/wultra/android/mtokensdk/inbox/InboxService.kt b/library/src/main/java/com/wultra/android/mtokensdk/inbox/InboxService.kt index 81eb77a..c7f9dcc 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/inbox/InboxService.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/inbox/InboxService.kt @@ -33,10 +33,30 @@ import com.wultra.android.powerauth.networking.tokens.IPowerAuthTokenProvider import io.getlime.security.powerauth.sdk.PowerAuthSDK import okhttp3.OkHttpClient +/** + * Convenience factory method to create an IInboxService instance + * from given PowerAuthSDK instance. + * + * @param appContext Application Context + * @param baseURL Base URL for inbox request (ending with `/enrollment-server` in the default setup) + * @param okHttpClient HTTP client instance for networking + * @param userAgent Default user agent for each request. + * @return IInboxService instance + */ fun PowerAuthSDK.createInboxService(appContext: Context, baseURL: String, okHttpClient: OkHttpClient, userAgent: UserAgent? = null): IInboxService { return InboxService(okHttpClient, baseURL, this, appContext, null, userAgent) } +/** + * Convenience factory method to create an IInboxService instance + * from given PowerAuthSDK instance. + * + * @param appContext Application Context + * @param baseURL Base URL for inbox request (ending with `/enrollment-server` in the default setup) + * @param strategy SSL validation strategy for networking + * @param userAgent Default user agent for each request. + * @return IInboxService instance + */ fun PowerAuthSDK.createInboxService(appContext: Context, baseURL: String, strategy: SSLValidationStrategy, userAgent: UserAgent? = null): IInboxService { val builder = OkHttpClient.Builder() strategy.configure(builder) diff --git a/library/src/main/java/com/wultra/android/mtokensdk/operation/OperationsService.kt b/library/src/main/java/com/wultra/android/mtokensdk/operation/OperationsService.kt index d2dd1da..44519f7 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/operation/OperationsService.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/operation/OperationsService.kt @@ -43,11 +43,12 @@ import java.util.* * from given PowerAuthSDK instance. * * @param appContext Application Context object. - * @param baseURL Base URL where the operations endpoint rests. + * @param baseURL Base URL where the operations endpoint rests (ending with `/enrollment-server` in the default setup). * @param httpClient OkHttpClient for API communication. * @param userAgent Default user agent for each request. * @param gsonBuilder Custom GSON builder for deserialization of request. If you want to provide or own * deserialization logic, we recommend adding to the instance obtained from the OperationsUtils.defaultGsonBuilder(). + * @return IOperationsService instance */ fun PowerAuthSDK.createOperationsService(appContext: Context, baseURL: String, httpClient: OkHttpClient, userAgent: UserAgent? = null, gsonBuilder: GsonBuilder? = null): IOperationsService { return OperationsService(this, appContext, httpClient, baseURL, null, userAgent, gsonBuilder) @@ -58,11 +59,12 @@ fun PowerAuthSDK.createOperationsService(appContext: Context, baseURL: String, h * from given PowerAuthSDK instance. * * @param appContext Application Context object. - * @param baseURL Base URL where the operations endpoint rests. + * @param baseURL Base URL where the operations endpoint rests (ending with `/enrollment-server` in the default setup). * @param strategy SSL validation strategy for networking. * @param userAgent Default user agent for each request. * @param gsonBuilder Custom GSON builder for deserialization of request. If you want to provide or own * deserialization logic, we recommend adding to the instance obtained from the OperationsUtils.defaultGsonBuilder(). + * @return IOperationsService instance */ fun PowerAuthSDK.createOperationsService(appContext: Context, baseURL: String, strategy: SSLValidationStrategy = SSLValidationStrategy.system(), userAgent: UserAgent? = null, gsonBuilder: GsonBuilder? = null): IOperationsService { val builder = OkHttpClient.Builder() diff --git a/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt b/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt index a17f8bb..dbf4dc3 100644 --- a/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt +++ b/library/src/main/java/com/wultra/android/mtokensdk/push/PushService.kt @@ -37,8 +37,10 @@ import okhttp3.OkHttpClient * from given PowerAuthSDK instance. * * @param appContext Application Context - * @param baseURL Base URL for push request + * @param baseURL Base URL for push request (ending with `/enrollment-server` in the default setup) * @param okHttpClient HTTP client instance for networking + * @param userAgent Default user agent for each request. + * @return IPushService instance */ fun PowerAuthSDK.createPushService(appContext: Context, baseURL: String, okHttpClient: OkHttpClient, userAgent: UserAgent? = null): IPushService { return PushService(okHttpClient, baseURL, this, appContext, null, userAgent) @@ -49,8 +51,10 @@ fun PowerAuthSDK.createPushService(appContext: Context, baseURL: String, okHttpC * from given PowerAuthSDK instance. * * @param appContext Application Context - * @param baseURL Base URL for push request + * @param baseURL Base URL for push request (ending with `/enrollment-server` in the default setup) * @param strategy SSL validation strategy for networking + * @param userAgent Default user agent for each request. + * @return IPushService instance */ fun PowerAuthSDK.createPushService(appContext: Context, baseURL: String, strategy: SSLValidationStrategy, userAgent: UserAgent? = null): IPushService { val builder = OkHttpClient.Builder() From a1d50cac93ac00d32798cb5f45e6706c12467b6e Mon Sep 17 00:00:00 2001 From: Jan Kobersky Date: Wed, 27 Nov 2024 09:35:54 +0100 Subject: [PATCH 6/6] Fixed docs --- docs/Using-Push-Service.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/Using-Push-Service.md b/docs/Using-Push-Service.md index 6504a34..b014e49 100644 --- a/docs/Using-Push-Service.md +++ b/docs/Using-Push-Service.md @@ -101,6 +101,7 @@ try { }.onFailure { // push notification registration failed } + } } else { // token retrieval failed }