@loyalytics/swan-react-native-sdk 2.5.1-beta.0 → 2.5.1-beta.2
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/android/build.gradle +23 -1
- package/android/gradle.properties +1 -0
- package/android/settings.gradle +1 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselAutoRemoteViews.kt +16 -6
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselRemoteViews.kt +5 -1
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselTemplate.kt +21 -5
- package/android/src/main/res/layout/swan_carousel_auto_expanded.xml +1 -1
- package/android/src/main/res/layout/swan_carousel_auto_flipper_item.xml +33 -0
- package/android/src/main/res/layout/swan_carousel_expanded.xml +43 -9
- package/lib/commonjs/version.js +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/src/version.d.ts +1 -1
- package/lib/typescript/module/src/version.d.ts +1 -1
- package/package.json +1 -1
package/android/build.gradle
CHANGED
|
@@ -7,6 +7,7 @@ buildscript {
|
|
|
7
7
|
mavenCentral()
|
|
8
8
|
}
|
|
9
9
|
dependencies {
|
|
10
|
+
classpath("com.android.tools.build:gradle:${safeExtGet('androidGradlePluginVersion', '8.2.2')}")
|
|
10
11
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${safeExtGet('kotlinVersion', '1.9.22')}")
|
|
11
12
|
}
|
|
12
13
|
}
|
|
@@ -19,7 +20,7 @@ android {
|
|
|
19
20
|
compileSdkVersion safeExtGet('compileSdkVersion', 34)
|
|
20
21
|
|
|
21
22
|
defaultConfig {
|
|
22
|
-
minSdkVersion safeExtGet('minSdkVersion',
|
|
23
|
+
minSdkVersion safeExtGet('minSdkVersion', 24)
|
|
23
24
|
targetSdkVersion safeExtGet('targetSdkVersion', 34)
|
|
24
25
|
}
|
|
25
26
|
|
|
@@ -29,6 +30,12 @@ android {
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
testOptions {
|
|
34
|
+
unitTests {
|
|
35
|
+
includeAndroidResources = true
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
def javaVersion = safeExtGet('jvmTargetVersion', '17')
|
|
33
40
|
|
|
34
41
|
compileOptions {
|
|
@@ -55,6 +62,18 @@ repositories {
|
|
|
55
62
|
mavenCentral()
|
|
56
63
|
}
|
|
57
64
|
|
|
65
|
+
// For standalone builds (CI, local `./gradlew test`), react-native:+ can't resolve
|
|
66
|
+
// because the host app's Maven repo isn't available. Substitute with react-android
|
|
67
|
+
// from Maven Central. When consumed by an app, rootProject.name != 'swan-android'
|
|
68
|
+
// so this block is skipped and the app's own dependency resolution applies.
|
|
69
|
+
if (rootProject.name == 'swan-android') {
|
|
70
|
+
configurations.all {
|
|
71
|
+
resolutionStrategy.dependencySubstitution {
|
|
72
|
+
substitute module('com.facebook.react:react-native') using module('com.facebook.react:react-android:0.76.2')
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
58
77
|
dependencies {
|
|
59
78
|
implementation "com.facebook.react:react-native:+"
|
|
60
79
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:${safeExtGet('kotlinVersion', '1.9.22')}"
|
|
@@ -63,4 +82,7 @@ dependencies {
|
|
|
63
82
|
|
|
64
83
|
testImplementation "junit:junit:4.13.2"
|
|
65
84
|
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3"
|
|
85
|
+
testImplementation "org.robolectric:robolectric:4.14.1"
|
|
86
|
+
testImplementation "androidx.test:core:1.6.1"
|
|
87
|
+
testImplementation "org.json:json:20231013"
|
|
66
88
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
android.useAndroidX=true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rootProject.name = 'swan-android'
|
package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselAutoRemoteViews.kt
CHANGED
|
@@ -5,6 +5,7 @@ import android.content.Context
|
|
|
5
5
|
import android.graphics.Bitmap
|
|
6
6
|
import android.widget.RemoteViews
|
|
7
7
|
import com.loyalytics.swan.R
|
|
8
|
+
import org.json.JSONObject
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Builds RemoteViews for auto-carousel using ViewFlipper.
|
|
@@ -12,6 +13,9 @@ import com.loyalytics.swan.R
|
|
|
12
13
|
* All images are added as children to the ViewFlipper which handles
|
|
13
14
|
* cycling natively with slide animations. No AlarmManager needed.
|
|
14
15
|
*
|
|
16
|
+
* Each child embeds per-item title and body text alongside the image,
|
|
17
|
+
* so text rotates together with the corresponding image.
|
|
18
|
+
*
|
|
15
19
|
* The entire flipper area gets a single click action (auto-carousel
|
|
16
20
|
* uses one deep link for the whole notification, same as CleverTap).
|
|
17
21
|
*
|
|
@@ -31,6 +35,7 @@ object CarouselAutoRemoteViews {
|
|
|
31
35
|
notificationId: Int,
|
|
32
36
|
title: String,
|
|
33
37
|
body: String,
|
|
38
|
+
items: List<JSONObject>,
|
|
34
39
|
bitmaps: List<Bitmap?>,
|
|
35
40
|
intervalMs: Int,
|
|
36
41
|
messageId: String,
|
|
@@ -44,12 +49,16 @@ object CarouselAutoRemoteViews {
|
|
|
44
49
|
|
|
45
50
|
// autoStart and flipInterval are set in XML (RemoteViews doesn't allow setAutoStart)
|
|
46
51
|
|
|
47
|
-
// Add each
|
|
52
|
+
// Add each item as a child of the ViewFlipper with image + title + body
|
|
48
53
|
removeAllViews(R.id.swan_carousel_flipper)
|
|
49
|
-
val
|
|
50
|
-
for (
|
|
51
|
-
val
|
|
52
|
-
|
|
54
|
+
val limit = minOf(items.size, bitmaps.size, MAX_FLIPPER_IMAGES)
|
|
55
|
+
for (i in 0 until limit) {
|
|
56
|
+
val bitmap = bitmaps[i] ?: continue
|
|
57
|
+
val item = items[i]
|
|
58
|
+
val childView = RemoteViews(packageName, R.layout.swan_carousel_auto_flipper_item)
|
|
59
|
+
childView.setImageViewBitmap(R.id.swan_carousel_auto_flipper_image, bitmap)
|
|
60
|
+
childView.setTextViewText(R.id.swan_carousel_auto_flipper_title, item.optString("title", ""))
|
|
61
|
+
childView.setTextViewText(R.id.swan_carousel_auto_flipper_body, item.optString("body", ""))
|
|
53
62
|
addView(R.id.swan_carousel_flipper, childView)
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -62,9 +71,10 @@ object CarouselAutoRemoteViews {
|
|
|
62
71
|
)
|
|
63
72
|
}
|
|
64
73
|
|
|
74
|
+
val firstItemTitle = items.firstOrNull()?.optString("title", "") ?: ""
|
|
65
75
|
val collapsed = RemoteViews(packageName, R.layout.swan_carousel_collapsed).apply {
|
|
66
76
|
setTextViewText(R.id.swan_carousel_collapsed_title, title)
|
|
67
|
-
setTextViewText(R.id.swan_carousel_collapsed_body, body)
|
|
77
|
+
setTextViewText(R.id.swan_carousel_collapsed_body, firstItemTitle.ifEmpty { body })
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
return AutoCarouselViews(expanded, collapsed)
|
package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselRemoteViews.kt
CHANGED
|
@@ -28,6 +28,8 @@ object CarouselRemoteViews {
|
|
|
28
28
|
notificationId: Int,
|
|
29
29
|
title: String,
|
|
30
30
|
body: String,
|
|
31
|
+
itemTitle: String,
|
|
32
|
+
itemBody: String,
|
|
31
33
|
bitmaps: List<Bitmap?>,
|
|
32
34
|
currentIndex: Int,
|
|
33
35
|
totalItems: Int,
|
|
@@ -39,6 +41,8 @@ object CarouselRemoteViews {
|
|
|
39
41
|
val expanded = RemoteViews(packageName, R.layout.swan_carousel_expanded).apply {
|
|
40
42
|
setTextViewText(R.id.swan_carousel_title, title)
|
|
41
43
|
setTextViewText(R.id.swan_carousel_body, body)
|
|
44
|
+
setTextViewText(R.id.swan_carousel_item_title, itemTitle)
|
|
45
|
+
setTextViewText(R.id.swan_carousel_item_body, itemBody)
|
|
42
46
|
setTextViewText(R.id.swan_carousel_counter, "${currentIndex + 1} / $totalItems")
|
|
43
47
|
|
|
44
48
|
// Populate ViewFlipper with all images
|
|
@@ -92,7 +96,7 @@ object CarouselRemoteViews {
|
|
|
92
96
|
|
|
93
97
|
val collapsed = RemoteViews(packageName, R.layout.swan_carousel_collapsed).apply {
|
|
94
98
|
setTextViewText(R.id.swan_carousel_collapsed_title, title)
|
|
95
|
-
setTextViewText(R.id.swan_carousel_collapsed_body, body)
|
|
99
|
+
setTextViewText(R.id.swan_carousel_collapsed_body, itemTitle.ifEmpty { body })
|
|
96
100
|
}
|
|
97
101
|
|
|
98
102
|
return CarouselViews(expanded, collapsed)
|
|
@@ -84,7 +84,8 @@ class CarouselTemplate : SwanNotificationTemplate {
|
|
|
84
84
|
notificationId: Int,
|
|
85
85
|
messageId: String,
|
|
86
86
|
itemIndex: Int,
|
|
87
|
-
itemRoute: String
|
|
87
|
+
itemRoute: String,
|
|
88
|
+
isContentIntent: Boolean = false
|
|
88
89
|
): PendingIntent {
|
|
89
90
|
val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
|
|
90
91
|
?: Intent(Intent.ACTION_MAIN).apply {
|
|
@@ -107,8 +108,15 @@ class CarouselTemplate : SwanNotificationTemplate {
|
|
|
107
108
|
PendingIntent.FLAG_UPDATE_CURRENT
|
|
108
109
|
}
|
|
109
110
|
|
|
111
|
+
// Use different request codes for image click vs content intent to prevent collision
|
|
112
|
+
val requestCode = if (isContentIntent) {
|
|
113
|
+
"${notificationId}CONTENT_CLICK".hashCode()
|
|
114
|
+
} else {
|
|
115
|
+
"$notificationId${ACTION_CLICK}$itemIndex".hashCode()
|
|
116
|
+
}
|
|
117
|
+
|
|
110
118
|
return PendingIntent.getActivity(
|
|
111
|
-
context,
|
|
119
|
+
context, requestCode, launchIntent, flags
|
|
112
120
|
)
|
|
113
121
|
}
|
|
114
122
|
}
|
|
@@ -321,7 +329,9 @@ class CarouselTemplate : SwanNotificationTemplate {
|
|
|
321
329
|
context = context,
|
|
322
330
|
notificationId = notificationId,
|
|
323
331
|
title = title,
|
|
324
|
-
body =
|
|
332
|
+
body = body,
|
|
333
|
+
itemTitle = currentItem.optString("title", ""),
|
|
334
|
+
itemBody = currentItem.optString("body", ""),
|
|
325
335
|
bitmaps = scaledBitmaps,
|
|
326
336
|
currentIndex = currentIndex,
|
|
327
337
|
totalItems = items.size,
|
|
@@ -398,6 +408,7 @@ class CarouselTemplate : SwanNotificationTemplate {
|
|
|
398
408
|
notificationId = notificationId,
|
|
399
409
|
title = title,
|
|
400
410
|
body = body,
|
|
411
|
+
items = items,
|
|
401
412
|
bitmaps = scaledBitmaps,
|
|
402
413
|
intervalMs = intervalMs,
|
|
403
414
|
messageId = messageId,
|
|
@@ -444,13 +455,16 @@ class CarouselTemplate : SwanNotificationTemplate {
|
|
|
444
455
|
extras: Bundle
|
|
445
456
|
): PendingIntent {
|
|
446
457
|
// For CLICK, use PendingIntent.getActivity() to avoid Android 12+ trampoline restriction
|
|
458
|
+
// This is always the content intent (notification body tap) — image clicks go through
|
|
459
|
+
// createClickActivityPendingIntent() directly from RemoteViews builders
|
|
447
460
|
if (action == ACTION_CLICK) {
|
|
448
461
|
return createClickActivityPendingIntent(
|
|
449
462
|
context,
|
|
450
463
|
notificationId,
|
|
451
464
|
extras.getString(EXTRA_MESSAGE_ID, ""),
|
|
452
465
|
extras.getInt(EXTRA_ITEM_INDEX, 0),
|
|
453
|
-
extras.getString(EXTRA_ITEM_ROUTE, "")
|
|
466
|
+
extras.getString(EXTRA_ITEM_ROUTE, ""),
|
|
467
|
+
isContentIntent = true
|
|
454
468
|
)
|
|
455
469
|
}
|
|
456
470
|
|
|
@@ -517,7 +531,9 @@ class CarouselTemplate : SwanNotificationTemplate {
|
|
|
517
531
|
}
|
|
518
532
|
else -> {
|
|
519
533
|
expanded.setViewPadding(R.id.swan_carousel_header, px16, 0, px16, px8)
|
|
520
|
-
expanded.setViewPadding(R.id.
|
|
534
|
+
expanded.setViewPadding(R.id.swan_carousel_item_title, px16, 0, px16, 0)
|
|
535
|
+
expanded.setViewPadding(R.id.swan_carousel_item_body, px16, 0, px16, 0)
|
|
536
|
+
expanded.setViewPadding(R.id.swan_carousel_counter, px16, 0, px16, 0)
|
|
521
537
|
}
|
|
522
538
|
}
|
|
523
539
|
}
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
<ViewFlipper
|
|
42
42
|
android:id="@+id/swan_carousel_flipper"
|
|
43
43
|
android:layout_width="match_parent"
|
|
44
|
-
android:layout_height="
|
|
44
|
+
android:layout_height="wrap_content"
|
|
45
45
|
android:layout_below="@id/swan_carousel_auto_header"
|
|
46
46
|
android:inAnimation="@anim/swan_slide_in_right"
|
|
47
47
|
android:outAnimation="@anim/swan_slide_out_left"
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:layout_width="match_parent"
|
|
4
|
+
android:layout_height="wrap_content"
|
|
5
|
+
android:orientation="vertical">
|
|
6
|
+
|
|
7
|
+
<ImageView
|
|
8
|
+
android:id="@+id/swan_carousel_auto_flipper_image"
|
|
9
|
+
android:layout_width="match_parent"
|
|
10
|
+
android:layout_height="144dp"
|
|
11
|
+
android:scaleType="centerCrop"
|
|
12
|
+
android:contentDescription="Carousel image" />
|
|
13
|
+
|
|
14
|
+
<TextView
|
|
15
|
+
android:id="@+id/swan_carousel_auto_flipper_title"
|
|
16
|
+
android:layout_width="match_parent"
|
|
17
|
+
android:layout_height="wrap_content"
|
|
18
|
+
android:textSize="13sp"
|
|
19
|
+
android:textStyle="bold"
|
|
20
|
+
android:textColor="#DE000000"
|
|
21
|
+
android:maxLines="1"
|
|
22
|
+
android:ellipsize="end"
|
|
23
|
+
android:paddingTop="4dp" />
|
|
24
|
+
|
|
25
|
+
<TextView
|
|
26
|
+
android:id="@+id/swan_carousel_auto_flipper_body"
|
|
27
|
+
android:layout_width="match_parent"
|
|
28
|
+
android:layout_height="wrap_content"
|
|
29
|
+
android:textSize="12sp"
|
|
30
|
+
android:textColor="#8A000000"
|
|
31
|
+
android:maxLines="1"
|
|
32
|
+
android:ellipsize="end" />
|
|
33
|
+
</LinearLayout>
|
|
@@ -78,17 +78,51 @@
|
|
|
78
78
|
android:contentDescription="Next" />
|
|
79
79
|
</RelativeLayout>
|
|
80
80
|
|
|
81
|
-
<!--
|
|
82
|
-
<
|
|
83
|
-
android:
|
|
84
|
-
android:layout_width="wrap_content"
|
|
81
|
+
<!-- Item title + body + counter -->
|
|
82
|
+
<LinearLayout
|
|
83
|
+
android:layout_width="match_parent"
|
|
85
84
|
android:layout_height="wrap_content"
|
|
86
|
-
android:
|
|
87
|
-
android:textSize="11sp"
|
|
88
|
-
android:textColor="#8A000000"
|
|
85
|
+
android:orientation="horizontal"
|
|
89
86
|
android:paddingStart="0dp"
|
|
90
87
|
android:paddingEnd="0dp"
|
|
91
|
-
android:paddingTop="
|
|
92
|
-
android:paddingBottom="4dp"
|
|
88
|
+
android:paddingTop="4dp"
|
|
89
|
+
android:paddingBottom="4dp"
|
|
90
|
+
android:gravity="center_vertical">
|
|
91
|
+
|
|
92
|
+
<LinearLayout
|
|
93
|
+
android:layout_width="0dp"
|
|
94
|
+
android:layout_height="wrap_content"
|
|
95
|
+
android:layout_weight="1"
|
|
96
|
+
android:orientation="vertical">
|
|
97
|
+
|
|
98
|
+
<TextView
|
|
99
|
+
android:id="@+id/swan_carousel_item_title"
|
|
100
|
+
android:layout_width="match_parent"
|
|
101
|
+
android:layout_height="wrap_content"
|
|
102
|
+
android:textSize="13sp"
|
|
103
|
+
android:textStyle="bold"
|
|
104
|
+
android:textColor="#DE000000"
|
|
105
|
+
android:maxLines="1"
|
|
106
|
+
android:ellipsize="end" />
|
|
107
|
+
|
|
108
|
+
<TextView
|
|
109
|
+
android:id="@+id/swan_carousel_item_body"
|
|
110
|
+
android:layout_width="match_parent"
|
|
111
|
+
android:layout_height="wrap_content"
|
|
112
|
+
android:textSize="12sp"
|
|
113
|
+
android:textColor="#8A000000"
|
|
114
|
+
android:maxLines="1"
|
|
115
|
+
android:ellipsize="end" />
|
|
116
|
+
</LinearLayout>
|
|
117
|
+
|
|
118
|
+
<TextView
|
|
119
|
+
android:id="@+id/swan_carousel_counter"
|
|
120
|
+
android:layout_width="wrap_content"
|
|
121
|
+
android:layout_height="wrap_content"
|
|
122
|
+
android:textSize="11sp"
|
|
123
|
+
android:textColor="#8A000000"
|
|
124
|
+
android:paddingStart="8dp"
|
|
125
|
+
android:paddingEnd="0dp" />
|
|
126
|
+
</LinearLayout>
|
|
93
127
|
|
|
94
128
|
</LinearLayout>
|
package/lib/commonjs/version.js
CHANGED
|
@@ -8,5 +8,5 @@ exports.SDK_VERSION = void 0;
|
|
|
8
8
|
// This file is generated from package.json version during build.
|
|
9
9
|
// See scripts/generate-version.js
|
|
10
10
|
|
|
11
|
-
const SDK_VERSION = exports.SDK_VERSION = '2.5.1-beta.
|
|
11
|
+
const SDK_VERSION = exports.SDK_VERSION = '2.5.1-beta.2';
|
|
12
12
|
//# sourceMappingURL=version.js.map
|
package/lib/module/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.5.1-beta.
|
|
1
|
+
export declare const SDK_VERSION = "2.5.1-beta.2";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const SDK_VERSION = "2.5.1-beta.
|
|
1
|
+
export declare const SDK_VERSION = "2.5.1-beta.2";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|