@loyalytics/swan-react-native-sdk 2.1.3-beta.0 → 2.1.3-beta.1
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 +66 -0
- package/android/src/main/AndroidManifest.xml +10 -0
- package/android/src/main/kotlin/com/loyalytics/swan/SwanNotificationModule.kt +43 -0
- package/android/src/main/kotlin/com/loyalytics/swan/SwanNotificationPackage.kt +16 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/SwanNotificationActionReceiver.kt +49 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/SwanNotificationTemplate.kt +20 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/SwanTemplateRegistry.kt +47 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselAutoRemoteViews.kt +103 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselFilmstripRemoteViews.kt +132 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselRemoteViews.kt +129 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/carousel/CarouselTemplate.kt +412 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/common/NotificationBitmapCache.kt +70 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/common/NotificationImageLoader.kt +97 -0
- package/android/src/main/kotlin/com/loyalytics/swan/templates/common/NotificationStateManager.kt +85 -0
- package/android/src/main/res/anim/swan_fade_in.xml +6 -0
- package/android/src/main/res/anim/swan_fade_out.xml +6 -0
- package/android/src/main/res/anim/swan_slide_in_right.xml +8 -0
- package/android/src/main/res/anim/swan_slide_out_left.xml +8 -0
- package/android/src/main/res/drawable/swan_ic_chevron_left.xml +11 -0
- package/android/src/main/res/drawable/swan_ic_chevron_right.xml +11 -0
- package/android/src/main/res/layout/swan_carousel_auto_expanded.xml +51 -0
- package/android/src/main/res/layout/swan_carousel_collapsed.xml +31 -0
- package/android/src/main/res/layout/swan_carousel_expanded.xml +96 -0
- package/android/src/main/res/layout/swan_carousel_filmstrip_expanded.xml +115 -0
- package/android/src/main/res/layout/swan_carousel_flipper_item.xml +7 -0
- package/android/src/test/kotlin/com/loyalytics/swan/templates/carousel/CarouselTemplateTest.kt +125 -0
- package/docs/SDK_INDUSTRY_REVIEW_REPORT.md +347 -0
- package/docs/Swan_Push_Notifications.postman_collection.json +330 -0
- package/docs/deep-link-attribution.md +281 -0
- package/ios/SwanNotificationContentExtension/Info.plist +40 -0
- package/ios/SwanNotificationContentExtension/MainInterface.storyboard +19 -0
- package/ios/SwanNotificationContentExtension/NotificationViewController.swift +190 -0
- package/ios/SwanNotificationContentExtension/SwanNotificationContentExtension.entitlements +10 -0
- package/ios/SwanNotificationContentExtension/common/ImageDownloader.swift +32 -0
- package/ios/SwanNotificationContentExtension/templates/CarouselView.swift +336 -0
- package/lib/commonjs/constants/ApiUrls.js.map +1 -1
- package/lib/commonjs/index.js +117 -35
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/providers/NullPushProvider.js.map +1 -1
- package/lib/commonjs/services/DeviceRegistrationService.js.map +1 -1
- package/lib/commonjs/state/AuthStateMachine.js.map +1 -1
- package/lib/commonjs/state/DeviceStateMachine.js.map +1 -1
- package/lib/commonjs/state/PushStateMachine.js.map +1 -1
- package/lib/commonjs/utils/FirebaseNotificationManager.js.map +1 -1
- package/lib/commonjs/utils/Logger.js.map +1 -1
- package/lib/commonjs/utils/SharedCredentialsManager.js +28 -0
- package/lib/commonjs/utils/SharedCredentialsManager.js.map +1 -1
- package/lib/commonjs/version.js +1 -1
- package/lib/module/index.js +117 -35
- package/lib/module/index.js.map +1 -1
- package/lib/module/providers/NullPushProvider.js.map +1 -1
- package/lib/module/services/DeviceRegistrationService.js.map +1 -1
- package/lib/module/state/AuthStateMachine.js.map +1 -1
- package/lib/module/state/DeviceStateMachine.js.map +1 -1
- package/lib/module/state/PushStateMachine.js.map +1 -1
- package/lib/module/utils/FirebaseNotificationManager.js.map +1 -1
- package/lib/module/utils/Logger.js.map +1 -1
- package/lib/module/utils/SharedCredentialsManager.js +28 -0
- package/lib/module/utils/SharedCredentialsManager.js.map +1 -1
- package/lib/module/version.js +1 -1
- package/lib/typescript/commonjs/src/constants/ApiUrls.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/providers/NullPushProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/services/DeviceRegistrationService.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/state/AuthStateMachine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/state/DeviceStateMachine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/state/PushStateMachine.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/utils/FirebaseNotificationManager.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/utils/Logger.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/utils/SharedCredentialsManager.d.ts +13 -0
- package/lib/typescript/commonjs/src/utils/SharedCredentialsManager.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/version.d.ts +1 -1
- package/lib/typescript/module/src/constants/ApiUrls.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/providers/NullPushProvider.d.ts.map +1 -1
- package/lib/typescript/module/src/services/DeviceRegistrationService.d.ts.map +1 -1
- package/lib/typescript/module/src/state/AuthStateMachine.d.ts.map +1 -1
- package/lib/typescript/module/src/state/DeviceStateMachine.d.ts.map +1 -1
- package/lib/typescript/module/src/state/PushStateMachine.d.ts.map +1 -1
- package/lib/typescript/module/src/utils/FirebaseNotificationManager.d.ts.map +1 -1
- package/lib/typescript/module/src/utils/Logger.d.ts.map +1 -1
- package/lib/typescript/module/src/utils/SharedCredentialsManager.d.ts +13 -0
- package/lib/typescript/module/src/utils/SharedCredentialsManager.d.ts.map +1 -1
- package/lib/typescript/module/src/version.d.ts +1 -1
- package/package.json +7 -3
- package/react-native.config.json +12 -0
- package/scripts/setup-ios-extension.js +100 -20
- package/scripts/test-carousel-push.js +266 -0
- package/swan-react-native-sdk.podspec +18 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<set xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<translate
|
|
4
|
+
android:fromXDelta="0"
|
|
5
|
+
android:toXDelta="-100%p"
|
|
6
|
+
android:duration="300"
|
|
7
|
+
android:interpolator="@android:anim/accelerate_interpolator" />
|
|
8
|
+
</set>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24"
|
|
7
|
+
android:tint="#DE000000">
|
|
8
|
+
<path
|
|
9
|
+
android:fillColor="#FF000000"
|
|
10
|
+
android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z" />
|
|
11
|
+
</vector>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:width="24dp"
|
|
4
|
+
android:height="24dp"
|
|
5
|
+
android:viewportWidth="24"
|
|
6
|
+
android:viewportHeight="24"
|
|
7
|
+
android:tint="#DE000000">
|
|
8
|
+
<path
|
|
9
|
+
android:fillColor="#FF000000"
|
|
10
|
+
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z" />
|
|
11
|
+
</vector>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:id="@+id/swan_carousel_auto_big"
|
|
4
|
+
android:layout_width="match_parent"
|
|
5
|
+
android:layout_height="wrap_content"
|
|
6
|
+
android:background="@android:color/transparent">
|
|
7
|
+
|
|
8
|
+
<!-- Header -->
|
|
9
|
+
<RelativeLayout
|
|
10
|
+
android:id="@+id/swan_carousel_auto_header"
|
|
11
|
+
android:layout_width="match_parent"
|
|
12
|
+
android:layout_height="wrap_content"
|
|
13
|
+
android:paddingLeft="0dp"
|
|
14
|
+
android:paddingRight="0dp"
|
|
15
|
+
android:paddingTop="0dp"
|
|
16
|
+
android:paddingBottom="8dp">
|
|
17
|
+
|
|
18
|
+
<TextView
|
|
19
|
+
android:id="@+id/swan_carousel_auto_title"
|
|
20
|
+
android:layout_width="match_parent"
|
|
21
|
+
android:layout_height="wrap_content"
|
|
22
|
+
android:layout_marginTop="0dp"
|
|
23
|
+
android:textSize="14sp"
|
|
24
|
+
android:textStyle="bold"
|
|
25
|
+
android:textColor="#DE000000"
|
|
26
|
+
android:maxLines="1"
|
|
27
|
+
android:ellipsize="end" />
|
|
28
|
+
|
|
29
|
+
<TextView
|
|
30
|
+
android:id="@+id/swan_carousel_auto_body"
|
|
31
|
+
android:layout_width="match_parent"
|
|
32
|
+
android:layout_height="wrap_content"
|
|
33
|
+
android:layout_below="@id/swan_carousel_auto_title"
|
|
34
|
+
android:textSize="12sp"
|
|
35
|
+
android:textColor="#8A000000"
|
|
36
|
+
android:maxLines="2"
|
|
37
|
+
android:ellipsize="end" />
|
|
38
|
+
</RelativeLayout>
|
|
39
|
+
|
|
40
|
+
<!-- ViewFlipper auto-scrolls through images -->
|
|
41
|
+
<ViewFlipper
|
|
42
|
+
android:id="@+id/swan_carousel_flipper"
|
|
43
|
+
android:layout_width="match_parent"
|
|
44
|
+
android:layout_height="196dp"
|
|
45
|
+
android:layout_below="@id/swan_carousel_auto_header"
|
|
46
|
+
android:inAnimation="@anim/swan_slide_in_right"
|
|
47
|
+
android:outAnimation="@anim/swan_slide_out_left"
|
|
48
|
+
android:autoStart="true"
|
|
49
|
+
android:flipInterval="4000" />
|
|
50
|
+
|
|
51
|
+
</RelativeLayout>
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
android:paddingLeft="16dp"
|
|
7
|
+
android:paddingRight="16dp"
|
|
8
|
+
android:paddingTop="4dp"
|
|
9
|
+
android:paddingBottom="4dp">
|
|
10
|
+
|
|
11
|
+
<TextView
|
|
12
|
+
android:id="@+id/swan_carousel_collapsed_title"
|
|
13
|
+
android:layout_width="match_parent"
|
|
14
|
+
android:layout_height="wrap_content"
|
|
15
|
+
android:textSize="14sp"
|
|
16
|
+
android:textStyle="bold"
|
|
17
|
+
android:textColor="#DE000000"
|
|
18
|
+
android:maxLines="1"
|
|
19
|
+
android:ellipsize="end" />
|
|
20
|
+
|
|
21
|
+
<TextView
|
|
22
|
+
android:id="@+id/swan_carousel_collapsed_body"
|
|
23
|
+
android:layout_width="match_parent"
|
|
24
|
+
android:layout_height="wrap_content"
|
|
25
|
+
android:textSize="12sp"
|
|
26
|
+
android:textColor="#8A000000"
|
|
27
|
+
android:maxLines="1"
|
|
28
|
+
android:ellipsize="end"
|
|
29
|
+
android:paddingTop="2dp" />
|
|
30
|
+
|
|
31
|
+
</LinearLayout>
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:id="@+id/swan_carousel_big"
|
|
4
|
+
android:layout_width="match_parent"
|
|
5
|
+
android:layout_height="wrap_content"
|
|
6
|
+
android:background="@android:color/transparent"
|
|
7
|
+
android:orientation="vertical">
|
|
8
|
+
|
|
9
|
+
<!-- Header: title + body -->
|
|
10
|
+
<RelativeLayout
|
|
11
|
+
android:id="@+id/swan_carousel_header"
|
|
12
|
+
android:layout_width="match_parent"
|
|
13
|
+
android:layout_height="wrap_content"
|
|
14
|
+
android:paddingStart="0dp"
|
|
15
|
+
android:paddingEnd="0dp"
|
|
16
|
+
android:paddingTop="0dp"
|
|
17
|
+
android:paddingBottom="8dp">
|
|
18
|
+
|
|
19
|
+
<TextView
|
|
20
|
+
android:id="@+id/swan_carousel_title"
|
|
21
|
+
android:layout_width="match_parent"
|
|
22
|
+
android:layout_height="wrap_content"
|
|
23
|
+
android:textSize="14sp"
|
|
24
|
+
android:textStyle="bold"
|
|
25
|
+
android:textColor="#DE000000"
|
|
26
|
+
android:maxLines="1"
|
|
27
|
+
android:ellipsize="end" />
|
|
28
|
+
|
|
29
|
+
<TextView
|
|
30
|
+
android:id="@+id/swan_carousel_body"
|
|
31
|
+
android:layout_width="match_parent"
|
|
32
|
+
android:layout_height="wrap_content"
|
|
33
|
+
android:layout_below="@id/swan_carousel_title"
|
|
34
|
+
android:textSize="12sp"
|
|
35
|
+
android:textColor="#8A000000"
|
|
36
|
+
android:maxLines="1"
|
|
37
|
+
android:ellipsize="end" />
|
|
38
|
+
</RelativeLayout>
|
|
39
|
+
|
|
40
|
+
<!-- Image area with ViewFlipper + arrow overlays -->
|
|
41
|
+
<RelativeLayout
|
|
42
|
+
android:id="@+id/swan_carousel_image_section"
|
|
43
|
+
android:layout_width="match_parent"
|
|
44
|
+
android:layout_height="0dp"
|
|
45
|
+
android:layout_weight="1"
|
|
46
|
+
android:minHeight="196dp">
|
|
47
|
+
|
|
48
|
+
<!-- Center ViewFlipper (all images pre-loaded) -->
|
|
49
|
+
<ViewFlipper
|
|
50
|
+
android:id="@+id/swan_carousel_center"
|
|
51
|
+
android:layout_width="match_parent"
|
|
52
|
+
android:layout_height="match_parent"
|
|
53
|
+
android:inAnimation="@anim/swan_fade_in"
|
|
54
|
+
android:outAnimation="@anim/swan_fade_out" />
|
|
55
|
+
|
|
56
|
+
<!-- Previous arrow -->
|
|
57
|
+
<ImageView
|
|
58
|
+
android:id="@+id/swan_carousel_btn_prev"
|
|
59
|
+
android:layout_width="36dp"
|
|
60
|
+
android:layout_height="36dp"
|
|
61
|
+
android:layout_alignParentStart="true"
|
|
62
|
+
android:layout_centerVertical="true"
|
|
63
|
+
android:layout_marginStart="4dp"
|
|
64
|
+
android:src="@drawable/swan_ic_chevron_left"
|
|
65
|
+
android:background="#80FFFFFF"
|
|
66
|
+
android:padding="6dp"
|
|
67
|
+
android:contentDescription="Previous" />
|
|
68
|
+
|
|
69
|
+
<!-- Next arrow -->
|
|
70
|
+
<ImageView
|
|
71
|
+
android:id="@+id/swan_carousel_btn_next"
|
|
72
|
+
android:layout_width="36dp"
|
|
73
|
+
android:layout_height="36dp"
|
|
74
|
+
android:layout_alignParentEnd="true"
|
|
75
|
+
android:layout_centerVertical="true"
|
|
76
|
+
android:layout_marginEnd="4dp"
|
|
77
|
+
android:src="@drawable/swan_ic_chevron_right"
|
|
78
|
+
android:background="#80FFFFFF"
|
|
79
|
+
android:padding="6dp"
|
|
80
|
+
android:contentDescription="Next" />
|
|
81
|
+
</RelativeLayout>
|
|
82
|
+
|
|
83
|
+
<!-- Counter -->
|
|
84
|
+
<TextView
|
|
85
|
+
android:id="@+id/swan_carousel_counter"
|
|
86
|
+
android:layout_width="wrap_content"
|
|
87
|
+
android:layout_height="wrap_content"
|
|
88
|
+
android:layout_gravity="end"
|
|
89
|
+
android:textSize="11sp"
|
|
90
|
+
android:textColor="#8A000000"
|
|
91
|
+
android:paddingStart="0dp"
|
|
92
|
+
android:paddingEnd="0dp"
|
|
93
|
+
android:paddingTop="2dp"
|
|
94
|
+
android:paddingBottom="4dp" />
|
|
95
|
+
|
|
96
|
+
</LinearLayout>
|
|
@@ -0,0 +1,115 @@
|
|
|
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
|
+
android:background="@android:color/transparent">
|
|
7
|
+
|
|
8
|
+
<!-- Notification title -->
|
|
9
|
+
<TextView
|
|
10
|
+
android:id="@+id/swan_filmstrip_title"
|
|
11
|
+
android:layout_width="match_parent"
|
|
12
|
+
android:layout_height="wrap_content"
|
|
13
|
+
android:textSize="14sp"
|
|
14
|
+
android:textStyle="bold"
|
|
15
|
+
android:textColor="#DE000000"
|
|
16
|
+
android:paddingStart="0dp"
|
|
17
|
+
android:paddingEnd="0dp"
|
|
18
|
+
android:paddingTop="0dp"
|
|
19
|
+
android:paddingBottom="8dp"
|
|
20
|
+
android:maxLines="1"
|
|
21
|
+
android:ellipsize="end" />
|
|
22
|
+
|
|
23
|
+
<!-- 3-image filmstrip: left preview | center | right preview -->
|
|
24
|
+
<LinearLayout
|
|
25
|
+
android:layout_width="match_parent"
|
|
26
|
+
android:layout_height="196dp"
|
|
27
|
+
android:orientation="horizontal"
|
|
28
|
+
android:gravity="center_vertical">
|
|
29
|
+
|
|
30
|
+
<!-- Left preview (dimmed, inset) -->
|
|
31
|
+
<ImageView
|
|
32
|
+
android:id="@+id/swan_filmstrip_left"
|
|
33
|
+
android:layout_width="0dp"
|
|
34
|
+
android:layout_height="match_parent"
|
|
35
|
+
android:layout_weight="1"
|
|
36
|
+
android:layout_marginTop="16dp"
|
|
37
|
+
android:layout_marginBottom="16dp"
|
|
38
|
+
android:layout_marginStart="2dp"
|
|
39
|
+
android:scaleType="centerCrop"
|
|
40
|
+
android:alpha="0.6"
|
|
41
|
+
android:contentDescription="Previous item" />
|
|
42
|
+
|
|
43
|
+
<!-- Center image (full size) -->
|
|
44
|
+
<ImageView
|
|
45
|
+
android:id="@+id/swan_filmstrip_center"
|
|
46
|
+
android:layout_width="0dp"
|
|
47
|
+
android:layout_height="match_parent"
|
|
48
|
+
android:layout_weight="3"
|
|
49
|
+
android:layout_marginStart="4dp"
|
|
50
|
+
android:layout_marginEnd="4dp"
|
|
51
|
+
android:scaleType="centerCrop"
|
|
52
|
+
android:contentDescription="Current item" />
|
|
53
|
+
|
|
54
|
+
<!-- Right preview (dimmed, inset) -->
|
|
55
|
+
<ImageView
|
|
56
|
+
android:id="@+id/swan_filmstrip_right"
|
|
57
|
+
android:layout_width="0dp"
|
|
58
|
+
android:layout_height="match_parent"
|
|
59
|
+
android:layout_weight="1"
|
|
60
|
+
android:layout_marginTop="16dp"
|
|
61
|
+
android:layout_marginBottom="16dp"
|
|
62
|
+
android:layout_marginEnd="2dp"
|
|
63
|
+
android:scaleType="centerCrop"
|
|
64
|
+
android:alpha="0.6"
|
|
65
|
+
android:contentDescription="Next item" />
|
|
66
|
+
</LinearLayout>
|
|
67
|
+
|
|
68
|
+
<!-- Item title + body + counter -->
|
|
69
|
+
<LinearLayout
|
|
70
|
+
android:layout_width="match_parent"
|
|
71
|
+
android:layout_height="wrap_content"
|
|
72
|
+
android:orientation="horizontal"
|
|
73
|
+
android:paddingStart="0dp"
|
|
74
|
+
android:paddingEnd="0dp"
|
|
75
|
+
android:paddingTop="4dp"
|
|
76
|
+
android:paddingBottom="4dp"
|
|
77
|
+
android:gravity="center_vertical">
|
|
78
|
+
|
|
79
|
+
<LinearLayout
|
|
80
|
+
android:layout_width="0dp"
|
|
81
|
+
android:layout_height="wrap_content"
|
|
82
|
+
android:layout_weight="1"
|
|
83
|
+
android:orientation="vertical">
|
|
84
|
+
|
|
85
|
+
<TextView
|
|
86
|
+
android:id="@+id/swan_filmstrip_item_title"
|
|
87
|
+
android:layout_width="match_parent"
|
|
88
|
+
android:layout_height="wrap_content"
|
|
89
|
+
android:textSize="13sp"
|
|
90
|
+
android:textStyle="bold"
|
|
91
|
+
android:textColor="#DE000000"
|
|
92
|
+
android:maxLines="1"
|
|
93
|
+
android:ellipsize="end" />
|
|
94
|
+
|
|
95
|
+
<TextView
|
|
96
|
+
android:id="@+id/swan_filmstrip_item_body"
|
|
97
|
+
android:layout_width="match_parent"
|
|
98
|
+
android:layout_height="wrap_content"
|
|
99
|
+
android:textSize="12sp"
|
|
100
|
+
android:textColor="#8A000000"
|
|
101
|
+
android:maxLines="1"
|
|
102
|
+
android:ellipsize="end" />
|
|
103
|
+
</LinearLayout>
|
|
104
|
+
|
|
105
|
+
<TextView
|
|
106
|
+
android:id="@+id/swan_filmstrip_counter"
|
|
107
|
+
android:layout_width="wrap_content"
|
|
108
|
+
android:layout_height="wrap_content"
|
|
109
|
+
android:textSize="11sp"
|
|
110
|
+
android:textColor="#8A000000"
|
|
111
|
+
android:paddingStart="8dp"
|
|
112
|
+
android:paddingEnd="0dp" />
|
|
113
|
+
</LinearLayout>
|
|
114
|
+
|
|
115
|
+
</LinearLayout>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
|
+
android:id="@+id/swan_carousel_flipper_image"
|
|
4
|
+
android:layout_width="match_parent"
|
|
5
|
+
android:layout_height="match_parent"
|
|
6
|
+
android:scaleType="centerCrop"
|
|
7
|
+
android:contentDescription="Carousel image" />
|
package/android/src/test/kotlin/com/loyalytics/swan/templates/carousel/CarouselTemplateTest.kt
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
package com.loyalytics.swan.templates.carousel
|
|
2
|
+
|
|
3
|
+
import org.junit.Assert.*
|
|
4
|
+
import org.junit.Test
|
|
5
|
+
|
|
6
|
+
class CarouselTemplateTest {
|
|
7
|
+
|
|
8
|
+
// --- wrapIndex ---
|
|
9
|
+
|
|
10
|
+
@Test
|
|
11
|
+
fun `wrapIndex forward at end wraps to first`() {
|
|
12
|
+
assertEquals(0, wrapIndex(3, 3))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@Test
|
|
16
|
+
fun `wrapIndex backward at start wraps to last`() {
|
|
17
|
+
assertEquals(2, wrapIndex(-1, 3))
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
@Test
|
|
21
|
+
fun `wrapIndex normal forward`() {
|
|
22
|
+
assertEquals(1, wrapIndex(1, 3))
|
|
23
|
+
assertEquals(2, wrapIndex(2, 3))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@Test
|
|
27
|
+
fun `wrapIndex normal backward`() {
|
|
28
|
+
assertEquals(1, wrapIndex(1, 3))
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@Test
|
|
32
|
+
fun `wrapIndex with single item always returns 0`() {
|
|
33
|
+
assertEquals(0, wrapIndex(0, 1))
|
|
34
|
+
assertEquals(0, wrapIndex(1, 1))
|
|
35
|
+
assertEquals(0, wrapIndex(-1, 1))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test
|
|
39
|
+
fun `wrapIndex with size 0 returns 0`() {
|
|
40
|
+
assertEquals(0, wrapIndex(0, 0))
|
|
41
|
+
assertEquals(0, wrapIndex(5, 0))
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun `wrapIndex large negative wraps correctly`() {
|
|
46
|
+
// -5 with size 3: -5 mod 3 = -2, + 3 = 1
|
|
47
|
+
assertEquals(1, wrapIndex(-5, 3))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
@Test
|
|
51
|
+
fun `wrapIndex large positive wraps correctly`() {
|
|
52
|
+
// 7 with size 3: 7 mod 3 = 1
|
|
53
|
+
assertEquals(1, wrapIndex(7, 3))
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@Test
|
|
57
|
+
fun `wrapIndex full cycle returns to start`() {
|
|
58
|
+
// Going forward through all items returns to 0
|
|
59
|
+
val size = 5
|
|
60
|
+
for (i in 0 until size) {
|
|
61
|
+
assertEquals(i, wrapIndex(i, size))
|
|
62
|
+
}
|
|
63
|
+
assertEquals(0, wrapIndex(size, size))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@Test
|
|
67
|
+
fun `wrapIndex two items alternates correctly`() {
|
|
68
|
+
assertEquals(0, wrapIndex(0, 2))
|
|
69
|
+
assertEquals(1, wrapIndex(1, 2))
|
|
70
|
+
assertEquals(0, wrapIndex(2, 2))
|
|
71
|
+
assertEquals(1, wrapIndex(-1, 2))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@Test
|
|
75
|
+
fun `wrapIndex filmstrip prev-current-next for 3 items`() {
|
|
76
|
+
val size = 3
|
|
77
|
+
// At index 0: prev=2, current=0, next=1
|
|
78
|
+
assertEquals(2, wrapIndex(0 - 1, size))
|
|
79
|
+
assertEquals(0, wrapIndex(0, size))
|
|
80
|
+
assertEquals(1, wrapIndex(0 + 1, size))
|
|
81
|
+
|
|
82
|
+
// At index 2: prev=1, current=2, next=0
|
|
83
|
+
assertEquals(1, wrapIndex(2 - 1, size))
|
|
84
|
+
assertEquals(2, wrapIndex(2, size))
|
|
85
|
+
assertEquals(0, wrapIndex(2 + 1, size))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// --- canHandle ---
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
fun `canHandle returns true for carousel type`() {
|
|
92
|
+
val template = CarouselTemplate()
|
|
93
|
+
assertTrue(template.canHandle("carousel"))
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
@Test
|
|
97
|
+
fun `canHandle returns false for other types`() {
|
|
98
|
+
val template = CarouselTemplate()
|
|
99
|
+
assertFalse(template.canHandle("timer"))
|
|
100
|
+
assertFalse(template.canHandle("cta"))
|
|
101
|
+
assertFalse(template.canHandle(""))
|
|
102
|
+
assertFalse(template.canHandle("CAROUSEL"))
|
|
103
|
+
assertFalse(template.canHandle("Carousel"))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// --- CarouselAutoRemoteViews.MAX_FLIPPER_IMAGES cap ---
|
|
107
|
+
// (Cannot directly test private const, but we verify the build method
|
|
108
|
+
// is accessible and the type is correct)
|
|
109
|
+
|
|
110
|
+
@Test
|
|
111
|
+
fun `CarouselAutoRemoteViews is a singleton object`() {
|
|
112
|
+
// Verify the object exists and is accessible
|
|
113
|
+
assertNotNull(CarouselAutoRemoteViews)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@Test
|
|
117
|
+
fun `CarouselFilmstripRemoteViews is a singleton object`() {
|
|
118
|
+
assertNotNull(CarouselFilmstripRemoteViews)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Test
|
|
122
|
+
fun `CarouselRemoteViews is a singleton object`() {
|
|
123
|
+
assertNotNull(CarouselRemoteViews)
|
|
124
|
+
}
|
|
125
|
+
}
|