react-native-klarna-inapp-sdk 2.4.2 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/android/build.gradle +10 -3
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/checkout/KlarnaCheckoutViewEventHandler.java +14 -1
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/checkout/KlarnaCheckoutViewEventSender.java +1 -2
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/checkout/KlarnaCheckoutViewManager.java +31 -9
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/event/ComponentEventSender.java +8 -10
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/event/KlarnaEventHandlerEventsUtil.java +5 -7
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/serializer/DynamicMapSerializer.kt +138 -0
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/ui/ResizeObserverWrapperView.java +28 -0
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/util/ArgumentsUtil.java +2 -3
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/util/ParserUtil.kt +27 -0
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/signin/KlarnaSignInEventsMapper.java +37 -8
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/signin/model/KlarnaSignInTokenSerializable.kt +32 -0
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/standalonewebview/KlarnaStandaloneWebViewEventSender.java +3 -3
- package/android/src/test/java/com/klarna/mobile/sdk/reactnative/KlarnaMobileSDKPackageTest.java +3 -2
- package/android/src/test/java/com/klarna/mobile/sdk/reactnative/common/event/ComponentEventSenderTest.java +3 -4
- package/android/src/test/java/com/klarna/mobile/sdk/reactnative/common/serializer/DynamicMapSerializerTest.kt +229 -0
- package/android/src/test/java/com/klarna/mobile/sdk/reactnative/common/util/ParserUtilTest.kt +39 -0
- package/android/src/test/java/com/klarna/mobile/sdk/reactnative/payments/KlarnaPaymentViewManagerTest.java +2 -2
- package/package.json +8 -1
- package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/util/ParserUtil.java +0 -36
package/README.md
CHANGED
|
@@ -27,8 +27,8 @@ This repository also includes a test application that you can use to see how it
|
|
|
27
27
|
|
|
28
28
|
### Documentations
|
|
29
29
|
|
|
30
|
-
* [Overview](https://docs.klarna.com/mobile-
|
|
31
|
-
* [Using the Mobile SDK on React Native](https://docs.klarna.com/mobile-sdk/reactnative/)
|
|
30
|
+
* [Overview](https://docs.klarna.com/payments/mobile-payments/before-you-start/introduction-mobile-integrations/)
|
|
31
|
+
* [Using the Mobile SDK on React Native](https://docs.klarna.com/payments/mobile-payments/integrate-with-mobile-sdk/reactnative/native-view/)
|
|
32
32
|
|
|
33
33
|
## Getting started
|
|
34
34
|
|
package/android/build.gradle
CHANGED
|
@@ -31,8 +31,6 @@ def isNewArchitectureEnabled() {
|
|
|
31
31
|
return rootProject.hasProperty("newArchEnabled") && rootProject.getProperty("newArchEnabled") == "true"
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
def appProject = rootProject.allprojects.find { it.plugins.hasPlugin('com.android.application') }
|
|
35
|
-
|
|
36
34
|
def supportsNamespace() {
|
|
37
35
|
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
|
38
36
|
def major = parsed[0].toInteger()
|
|
@@ -46,6 +44,9 @@ def supportsNamespace() {
|
|
|
46
44
|
return major >= 8
|
|
47
45
|
}
|
|
48
46
|
|
|
47
|
+
plugins {
|
|
48
|
+
id "org.jetbrains.kotlin.plugin.serialization" version "2.2.0"
|
|
49
|
+
}
|
|
49
50
|
apply plugin: 'com.android.library'
|
|
50
51
|
apply plugin: 'kotlin-android'
|
|
51
52
|
if (isNewArchitectureEnabled()) {
|
|
@@ -109,13 +110,19 @@ dependencies {
|
|
|
109
110
|
//noinspection GradleDynamicVersion
|
|
110
111
|
implementation "com.facebook.react:react-native:${getExtOrFallback('reactnativeVersion', '+')}"
|
|
111
112
|
|
|
112
|
-
implementation
|
|
113
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib:${getExtOrDefault('kotlinVersion')}"
|
|
114
|
+
implementation "org.jetbrains.kotlin:kotlin-reflect:${getExtOrDefault('kotlinVersion')}"
|
|
115
|
+
implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.9.0'
|
|
116
|
+
|
|
117
|
+
implementation 'com.klarna.mobile:sdk:2.7.1'
|
|
113
118
|
|
|
114
119
|
testImplementation "junit:junit:4.13.2"
|
|
115
120
|
testImplementation 'androidx.test:core:1.5.0'
|
|
116
121
|
testImplementation "org.robolectric:robolectric:4.11.1"
|
|
117
122
|
testImplementation "org.mockito:mockito-core:3.12.4"
|
|
118
123
|
testImplementation "org.mockito:mockito-inline:3.12.4"
|
|
124
|
+
testImplementation "org.jetbrains.kotlin:kotlin-test-junit:${getExtOrDefault('kotlinVersion')}"
|
|
125
|
+
testImplementation "io.mockk:mockk:1.14.5"
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
// removed from the docs but we can keep it
|
|
@@ -10,15 +10,28 @@ import com.klarna.mobile.sdk.api.component.KlarnaComponent;
|
|
|
10
10
|
|
|
11
11
|
public class KlarnaCheckoutViewEventHandler implements KlarnaEventHandler {
|
|
12
12
|
|
|
13
|
+
public interface OnLoadListener {
|
|
14
|
+
void onLoad(KlarnaCheckoutView klarnaCheckoutView);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
private static final String EVENT_LOAD = "load";
|
|
13
18
|
private final KlarnaCheckoutViewEventSender klarnaCheckoutViewEventSender;
|
|
19
|
+
private final OnLoadListener onLoadListener;
|
|
14
20
|
|
|
15
|
-
public KlarnaCheckoutViewEventHandler(KlarnaCheckoutViewEventSender klarnaCheckoutViewEventSender) {
|
|
21
|
+
public KlarnaCheckoutViewEventHandler(@NonNull KlarnaCheckoutViewEventSender klarnaCheckoutViewEventSender, OnLoadListener onLoadListener) {
|
|
16
22
|
this.klarnaCheckoutViewEventSender = klarnaCheckoutViewEventSender;
|
|
23
|
+
this.onLoadListener = onLoadListener;
|
|
17
24
|
}
|
|
18
25
|
|
|
19
26
|
@Override
|
|
20
27
|
public void onEvent(@NonNull KlarnaComponent klarnaComponent, @NonNull KlarnaProductEvent klarnaProductEvent) {
|
|
21
28
|
if (klarnaComponent instanceof KlarnaCheckoutView) {
|
|
29
|
+
String eventName = klarnaProductEvent.getAction();
|
|
30
|
+
if (EVENT_LOAD.equalsIgnoreCase(eventName)) {
|
|
31
|
+
if (onLoadListener != null) {
|
|
32
|
+
onLoadListener.onLoad((KlarnaCheckoutView) klarnaComponent);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
22
35
|
klarnaCheckoutViewEventSender.sendKlarnaProductEvent((KlarnaCheckoutView) klarnaComponent, klarnaProductEvent);
|
|
23
36
|
}
|
|
24
37
|
}
|
|
@@ -13,7 +13,6 @@ import com.klarna.mobile.sdk.reactnative.common.event.KlarnaEventHandlerEventsUt
|
|
|
13
13
|
import com.klarna.mobile.sdk.reactnative.common.ui.ResizeObserverWrapperView;
|
|
14
14
|
import com.klarna.mobile.sdk.reactnative.common.util.ArgumentsUtil;
|
|
15
15
|
|
|
16
|
-
import java.lang.ref.WeakReference;
|
|
17
16
|
import java.util.Arrays;
|
|
18
17
|
import java.util.Collection;
|
|
19
18
|
import java.util.HashMap;
|
|
@@ -24,7 +23,7 @@ public class KlarnaCheckoutViewEventSender extends ComponentEventSender<ResizeOb
|
|
|
24
23
|
private static final String EVENT_NAME_ON_RESIZED = "onResized";
|
|
25
24
|
private static final String EVENT_NAME_ON_CHECKOUT_VIEW_READY = "onCheckoutViewReady";
|
|
26
25
|
|
|
27
|
-
KlarnaCheckoutViewEventSender(@NonNull final Map<
|
|
26
|
+
KlarnaCheckoutViewEventSender(@NonNull final Map<ResizeObserverWrapperView<KlarnaCheckoutView>, EventDispatcher> viewToDispatcher) {
|
|
28
27
|
super(viewToDispatcher);
|
|
29
28
|
}
|
|
30
29
|
|
|
@@ -19,38 +19,56 @@ import com.klarna.mobile.sdk.reactnative.common.WebViewResizeObserver;
|
|
|
19
19
|
import com.klarna.mobile.sdk.reactnative.common.ui.ResizeObserverWrapperView;
|
|
20
20
|
import com.klarna.mobile.sdk.reactnative.spec.RNKlarnaCheckoutViewSpec;
|
|
21
21
|
|
|
22
|
-
import java.lang.ref.WeakReference;
|
|
23
|
-
import java.util.HashMap;
|
|
24
22
|
import java.util.Map;
|
|
25
23
|
import java.util.Objects;
|
|
24
|
+
import java.util.WeakHashMap;
|
|
26
25
|
|
|
27
26
|
public class KlarnaCheckoutViewManager extends RNKlarnaCheckoutViewSpec<ResizeObserverWrapperView<KlarnaCheckoutView>> {
|
|
28
27
|
|
|
28
|
+
private static final int MAX_SET_SNIPPET_RETRIES = 3;
|
|
29
29
|
public static final String KLARNA_CHECKOUT_VIEW_REACT_CLASS = "RNKlarnaCheckoutView";
|
|
30
|
-
|
|
31
30
|
// Commands that can be triggered from RN
|
|
32
31
|
public static final String COMMAND_SET_SNIPPET = "setSnippet";
|
|
33
32
|
public static final String COMMAND_SUSPEND = "suspend";
|
|
34
33
|
public static final String COMMAND_RESUME = "resume";
|
|
35
34
|
|
|
36
35
|
private final ReactApplicationContext reactAppContext;
|
|
37
|
-
|
|
38
36
|
/**
|
|
39
37
|
* Store a map of views to event dispatchers so we send up events via the right views.
|
|
40
38
|
*/
|
|
41
|
-
private final Map<
|
|
39
|
+
private final Map<ResizeObserverWrapperView<KlarnaCheckoutView>, EventDispatcher> viewToDispatcher;
|
|
40
|
+
private final Map<KlarnaCheckoutView, ResizeObserverWrapperView<KlarnaCheckoutView>> checkoutViewToResizeObserverWrapperMap = new WeakHashMap<>();
|
|
41
|
+
private final Map<ResizeObserverWrapperView<KlarnaCheckoutView>, Integer> setSnippetRetriesMap = new WeakHashMap<>();
|
|
42
|
+
private final Map<ResizeObserverWrapperView<KlarnaCheckoutView>, String> snippetMap = new WeakHashMap<>();
|
|
42
43
|
|
|
43
44
|
private final KlarnaCheckoutViewEventSender eventSender;
|
|
44
45
|
private final KlarnaCheckoutViewEventHandler eventHandler;
|
|
45
|
-
private Runnable javascriptInterfaceInjectionRunnable;
|
|
46
46
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
|
47
|
+
private Runnable javascriptInterfaceInjectionRunnable;
|
|
47
48
|
|
|
48
49
|
public KlarnaCheckoutViewManager(ReactApplicationContext reactApplicationContext) {
|
|
49
50
|
super();
|
|
50
|
-
viewToDispatcher = new
|
|
51
|
+
viewToDispatcher = new WeakHashMap<>();
|
|
51
52
|
reactAppContext = reactApplicationContext;
|
|
52
53
|
eventSender = new KlarnaCheckoutViewEventSender(viewToDispatcher);
|
|
53
|
-
eventHandler = new KlarnaCheckoutViewEventHandler(eventSender
|
|
54
|
+
eventHandler = new KlarnaCheckoutViewEventHandler(eventSender, klarnaCheckoutView -> {
|
|
55
|
+
ResizeObserverWrapperView<KlarnaCheckoutView> resizeObserverWrapperView =
|
|
56
|
+
checkoutViewToResizeObserverWrapperMap.get(klarnaCheckoutView);
|
|
57
|
+
if (resizeObserverWrapperView == null) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
Integer retries = setSnippetRetriesMap.getOrDefault(resizeObserverWrapperView, 0);
|
|
61
|
+
if (retries == null || retries >= MAX_SET_SNIPPET_RETRIES) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
String snippet = snippetMap.get(resizeObserverWrapperView);
|
|
65
|
+
handler.postDelayed(() -> {
|
|
66
|
+
if (klarnaCheckoutView != null && klarnaCheckoutView.getHeight() == 0) {
|
|
67
|
+
setSnippet(resizeObserverWrapperView, snippet);
|
|
68
|
+
setSnippetRetriesMap.put(resizeObserverWrapperView, retries + 1);
|
|
69
|
+
}
|
|
70
|
+
}, 500);
|
|
71
|
+
});
|
|
54
72
|
}
|
|
55
73
|
|
|
56
74
|
@NonNull
|
|
@@ -65,9 +83,11 @@ public class KlarnaCheckoutViewManager extends RNKlarnaCheckoutViewSpec<ResizeOb
|
|
|
65
83
|
KlarnaCheckoutView klarnaCheckoutView = new KlarnaCheckoutView(reactAppContext.getCurrentActivity(), null, 0, eventHandler);
|
|
66
84
|
ResizeObserverWrapperView<KlarnaCheckoutView> view = new ResizeObserverWrapperView<>(reactAppContext, null, klarnaCheckoutView);
|
|
67
85
|
|
|
86
|
+
checkoutViewToResizeObserverWrapperMap.put(klarnaCheckoutView, view);
|
|
87
|
+
|
|
68
88
|
// Each view has its own event dispatcher.
|
|
69
89
|
EventDispatcher eventDispatcher = UIManagerHelper.getEventDispatcherForReactTag((ReactContext) view.getContext(), view.getId());
|
|
70
|
-
viewToDispatcher.put(
|
|
90
|
+
viewToDispatcher.put(view, eventDispatcher);
|
|
71
91
|
|
|
72
92
|
view.initiateWebViewResizeObserver(WebViewResizeObserver.TargetElement.CHECKOUT_CONTAINER, (resizeObserverWrapperView, newHeight) -> {
|
|
73
93
|
if (resizeObserverWrapperView == null || eventDispatcher == null) {
|
|
@@ -152,6 +172,8 @@ public class KlarnaCheckoutViewManager extends RNKlarnaCheckoutViewSpec<ResizeOb
|
|
|
152
172
|
return;
|
|
153
173
|
}
|
|
154
174
|
|
|
175
|
+
snippetMap.put(view, snippet);
|
|
176
|
+
|
|
155
177
|
klarnaCheckoutView.setSnippet(snippet);
|
|
156
178
|
view.addJavascriptInterfaceToWebView();
|
|
157
179
|
|
|
@@ -12,14 +12,13 @@ import com.facebook.react.uimanager.UIManagerHelper;
|
|
|
12
12
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
|
13
13
|
import com.klarna.mobile.sdk.reactnative.common.ui.WrapperView;
|
|
14
14
|
|
|
15
|
-
import java.lang.ref.WeakReference;
|
|
16
15
|
import java.util.Collection;
|
|
17
16
|
import java.util.Map;
|
|
18
17
|
|
|
19
18
|
public abstract class ComponentEventSender<T extends View> {
|
|
20
|
-
private final Map<
|
|
19
|
+
private final Map<T, EventDispatcher> viewToDispatcher;
|
|
21
20
|
|
|
22
|
-
protected ComponentEventSender(@NonNull final Map<
|
|
21
|
+
protected ComponentEventSender(@NonNull final Map<T, EventDispatcher> viewToDispatcher) {
|
|
23
22
|
this.viewToDispatcher = viewToDispatcher;
|
|
24
23
|
}
|
|
25
24
|
|
|
@@ -50,15 +49,14 @@ public abstract class ComponentEventSender<T extends View> {
|
|
|
50
49
|
if (view == null) {
|
|
51
50
|
return null;
|
|
52
51
|
}
|
|
53
|
-
for (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
return viewReference;
|
|
52
|
+
for (T reference : viewToDispatcher.keySet()) {
|
|
53
|
+
if (reference != null && reference == view) {
|
|
54
|
+
return reference;
|
|
57
55
|
}
|
|
58
|
-
if (
|
|
59
|
-
View wrappedView = ((WrapperView)
|
|
56
|
+
if (reference instanceof WrapperView) {
|
|
57
|
+
View wrappedView = ((WrapperView) reference).getView();
|
|
60
58
|
if (wrappedView == view) {
|
|
61
|
-
return
|
|
59
|
+
return reference;
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
62
|
}
|
|
@@ -5,19 +5,16 @@ import android.view.View;
|
|
|
5
5
|
import androidx.annotation.NonNull;
|
|
6
6
|
import androidx.annotation.Nullable;
|
|
7
7
|
|
|
8
|
-
import com.facebook.react.bridge.Promise;
|
|
9
8
|
import com.facebook.react.bridge.ReadableMap;
|
|
10
9
|
import com.facebook.react.bridge.WritableMap;
|
|
11
|
-
import com.google.gson.reflect.TypeToken;
|
|
12
10
|
import com.klarna.mobile.sdk.KlarnaMobileSDKError;
|
|
13
11
|
import com.klarna.mobile.sdk.api.KlarnaProductEvent;
|
|
14
|
-
import com.klarna.mobile.sdk.
|
|
12
|
+
import com.klarna.mobile.sdk.reactnative.common.serializer.DynamicMapSerializer;
|
|
15
13
|
import com.klarna.mobile.sdk.reactnative.common.util.ArgumentsUtil;
|
|
16
14
|
import com.klarna.mobile.sdk.reactnative.common.util.ParserUtil;
|
|
17
15
|
import com.klarna.mobile.sdk.reactnative.signin.KlarnaSignInData;
|
|
18
16
|
import com.klarna.mobile.sdk.reactnative.signin.KlarnaSignInEventsMapper;
|
|
19
17
|
|
|
20
|
-
import java.lang.reflect.Type;
|
|
21
18
|
import java.util.HashMap;
|
|
22
19
|
import java.util.Map;
|
|
23
20
|
|
|
@@ -35,7 +32,7 @@ public class KlarnaEventHandlerEventsUtil {
|
|
|
35
32
|
private static final String PARAM_NAME_PRODUCT_EVENT = "productEvent";
|
|
36
33
|
|
|
37
34
|
public static <T extends View> void sendKlarnaProductEvent(@NonNull ComponentEventSender<T> componentEventSender, @Nullable View view, @NonNull KlarnaProductEvent klarnaProductEvent) {
|
|
38
|
-
String stringifiedParams = ParserUtil.toJson(klarnaProductEvent.getParams());
|
|
35
|
+
String stringifiedParams = ParserUtil.INSTANCE.toJson(DynamicMapSerializer.INSTANCE, klarnaProductEvent.getParams());
|
|
39
36
|
String paramsJson = stringifiedParams == null ? "{}" : stringifiedParams;
|
|
40
37
|
ReadableMap eventMap = ArgumentsUtil.createMap(new HashMap<String, Object>() {{
|
|
41
38
|
put(PARAM_NAME_ACTION, klarnaProductEvent.getAction());
|
|
@@ -64,9 +61,10 @@ public class KlarnaEventHandlerEventsUtil {
|
|
|
64
61
|
return;
|
|
65
62
|
}
|
|
66
63
|
Map<String, Object> updatedParams = new HashMap<>();
|
|
67
|
-
for(Map.Entry<String, Object> entry : klarnaProductEvent.getParams().entrySet()) {
|
|
64
|
+
for (Map.Entry<String, Object> entry : klarnaProductEvent.getParams().entrySet()) {
|
|
68
65
|
String updatedKey = KlarnaSignInEventsMapper.mapSignInParamName(entry.getKey());
|
|
69
|
-
|
|
66
|
+
Object updatedValue = KlarnaSignInEventsMapper.transformSignInParamValue(entry.getKey(), entry.getValue());
|
|
67
|
+
updatedParams.put(updatedKey, updatedValue);
|
|
70
68
|
}
|
|
71
69
|
String eventName = KlarnaSignInEventsMapper.mapSignInEventName(klarnaProductEvent.getAction());
|
|
72
70
|
ReadableMap eventMap = ArgumentsUtil.createMap(new HashMap<String, Object>() {{
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
package com.klarna.mobile.sdk.reactnative.common.serializer
|
|
2
|
+
|
|
3
|
+
import kotlinx.serialization.KSerializer
|
|
4
|
+
import kotlinx.serialization.descriptors.SerialDescriptor
|
|
5
|
+
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
|
|
6
|
+
import kotlinx.serialization.encoding.Decoder
|
|
7
|
+
import kotlinx.serialization.encoding.Encoder
|
|
8
|
+
import kotlinx.serialization.json.JsonArray
|
|
9
|
+
import kotlinx.serialization.json.JsonDecoder
|
|
10
|
+
import kotlinx.serialization.json.JsonElement
|
|
11
|
+
import kotlinx.serialization.json.JsonEncoder
|
|
12
|
+
import kotlinx.serialization.json.JsonNull
|
|
13
|
+
import kotlinx.serialization.json.JsonObject
|
|
14
|
+
import kotlinx.serialization.json.JsonPrimitive
|
|
15
|
+
import kotlinx.serialization.json.buildJsonArray
|
|
16
|
+
import kotlinx.serialization.json.buildJsonObject
|
|
17
|
+
import kotlinx.serialization.json.doubleOrNull
|
|
18
|
+
import kotlinx.serialization.json.floatOrNull
|
|
19
|
+
import kotlinx.serialization.json.intOrNull
|
|
20
|
+
import kotlinx.serialization.json.jsonObject
|
|
21
|
+
import kotlinx.serialization.json.longOrNull
|
|
22
|
+
import kotlinx.serialization.modules.SerializersModule
|
|
23
|
+
import kotlinx.serialization.serializerOrNull
|
|
24
|
+
import kotlin.reflect.full.memberProperties
|
|
25
|
+
|
|
26
|
+
object DynamicMapSerializer : KSerializer<Map<String, Any?>> {
|
|
27
|
+
override val descriptor: SerialDescriptor =
|
|
28
|
+
buildClassSerialDescriptor("EnhancedDynamicMap")
|
|
29
|
+
|
|
30
|
+
override fun serialize(
|
|
31
|
+
encoder: Encoder,
|
|
32
|
+
value: Map<String, Any?>,
|
|
33
|
+
) {
|
|
34
|
+
val jsonEncoder = encoder as? JsonEncoder ?: error("Only JSON is supported")
|
|
35
|
+
val jsonObject =
|
|
36
|
+
buildJsonObject {
|
|
37
|
+
value.forEach { (key, any) ->
|
|
38
|
+
put(key, serializeValue(any, jsonEncoder))
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
jsonEncoder.encodeJsonElement(jsonObject)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
override fun deserialize(decoder: Decoder): Map<String, Any?> {
|
|
45
|
+
val jsonDecoder = decoder as? JsonDecoder ?: error("Only JSON is supported")
|
|
46
|
+
val jsonObject = jsonDecoder.decodeJsonElement().jsonObject
|
|
47
|
+
return jsonObject.mapValues { (_, element) -> deserializeValue(element) }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun serializeValue(value: Any?, encoder: JsonEncoder): JsonElement =
|
|
51
|
+
when (value) {
|
|
52
|
+
null -> JsonNull
|
|
53
|
+
is Boolean -> JsonPrimitive(value)
|
|
54
|
+
is Byte -> JsonPrimitive(value)
|
|
55
|
+
is Short -> JsonPrimitive(value)
|
|
56
|
+
is Int -> JsonPrimitive(value)
|
|
57
|
+
is Long -> JsonPrimitive(value)
|
|
58
|
+
is Float -> JsonPrimitive(value)
|
|
59
|
+
is Double -> JsonPrimitive(value)
|
|
60
|
+
is String -> JsonPrimitive(value)
|
|
61
|
+
is Char -> JsonPrimitive(value.toString())
|
|
62
|
+
is Enum<*> -> JsonPrimitive(value.toString())
|
|
63
|
+
is Map<*, *> -> {
|
|
64
|
+
buildJsonObject {
|
|
65
|
+
value.forEach { (k, v) ->
|
|
66
|
+
put(k.toString(), serializeValue(v, encoder))
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
is Collection<*> -> {
|
|
72
|
+
buildJsonArray {
|
|
73
|
+
value.forEach { add(serializeValue(it, encoder)) }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
is Array<*> -> {
|
|
78
|
+
buildJsonArray {
|
|
79
|
+
value.forEach { add(serializeValue(it, encoder)) }
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
else -> {
|
|
84
|
+
serializerFor(encoder.serializersModule, value)?.let { serializer ->
|
|
85
|
+
// Use the serializer to convert the object to JsonElement
|
|
86
|
+
val jsonElement = encoder.json.encodeToJsonElement(serializer, value)
|
|
87
|
+
jsonElement
|
|
88
|
+
} ?: run {
|
|
89
|
+
// Check if it's a data class
|
|
90
|
+
if (value::class.isData) {
|
|
91
|
+
// Convert data class to Map by using reflection
|
|
92
|
+
val properties = value::class.memberProperties
|
|
93
|
+
.associate { it.name to it.call(value) }
|
|
94
|
+
|
|
95
|
+
buildJsonObject {
|
|
96
|
+
properties.forEach { (k, v) ->
|
|
97
|
+
put(k, serializeValue(v, encoder))
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
JsonPrimitive(value.toString()) // Fallback to string representation
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private fun deserializeValue(element: JsonElement): Any? =
|
|
108
|
+
when (element) {
|
|
109
|
+
is JsonNull -> null
|
|
110
|
+
is JsonPrimitive -> {
|
|
111
|
+
when {
|
|
112
|
+
element.isString -> element.content
|
|
113
|
+
element.content.equals("true", ignoreCase = true) -> true
|
|
114
|
+
element.content.equals("false", ignoreCase = true) -> false
|
|
115
|
+
// Try parsing as numbers with proper type detection
|
|
116
|
+
element.content.contains('.') -> {
|
|
117
|
+
element.doubleOrNull ?: element.floatOrNull ?: element.content
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
else -> {
|
|
121
|
+
element.longOrNull ?: element.intOrNull ?: element.content
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
is JsonObject -> element.mapValues { (_, value) -> deserializeValue(value) }
|
|
127
|
+
is JsonArray -> element.map { deserializeValue(it) }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private fun serializerFor(
|
|
131
|
+
serializersModule: SerializersModule,
|
|
132
|
+
obj: Any?,
|
|
133
|
+
): KSerializer<Any>? {
|
|
134
|
+
return obj?.let {
|
|
135
|
+
serializersModule.serializerOrNull(obj::class.java)
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
package com.klarna.mobile.sdk.reactnative.common.ui;
|
|
2
2
|
|
|
3
3
|
import android.content.Context;
|
|
4
|
+
import android.os.Handler;
|
|
5
|
+
import android.os.Looper;
|
|
4
6
|
import android.util.AttributeSet;
|
|
7
|
+
import android.util.TypedValue;
|
|
5
8
|
import android.view.View;
|
|
6
9
|
import android.view.ViewGroup;
|
|
7
10
|
import android.webkit.WebView;
|
|
@@ -23,6 +26,7 @@ public class ResizeObserverWrapperView<T extends ViewGroup> extends WrapperView<
|
|
|
23
26
|
private WebViewResizeObserver webViewResizeObserver;
|
|
24
27
|
private WebViewResizeObserver.WebViewResizeObserverCallback webViewResizeObserverCallback;
|
|
25
28
|
private OnResizedListener<T> onResizedListener;
|
|
29
|
+
private final Handler handler = new Handler(Looper.getMainLooper());
|
|
26
30
|
|
|
27
31
|
public ResizeObserverWrapperView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
|
28
32
|
super(context, attrs, defStyleAttr);
|
|
@@ -43,12 +47,36 @@ public class ResizeObserverWrapperView<T extends ViewGroup> extends WrapperView<
|
|
|
43
47
|
OnResizedListener<T> listener = onResizedListenerWeakReference.get();
|
|
44
48
|
if (listener != null) {
|
|
45
49
|
listener.onResized(ResizeObserverWrapperView.this, value);
|
|
50
|
+
int height = convertDpToPx(value);
|
|
51
|
+
for (int i = 0; i < getChildCount(); i++) {
|
|
52
|
+
View view = getChildAt(i);
|
|
53
|
+
setViewHeight(view, height);
|
|
54
|
+
}
|
|
46
55
|
}
|
|
47
56
|
}
|
|
48
57
|
};
|
|
49
58
|
this.webViewResizeObserver = new WebViewResizeObserver(webViewResizeObserverCallback, targetElement);
|
|
50
59
|
}
|
|
51
60
|
|
|
61
|
+
private void setViewHeight(View view, int heightInPx) {
|
|
62
|
+
if (view == null || heightInPx < 0) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
handler.post(() -> {
|
|
66
|
+
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
|
67
|
+
if (layoutParams != null) {
|
|
68
|
+
layoutParams.height = heightInPx;
|
|
69
|
+
} else {
|
|
70
|
+
layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, heightInPx);
|
|
71
|
+
}
|
|
72
|
+
view.setLayoutParams(layoutParams);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private int convertDpToPx(int value) {
|
|
77
|
+
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
|
|
78
|
+
}
|
|
79
|
+
|
|
52
80
|
public void addJavascriptInterfaceToWebView() {
|
|
53
81
|
WebView webView = findWebView(getView());
|
|
54
82
|
if (webView != null && webViewResizeObserver != null) {
|
package/android/src/main/java/com/klarna/mobile/sdk/reactnative/common/util/ArgumentsUtil.java
CHANGED
|
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReadableArray;
|
|
|
5
5
|
import com.facebook.react.bridge.ReadableMap;
|
|
6
6
|
import com.facebook.react.bridge.WritableArray;
|
|
7
7
|
import com.facebook.react.bridge.WritableMap;
|
|
8
|
+
import com.klarna.mobile.sdk.reactnative.common.serializer.DynamicMapSerializer;
|
|
8
9
|
|
|
9
10
|
import org.json.JSONException;
|
|
10
11
|
import org.json.JSONObject;
|
|
@@ -13,8 +14,6 @@ import java.util.Iterator;
|
|
|
13
14
|
import java.util.List;
|
|
14
15
|
import java.util.Map;
|
|
15
16
|
|
|
16
|
-
import okhttp3.internal.Util;
|
|
17
|
-
|
|
18
17
|
public class ArgumentsUtil {
|
|
19
18
|
|
|
20
19
|
public static WritableMap createMap(Map<String, Object> sourceMap) {
|
|
@@ -67,7 +66,7 @@ public class ArgumentsUtil {
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
public static WritableMap createMapUsingJSONString(Map<String, Object> sourceMap) {
|
|
70
|
-
String jsonString = ParserUtil.toJson(sourceMap);
|
|
69
|
+
String jsonString = ParserUtil.INSTANCE.toJson(DynamicMapSerializer.INSTANCE, sourceMap);
|
|
71
70
|
WritableMap map = Arguments.createMap();
|
|
72
71
|
try {
|
|
73
72
|
JSONObject jsonObject = new JSONObject(jsonString);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
package com.klarna.mobile.sdk.reactnative.common.util
|
|
2
|
+
|
|
3
|
+
import kotlinx.serialization.SerializationStrategy
|
|
4
|
+
import kotlinx.serialization.json.Json
|
|
5
|
+
import kotlinx.serialization.json.JsonElement
|
|
6
|
+
|
|
7
|
+
object ParserUtil {
|
|
8
|
+
val json: Json by lazy {
|
|
9
|
+
Json {
|
|
10
|
+
ignoreUnknownKeys = true
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
fun <T> toJson(
|
|
15
|
+
serializer: SerializationStrategy<T>,
|
|
16
|
+
src: T,
|
|
17
|
+
): String {
|
|
18
|
+
val res = json.encodeToString(serializer, src)
|
|
19
|
+
return res
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
inline fun <reified T> toJson(
|
|
23
|
+
src: T,
|
|
24
|
+
): String = json.encodeToString(src)
|
|
25
|
+
|
|
26
|
+
fun parse(json: String): JsonElement = this.json.parseToJsonElement(json)
|
|
27
|
+
}
|
package/android/src/main/java/com/klarna/mobile/sdk/reactnative/signin/KlarnaSignInEventsMapper.java
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
package com.klarna.mobile.sdk.reactnative.signin;
|
|
2
2
|
|
|
3
|
+
import com.klarna.mobile.sdk.api.signin.model.KlarnaSignInToken;
|
|
4
|
+
import com.klarna.mobile.sdk.reactnative.signin.model.KlarnaSignInTokenSerializable;
|
|
5
|
+
|
|
3
6
|
import java.util.Collections;
|
|
4
7
|
import java.util.HashMap;
|
|
5
8
|
import java.util.Map;
|
|
@@ -22,19 +25,45 @@ public class KlarnaSignInEventsMapper {
|
|
|
22
25
|
return errorsMap.get(key);
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
private static final Map<String, String> EVENTS_MAP = new HashMap<>() {
|
|
29
|
+
{
|
|
30
|
+
put("KlarnaSignInToken", "KlarnaSignInToken");
|
|
31
|
+
put("KlarnaSignInUserCancelled", "KlarnaSignInUserCancelled");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
25
35
|
public static String mapSignInEventName(String key) {
|
|
26
|
-
|
|
27
|
-
eventsMap.put("KlarnaSignInToken", "KlarnaSignInToken");
|
|
28
|
-
eventsMap.put("KlarnaSignInUserCancelled", "KlarnaSignInUserCancelled");
|
|
29
|
-
if (eventsMap.get(key) == null) {
|
|
36
|
+
if (EVENTS_MAP.get(key) == null) {
|
|
30
37
|
return key;
|
|
31
38
|
}
|
|
32
|
-
return
|
|
39
|
+
return EVENTS_MAP.get(key);
|
|
33
40
|
}
|
|
34
41
|
|
|
42
|
+
private static final Map<String, String> PARAMS_MAP = Collections.singletonMap("KlarnaSignInToken", "KlarnaSignInToken");
|
|
43
|
+
|
|
35
44
|
public static String mapSignInParamName(String key) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
if (EVENTS_MAP.containsKey(key)) {
|
|
46
|
+
return PARAMS_MAP.get(key);
|
|
47
|
+
}
|
|
48
|
+
return key;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private static final Map<String, ValueTransformer<Object>> TRANSFORMER_MAP = Collections.singletonMap("KlarnaSignInToken", value -> {
|
|
52
|
+
if (value instanceof KlarnaSignInToken) {
|
|
53
|
+
return KlarnaSignInTokenSerializable.Companion.from((KlarnaSignInToken) value);
|
|
54
|
+
}
|
|
55
|
+
return value;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
public static Object transformSignInParamValue(String key, Object value) {
|
|
59
|
+
ValueTransformer<Object> transformer = TRANSFORMER_MAP.get(key);
|
|
60
|
+
if (transformer != null) {
|
|
61
|
+
return transformer.transform(value);
|
|
62
|
+
}
|
|
63
|
+
return value;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
public interface ValueTransformer<T> {
|
|
67
|
+
T transform(Object value);
|
|
39
68
|
}
|
|
40
69
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
package com.klarna.mobile.sdk.reactnative.signin.model
|
|
2
|
+
|
|
3
|
+
import com.klarna.mobile.sdk.api.signin.model.KlarnaSignInToken
|
|
4
|
+
import kotlinx.serialization.SerialName
|
|
5
|
+
import kotlinx.serialization.Serializable
|
|
6
|
+
|
|
7
|
+
@Serializable
|
|
8
|
+
data class KlarnaSignInTokenSerializable(
|
|
9
|
+
@SerialName("access_token")
|
|
10
|
+
val accessToken: String? = null,
|
|
11
|
+
@SerialName("refresh_token")
|
|
12
|
+
val refreshToken: String? = null,
|
|
13
|
+
@SerialName("id_token")
|
|
14
|
+
val idToken: String? = null,
|
|
15
|
+
@SerialName("expires_in")
|
|
16
|
+
val expiresIn: String? = null,
|
|
17
|
+
@SerialName("scope")
|
|
18
|
+
val scope: String? = null,
|
|
19
|
+
@SerialName("token_type")
|
|
20
|
+
val tokenType: String? = null,
|
|
21
|
+
) {
|
|
22
|
+
companion object {
|
|
23
|
+
fun from(klarnaSignInToken: KlarnaSignInToken) = KlarnaSignInTokenSerializable(
|
|
24
|
+
accessToken = klarnaSignInToken.accessToken,
|
|
25
|
+
refreshToken = klarnaSignInToken.refreshToken,
|
|
26
|
+
idToken = klarnaSignInToken.idToken,
|
|
27
|
+
expiresIn = klarnaSignInToken.expiresIn,
|
|
28
|
+
scope = klarnaSignInToken.scope,
|
|
29
|
+
tokenType = klarnaSignInToken.tokenType
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -8,10 +8,11 @@ import com.facebook.react.bridge.ReadableMap;
|
|
|
8
8
|
import com.facebook.react.bridge.WritableMap;
|
|
9
9
|
import com.facebook.react.uimanager.UIManagerHelper;
|
|
10
10
|
import com.facebook.react.uimanager.events.EventDispatcher;
|
|
11
|
-
import com.google.gson.Gson;
|
|
12
11
|
import com.klarna.mobile.sdk.api.KlarnaProductEvent;
|
|
13
12
|
import com.klarna.mobile.sdk.api.standalonewebview.KlarnaStandaloneWebView;
|
|
13
|
+
import com.klarna.mobile.sdk.reactnative.common.serializer.DynamicMapSerializer;
|
|
14
14
|
import com.klarna.mobile.sdk.reactnative.common.util.ArgumentsUtil;
|
|
15
|
+
import com.klarna.mobile.sdk.reactnative.common.util.ParserUtil;
|
|
15
16
|
|
|
16
17
|
import java.lang.ref.WeakReference;
|
|
17
18
|
import java.util.HashMap;
|
|
@@ -39,7 +40,6 @@ public class KlarnaStandaloneWebViewEventSender {
|
|
|
39
40
|
private static final String PARAM_NAME_PARAMS = "params";
|
|
40
41
|
private static final String PARAM_NAME_DID_CRASH = "didCrash";
|
|
41
42
|
|
|
42
|
-
private final Gson gson = new Gson();
|
|
43
43
|
private final WeakReference<ReactContext> reactContextWeakReference;
|
|
44
44
|
private final Map<WeakReference<KlarnaStandaloneWebView>, EventDispatcher> viewToDispatcher;
|
|
45
45
|
|
|
@@ -78,7 +78,7 @@ public class KlarnaStandaloneWebViewEventSender {
|
|
|
78
78
|
public void sendKlarnaMessageEvent(@Nullable KlarnaStandaloneWebView view, @NonNull KlarnaProductEvent klarnaProductEvent) {
|
|
79
79
|
String paramsJson = "{}";
|
|
80
80
|
try {
|
|
81
|
-
paramsJson =
|
|
81
|
+
paramsJson = ParserUtil.INSTANCE.toJson(DynamicMapSerializer.INSTANCE, klarnaProductEvent.getParams());
|
|
82
82
|
} catch (Exception ignored) {
|
|
83
83
|
}
|
|
84
84
|
String finalParamsJson = paramsJson;
|
package/android/src/test/java/com/klarna/mobile/sdk/reactnative/KlarnaMobileSDKPackageTest.java
CHANGED
|
@@ -7,7 +7,6 @@ import androidx.test.core.app.ApplicationProvider;
|
|
|
7
7
|
import com.facebook.react.bridge.NativeModule;
|
|
8
8
|
import com.facebook.react.bridge.ReactApplicationContext;
|
|
9
9
|
import com.facebook.react.uimanager.ViewManager;
|
|
10
|
-
import com.klarna.mobile.sdk.reactnative.KlarnaMobileSDKPackage;
|
|
11
10
|
import com.klarna.mobile.sdk.reactnative.checkout.KlarnaCheckoutViewManager;
|
|
12
11
|
import com.klarna.mobile.sdk.reactnative.payments.KlarnaPaymentViewManager;
|
|
13
12
|
import com.klarna.mobile.sdk.reactnative.spec.RNKlarnaSignInModuleSpec;
|
|
@@ -17,6 +16,7 @@ import org.junit.Assert;
|
|
|
17
16
|
import org.junit.Before;
|
|
18
17
|
import org.junit.Test;
|
|
19
18
|
import org.junit.runner.RunWith;
|
|
19
|
+
import org.mockito.Mockito;
|
|
20
20
|
import org.robolectric.RobolectricTestRunner;
|
|
21
21
|
|
|
22
22
|
import java.util.List;
|
|
@@ -30,7 +30,8 @@ public class KlarnaMobileSDKPackageTest {
|
|
|
30
30
|
@Before
|
|
31
31
|
public void setup() {
|
|
32
32
|
application = ApplicationProvider.getApplicationContext();
|
|
33
|
-
reactContext =
|
|
33
|
+
reactContext = Mockito.mock(ReactApplicationContext.class);
|
|
34
|
+
Mockito.when(reactContext.getApplicationContext()).thenAnswer(invocation -> application);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
@Test
|
|
@@ -16,7 +16,6 @@ import org.mockito.MockedStatic;
|
|
|
16
16
|
import org.mockito.Mockito;
|
|
17
17
|
import org.mockito.junit.MockitoJUnitRunner;
|
|
18
18
|
|
|
19
|
-
import java.lang.ref.WeakReference;
|
|
20
19
|
import java.util.Arrays;
|
|
21
20
|
import java.util.Collection;
|
|
22
21
|
import java.util.HashMap;
|
|
@@ -30,14 +29,14 @@ public class ComponentEventSenderTest {
|
|
|
30
29
|
private TestEventSender testEventSender;
|
|
31
30
|
private View view;
|
|
32
31
|
private EventDispatcher eventDispatcher;
|
|
33
|
-
private Map<
|
|
32
|
+
private Map<View, EventDispatcher> viewToDispatcher;
|
|
34
33
|
|
|
35
34
|
@Before
|
|
36
35
|
public void setUp() {
|
|
37
36
|
view = Mockito.mock(View.class);
|
|
38
37
|
eventDispatcher = Mockito.mock(EventDispatcher.class);
|
|
39
38
|
viewToDispatcher = new HashMap<>() {{
|
|
40
|
-
put(
|
|
39
|
+
put(view, eventDispatcher);
|
|
41
40
|
}};
|
|
42
41
|
testEventSender = new TestEventSender(viewToDispatcher);
|
|
43
42
|
|
|
@@ -76,7 +75,7 @@ public class ComponentEventSenderTest {
|
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
private class TestEventSender extends ComponentEventSender<View> {
|
|
79
|
-
public TestEventSender(Map<
|
|
78
|
+
public TestEventSender(Map<View, EventDispatcher> viewToDispatcher) {
|
|
80
79
|
super(viewToDispatcher);
|
|
81
80
|
}
|
|
82
81
|
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
package com.klarna.mobile.sdk.reactnative.common.serializer
|
|
2
|
+
|
|
3
|
+
import com.klarna.mobile.sdk.reactnative.common.util.ParserUtil
|
|
4
|
+
import kotlinx.serialization.Serializable
|
|
5
|
+
import kotlinx.serialization.json.Json
|
|
6
|
+
import org.junit.Assert.assertEquals
|
|
7
|
+
import org.junit.Test
|
|
8
|
+
import kotlin.test.assertContentEquals
|
|
9
|
+
|
|
10
|
+
class DynamicMapSerializerTest {
|
|
11
|
+
|
|
12
|
+
private val json = Json { ignoreUnknownKeys = true }
|
|
13
|
+
|
|
14
|
+
@Test
|
|
15
|
+
fun `test serialization of primitive values`() {
|
|
16
|
+
val map = mapOf(
|
|
17
|
+
"nullValue" to null,
|
|
18
|
+
"booleanValue" to true,
|
|
19
|
+
"intValue" to 42,
|
|
20
|
+
"longValue" to 123456789L,
|
|
21
|
+
"floatValue" to 3.14f,
|
|
22
|
+
"doubleValue" to 2.71828,
|
|
23
|
+
"stringValue" to "hello",
|
|
24
|
+
"charValue" to 'A'
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, map)
|
|
28
|
+
val expectedJson =
|
|
29
|
+
"""{"nullValue":null,"booleanValue":true,"intValue":42,"longValue":123456789,"floatValue":3.14,"doubleValue":2.71828,"stringValue":"hello","charValue":"A"}"""
|
|
30
|
+
|
|
31
|
+
assertEquals(expectedJson, jsonString)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@Test
|
|
35
|
+
fun `test deserialization of primitive values`() {
|
|
36
|
+
val jsonString =
|
|
37
|
+
"""{"nullValue":null,"booleanValue":true,"intValue":42,"longValue":123456789,"floatValue":3.14,"doubleValue":2.71828,"stringValue":"hello"}"""
|
|
38
|
+
|
|
39
|
+
val map = json.decodeFromString(DynamicMapSerializer, jsonString)
|
|
40
|
+
|
|
41
|
+
assertEquals(null, map["nullValue"])
|
|
42
|
+
assertEquals(true, map["booleanValue"])
|
|
43
|
+
assertEquals(42L, map["intValue"])
|
|
44
|
+
assertEquals(123456789L, map["longValue"])
|
|
45
|
+
assertEquals(3.14, map["floatValue"])
|
|
46
|
+
assertEquals(2.71828, map["doubleValue"])
|
|
47
|
+
assertEquals("hello", map["stringValue"])
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Test
|
|
51
|
+
fun `test serialization of nested maps`() {
|
|
52
|
+
val nestedMap = mapOf(
|
|
53
|
+
"inner" to mapOf(
|
|
54
|
+
"key1" to "value1",
|
|
55
|
+
"key2" to 42
|
|
56
|
+
)
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, nestedMap)
|
|
60
|
+
val expectedJson = """{"inner":{"key1":"value1","key2":42}}"""
|
|
61
|
+
|
|
62
|
+
assertEquals(expectedJson, jsonString)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
@Suppress("UNCHECKED_CAST")
|
|
66
|
+
@Test
|
|
67
|
+
fun `test deserialization of nested maps`() {
|
|
68
|
+
val jsonString = """{"outer":{"inner":{"key1":"value1","key2":42}}}"""
|
|
69
|
+
|
|
70
|
+
val map = json.decodeFromString(DynamicMapSerializer, jsonString)
|
|
71
|
+
|
|
72
|
+
val outer = map["outer"] as Map<String, Any?>
|
|
73
|
+
val inner = outer["inner"] as Map<String, Any?>
|
|
74
|
+
|
|
75
|
+
assertEquals("value1", inner["key1"])
|
|
76
|
+
assertEquals(42L, inner["key2"])
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
@Test
|
|
80
|
+
fun `test serialization of collections`() {
|
|
81
|
+
val map = mapOf(
|
|
82
|
+
"list" to listOf(1, 2, 3),
|
|
83
|
+
"mixedList" to listOf(1, "two", 3.0)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, map)
|
|
87
|
+
val expectedJson = """{"list":[1,2,3],"mixedList":[1,"two",3.0]}"""
|
|
88
|
+
|
|
89
|
+
assertEquals(expectedJson, jsonString)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@Suppress("UNCHECKED_CAST")
|
|
93
|
+
@Test
|
|
94
|
+
fun `test deserialization of collections`() {
|
|
95
|
+
val jsonString = """{"list":[1,2,3],"mixedList":[1,"two",3.0]}"""
|
|
96
|
+
|
|
97
|
+
val map = json.decodeFromString(DynamicMapSerializer, jsonString)
|
|
98
|
+
|
|
99
|
+
val list = map["list"] as List<Long>
|
|
100
|
+
val mixedList = map["mixedList"] as Collection<Any?>
|
|
101
|
+
|
|
102
|
+
assertContentEquals(listOf(1L, 2L, 3L), list)
|
|
103
|
+
assertEquals(1L, mixedList.elementAt(0))
|
|
104
|
+
assertEquals("two", mixedList.elementAt(1))
|
|
105
|
+
assertEquals(3.0, mixedList.elementAt(2))
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Test
|
|
109
|
+
fun `test serialization of arrays`() {
|
|
110
|
+
val map = mapOf(
|
|
111
|
+
"array" to arrayOf(1, 2, 3)
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, map)
|
|
115
|
+
val expectedJson = """{"array":[1,2,3]}"""
|
|
116
|
+
|
|
117
|
+
assertEquals(expectedJson, jsonString)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@Test
|
|
121
|
+
fun `test serialization of complex nested structures`() {
|
|
122
|
+
val complex = mapOf(
|
|
123
|
+
"name" to "Complex",
|
|
124
|
+
"properties" to mapOf(
|
|
125
|
+
"nested" to listOf(
|
|
126
|
+
mapOf("id" to 1, "value" to "first"),
|
|
127
|
+
mapOf("id" to 2, "value" to "second")
|
|
128
|
+
)
|
|
129
|
+
),
|
|
130
|
+
"tags" to arrayOf("important", "test")
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, complex)
|
|
134
|
+
val expectedJson =
|
|
135
|
+
"""{"name":"Complex","properties":{"nested":[{"id":1,"value":"first"},{"id":2,"value":"second"}]},"tags":["important","test"]}"""
|
|
136
|
+
|
|
137
|
+
assertEquals(expectedJson, jsonString)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@Suppress("UNCHECKED_CAST")
|
|
141
|
+
@Test
|
|
142
|
+
fun `test serialization and deserialization of data classes`() {
|
|
143
|
+
// Define a data class for testing
|
|
144
|
+
data class TestDataClass2(
|
|
145
|
+
val street: String,
|
|
146
|
+
val city: String,
|
|
147
|
+
val zipCode: String
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
data class TestDataClass1(
|
|
151
|
+
val name: String,
|
|
152
|
+
val age: Int,
|
|
153
|
+
val address: TestDataClass2
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
// Create test data
|
|
157
|
+
val address = TestDataClass2("123 Main St", "Anytown", "12345")
|
|
158
|
+
val person = TestDataClass1("John Doe", 30, address)
|
|
159
|
+
|
|
160
|
+
// Create a map with the data class
|
|
161
|
+
val map = mapOf(
|
|
162
|
+
"person" to person,
|
|
163
|
+
"simpleValue" to 42
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
// Serialize
|
|
167
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, map)
|
|
168
|
+
|
|
169
|
+
// Expected JSON structure
|
|
170
|
+
val expectedJson =
|
|
171
|
+
"""{"person":{"name":"John Doe","age":30,"address":{"street":"123 Main St","city":"Anytown","zipCode":"12345"}},"simpleValue":42}"""
|
|
172
|
+
|
|
173
|
+
// Verify serialization
|
|
174
|
+
assertEquals(ParserUtil.parse(expectedJson), ParserUtil.parse(jsonString))
|
|
175
|
+
|
|
176
|
+
// Deserialize
|
|
177
|
+
val deserializedMap = json.decodeFromString(DynamicMapSerializer, jsonString)
|
|
178
|
+
|
|
179
|
+
// Verify deserialization
|
|
180
|
+
assertEquals(42L, deserializedMap["simpleValue"])
|
|
181
|
+
|
|
182
|
+
// Check the nested structure
|
|
183
|
+
val deserializedPerson = deserializedMap["person"] as Map<String, Any?>
|
|
184
|
+
assertEquals("John Doe", deserializedPerson["name"])
|
|
185
|
+
assertEquals(30L, deserializedPerson["age"])
|
|
186
|
+
|
|
187
|
+
val deserializedAddress = deserializedPerson["address"] as Map<String, Any?>
|
|
188
|
+
assertEquals("123 Main St", deserializedAddress["street"])
|
|
189
|
+
assertEquals("Anytown", deserializedAddress["city"])
|
|
190
|
+
assertEquals("12345", deserializedAddress["zipCode"])
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@Test
|
|
194
|
+
fun `test serialization and deserialization of serializable objects`() {
|
|
195
|
+
// Define a serializable class
|
|
196
|
+
@Serializable
|
|
197
|
+
data class SerializableUser(
|
|
198
|
+
val id: Int,
|
|
199
|
+
val username: String
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// Create test data with serializable object
|
|
203
|
+
val user = SerializableUser(id = 1001, username = "testuser")
|
|
204
|
+
val map = mapOf(
|
|
205
|
+
"user" to user,
|
|
206
|
+
"timestamp" to 1634567890L
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
// Serialize
|
|
210
|
+
val jsonString = json.encodeToString(DynamicMapSerializer, map)
|
|
211
|
+
|
|
212
|
+
// Expected JSON structure
|
|
213
|
+
val expectedJson = """{"user":{"id":1001,"username":"testuser"},"timestamp":1634567890}"""
|
|
214
|
+
|
|
215
|
+
// Verify serialization
|
|
216
|
+
assertEquals(ParserUtil.parse(expectedJson), ParserUtil.parse(jsonString))
|
|
217
|
+
|
|
218
|
+
// Deserialize
|
|
219
|
+
val deserializedMap = json.decodeFromString(DynamicMapSerializer, jsonString)
|
|
220
|
+
|
|
221
|
+
// Verify deserialization
|
|
222
|
+
assertEquals(1634567890L, deserializedMap["timestamp"])
|
|
223
|
+
|
|
224
|
+
// Check the serializable object
|
|
225
|
+
val deserializedUser = deserializedMap["user"] as Map<String, Any?>
|
|
226
|
+
assertEquals(1001L, deserializedUser["id"])
|
|
227
|
+
assertEquals("testuser", deserializedUser["username"])
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package com.klarna.mobile.sdk.reactnative.common.util
|
|
2
|
+
|
|
3
|
+
import kotlinx.serialization.SerialName
|
|
4
|
+
import kotlinx.serialization.Serializable
|
|
5
|
+
import kotlinx.serialization.SerializationException
|
|
6
|
+
import org.junit.Test
|
|
7
|
+
import kotlin.test.assertEquals
|
|
8
|
+
import kotlin.test.assertFailsWith
|
|
9
|
+
import kotlin.test.assertNotNull
|
|
10
|
+
|
|
11
|
+
internal class ParserUtilTest {
|
|
12
|
+
@Test
|
|
13
|
+
fun `test gson is not null`() {
|
|
14
|
+
assertNotNull(ParserUtil.json)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@Test
|
|
18
|
+
fun `test toJson success`() {
|
|
19
|
+
val serialized = ParserUtil.toJson(TestData("test"))
|
|
20
|
+
assertEquals("{\"data\":\"test\"}", serialized)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@Test
|
|
24
|
+
fun `test toJson fail`() {
|
|
25
|
+
val testData = NonSerializableData("")
|
|
26
|
+
assertFailsWith(SerializationException::class, "test") {
|
|
27
|
+
ParserUtil.toJson(testData)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Serializable
|
|
32
|
+
private data class TestData(
|
|
33
|
+
@SerialName("data") val data: String,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
private data class NonSerializableData(
|
|
37
|
+
val data: String,
|
|
38
|
+
)
|
|
39
|
+
}
|
|
@@ -27,7 +27,6 @@ import org.junit.Test;
|
|
|
27
27
|
import org.junit.runner.RunWith;
|
|
28
28
|
import org.mockito.MockedStatic;
|
|
29
29
|
import org.mockito.Mockito;
|
|
30
|
-
import org.mockito.junit.MockitoJUnitRunner;
|
|
31
30
|
import org.robolectric.RobolectricTestRunner;
|
|
32
31
|
|
|
33
32
|
import java.util.Collections;
|
|
@@ -42,7 +41,8 @@ public class KlarnaPaymentViewManagerTest {
|
|
|
42
41
|
@Before
|
|
43
42
|
public void setup() {
|
|
44
43
|
Application application = ApplicationProvider.getApplicationContext();
|
|
45
|
-
ReactApplicationContext reactContext =
|
|
44
|
+
ReactApplicationContext reactContext = Mockito.mock(ReactApplicationContext.class);
|
|
45
|
+
Mockito.when(reactContext.getApplicationContext()).thenAnswer(invocation -> application);
|
|
46
46
|
|
|
47
47
|
manager = new KlarnaPaymentViewManager(reactContext);
|
|
48
48
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-klarna-inapp-sdk",
|
|
3
3
|
"title": "Klarna Mobile SDK React Native",
|
|
4
|
-
"version": "2.4.
|
|
4
|
+
"version": "2.4.4",
|
|
5
5
|
"description": "This library wraps Klarna Mobile SDK and exposes its functionality as React Native components.",
|
|
6
6
|
"main": "lib/commonjs/index",
|
|
7
7
|
"module": "lib/module/index",
|
|
@@ -105,6 +105,13 @@
|
|
|
105
105
|
"jsSrcsDir": "src/specs",
|
|
106
106
|
"android": {
|
|
107
107
|
"javaPackageName": "com.klarna.mobile.sdk.reactnative"
|
|
108
|
+
},
|
|
109
|
+
"ios": {
|
|
110
|
+
"componentProvider": {
|
|
111
|
+
"RNKlarnaStandaloneWebView": "KlarnaStandaloneWebViewWrapper",
|
|
112
|
+
"RNKlarnaCheckoutView": "KlarnaCheckoutViewWrapper",
|
|
113
|
+
"RNKlarnaPaymentView": "PaymentViewWrapper"
|
|
114
|
+
}
|
|
108
115
|
}
|
|
109
116
|
}
|
|
110
117
|
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
package com.klarna.mobile.sdk.reactnative.common.util;
|
|
2
|
-
|
|
3
|
-
import android.util.Log;
|
|
4
|
-
|
|
5
|
-
import com.google.gson.Gson;
|
|
6
|
-
import com.google.gson.GsonBuilder;
|
|
7
|
-
|
|
8
|
-
public class ParserUtil {
|
|
9
|
-
public static final Gson gson = new GsonBuilder().create();
|
|
10
|
-
|
|
11
|
-
public static String toJson(Object obj) {
|
|
12
|
-
return gson.toJson(obj);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
public static String toJsonSafe(Object obj) {
|
|
16
|
-
try {
|
|
17
|
-
return gson.toJson(obj);
|
|
18
|
-
} catch (Throwable t) {
|
|
19
|
-
Log.e(ParserUtil.class.getSimpleName(), "Failed to serialize object to string with Gson: " + t.getMessage());
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
public static <T> T fromJson(String json, Class<T> cls) {
|
|
25
|
-
return gson.fromJson(json, cls);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
public static <T> T fromJsonSafe(String json, Class<T> cls) {
|
|
29
|
-
try {
|
|
30
|
-
return gson.fromJson(json, cls);
|
|
31
|
-
} catch (Throwable t) {
|
|
32
|
-
Log.e(ParserUtil.class.getSimpleName(), "Failed to deserialize object from string with Gson: " + t.getMessage());
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|