@renegades/react-native-tickle 0.1.0

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.
Files changed (79) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +352 -0
  3. package/Tickle.podspec +29 -0
  4. package/android/CMakeLists.txt +24 -0
  5. package/android/build.gradle +126 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/tickle/Tickle.kt +71 -0
  10. package/android/src/main/java/com/margelo/nitro/tickle/TicklePackage.kt +22 -0
  11. package/ios/Tickle.swift +185 -0
  12. package/ios/TickleUtils.swift +404 -0
  13. package/lib/module/Tickle.nitro.js +4 -0
  14. package/lib/module/Tickle.nitro.js.map +1 -0
  15. package/lib/module/index.js +254 -0
  16. package/lib/module/index.js.map +1 -0
  17. package/lib/module/package.json +1 -0
  18. package/lib/typescript/package.json +1 -0
  19. package/lib/typescript/src/Tickle.nitro.d.ts +63 -0
  20. package/lib/typescript/src/Tickle.nitro.d.ts.map +1 -0
  21. package/lib/typescript/src/index.d.ts +148 -0
  22. package/lib/typescript/src/index.d.ts.map +1 -0
  23. package/nitro.json +17 -0
  24. package/nitrogen/generated/android/c++/JHapticCurve.hpp +87 -0
  25. package/nitrogen/generated/android/c++/JHapticCurveControlPoint.hpp +61 -0
  26. package/nitrogen/generated/android/c++/JHapticEvent.hpp +94 -0
  27. package/nitrogen/generated/android/c++/JHapticEventParameter.hpp +62 -0
  28. package/nitrogen/generated/android/c++/JHapticEventType.hpp +58 -0
  29. package/nitrogen/generated/android/c++/JHapticImpactStyle.hpp +67 -0
  30. package/nitrogen/generated/android/c++/JHapticNotificationType.hpp +61 -0
  31. package/nitrogen/generated/android/c++/JHapticParameterType.hpp +58 -0
  32. package/nitrogen/generated/android/c++/JHybridTickleSpec.cpp +162 -0
  33. package/nitrogen/generated/android/c++/JHybridTickleSpec.hpp +79 -0
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticCurve.kt +44 -0
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticCurveControlPoint.kt +41 -0
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticEvent.kt +47 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticEventParameter.kt +41 -0
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticEventType.kt +23 -0
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticImpactStyle.kt +26 -0
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticNotificationType.kt +24 -0
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HapticParameterType.kt +23 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/HybridTickleSpec.kt +109 -0
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/tickle/tickleOnLoad.kt +35 -0
  44. package/nitrogen/generated/android/tickle+autolinking.cmake +81 -0
  45. package/nitrogen/generated/android/tickle+autolinking.gradle +27 -0
  46. package/nitrogen/generated/android/tickleOnLoad.cpp +44 -0
  47. package/nitrogen/generated/android/tickleOnLoad.hpp +25 -0
  48. package/nitrogen/generated/ios/Tickle+autolinking.rb +60 -0
  49. package/nitrogen/generated/ios/Tickle-Swift-Cxx-Bridge.cpp +33 -0
  50. package/nitrogen/generated/ios/Tickle-Swift-Cxx-Bridge.hpp +139 -0
  51. package/nitrogen/generated/ios/Tickle-Swift-Cxx-Umbrella.hpp +70 -0
  52. package/nitrogen/generated/ios/TickleAutolinking.mm +33 -0
  53. package/nitrogen/generated/ios/TickleAutolinking.swift +25 -0
  54. package/nitrogen/generated/ios/c++/HybridTickleSpecSwift.cpp +11 -0
  55. package/nitrogen/generated/ios/c++/HybridTickleSpecSwift.hpp +185 -0
  56. package/nitrogen/generated/ios/swift/HapticCurve.swift +46 -0
  57. package/nitrogen/generated/ios/swift/HapticCurveControlPoint.swift +35 -0
  58. package/nitrogen/generated/ios/swift/HapticEvent.swift +57 -0
  59. package/nitrogen/generated/ios/swift/HapticEventParameter.swift +35 -0
  60. package/nitrogen/generated/ios/swift/HapticEventType.swift +40 -0
  61. package/nitrogen/generated/ios/swift/HapticImpactStyle.swift +52 -0
  62. package/nitrogen/generated/ios/swift/HapticNotificationType.swift +44 -0
  63. package/nitrogen/generated/ios/swift/HapticParameterType.swift +40 -0
  64. package/nitrogen/generated/ios/swift/HybridTickleSpec.swift +69 -0
  65. package/nitrogen/generated/ios/swift/HybridTickleSpec_cxx.swift +282 -0
  66. package/nitrogen/generated/shared/c++/HapticCurve.hpp +96 -0
  67. package/nitrogen/generated/shared/c++/HapticCurveControlPoint.hpp +87 -0
  68. package/nitrogen/generated/shared/c++/HapticEvent.hpp +101 -0
  69. package/nitrogen/generated/shared/c++/HapticEventParameter.hpp +88 -0
  70. package/nitrogen/generated/shared/c++/HapticEventType.hpp +76 -0
  71. package/nitrogen/generated/shared/c++/HapticImpactStyle.hpp +88 -0
  72. package/nitrogen/generated/shared/c++/HapticNotificationType.hpp +80 -0
  73. package/nitrogen/generated/shared/c++/HapticParameterType.hpp +76 -0
  74. package/nitrogen/generated/shared/c++/HybridTickleSpec.cpp +34 -0
  75. package/nitrogen/generated/shared/c++/HybridTickleSpec.hpp +87 -0
  76. package/package.json +179 -0
  77. package/react-native.config.js +8 -0
  78. package/src/Tickle.nitro.ts +84 -0
  79. package/src/index.tsx +306 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Renegades
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,352 @@
1
+ # react-native-tickle
2
+
3
+ AHAP-style haptics (transient + continuous) on top of [Nitro Modules](https://nitro.margelo.com/) — the core functions are **UI-thread friendly** (`'worklet'`).
4
+
5
+ ## Why this package?
6
+
7
+ Complete flexibility over iOS haptics with synchronous UI-thread execution.
8
+
9
+ > iOS only (Core Haptics). Android support could be added in the future, but it’s currently unplanned.
10
+
11
+ ## Installation
12
+
13
+ ```sh
14
+ npm i react-native-tickle react-native-nitro-modules
15
+ ```
16
+
17
+ ## Concepts (what to use when)
18
+
19
+ - **Transient**: Instant “click/tap” events. No duration — you trigger them at a point in time.
20
+ - **Continuous (pattern)**: Time-based patterns you _can_ define ahead of time. You provide **events** (with `duration`) and optionally **curves** (automation over time).
21
+ - **Continuous player (real-time)**: For _unpredictable_ input (gesture position, scroll velocity, real-time data). You create a player once, then **start → update (many times) → stop**.
22
+
23
+ ### Why `events[]` and `curves[]` are separate
24
+
25
+ On iOS Core Haptics, a pattern is made of two different building blocks:
26
+
27
+ - **Events**: things that happen (transient “ticks” or continuous segments) at a `relativeTime`, with base `intensity`/`sharpness`.
28
+ - **Curves**: how parameters (intensity/sharpness) evolve over time via control points, independent of “what event” is currently playing.
29
+
30
+ They’re separate because they’re different object types in Core Haptics (events vs parameter curves) and they serve different jobs: **events define the structure**, **curves define the modulation**. You often combine both in one pattern.
31
+
32
+ ## Usage
33
+
34
+ ### Haptic "provider" (recommended)
35
+
36
+ Wrap your app inside `HapticProvider`. This initializes the engine and automatically destroys it when the app goes to background.
37
+
38
+ ```tsx
39
+ import { HapticProvider } from 'react-native-tickle';
40
+
41
+ export function App() {
42
+ return <HapticProvider>{/* {Rest of your app} */}</HapticProvider>;
43
+ }
44
+ ```
45
+
46
+ ### `startHaptic(events, curves)` (transient + continuous patterns)
47
+
48
+ Play a transient:
49
+
50
+ ```ts
51
+ import { startHaptic } from 'react-native-tickle';
52
+
53
+ startHaptic(
54
+ [
55
+ {
56
+ type: 'transient',
57
+ relativeTime: 0,
58
+ parameters: [
59
+ { type: 'intensity', value: 1 },
60
+ { type: 'sharpness', value: 0.5 },
61
+ ],
62
+ },
63
+ ],
64
+ []
65
+ );
66
+ ```
67
+
68
+ Play a continuous pattern (events + curves together):
69
+
70
+ ```ts
71
+ import { startHaptic } from 'react-native-tickle';
72
+
73
+ startHaptic(
74
+ [
75
+ {
76
+ type: 'continuous',
77
+ relativeTime: 0,
78
+ duration: 1200,
79
+ parameters: [
80
+ { type: 'intensity', value: 0.2 },
81
+ { type: 'sharpness', value: 0.5 },
82
+ ],
83
+ },
84
+ ],
85
+ [
86
+ {
87
+ type: 'intensity',
88
+ relativeTime: 0,
89
+ controlPoints: [
90
+ { relativeTime: 0, value: 0.2 },
91
+ { relativeTime: 600, value: 1.0 },
92
+ { relativeTime: 1200, value: 0.2 },
93
+ ],
94
+ },
95
+ ]
96
+ );
97
+ ```
98
+
99
+ Combine transient + continuous in one pattern:
100
+
101
+ ```ts
102
+ import { startHaptic } from 'react-native-tickle';
103
+
104
+ startHaptic(
105
+ [
106
+ {
107
+ type: 'transient',
108
+ relativeTime: 0,
109
+ parameters: [
110
+ { type: 'intensity', value: 1 },
111
+ { type: 'sharpness', value: 0.8 },
112
+ ],
113
+ },
114
+ {
115
+ type: 'continuous',
116
+ relativeTime: 50,
117
+ duration: 800,
118
+ parameters: [
119
+ { type: 'intensity', value: 0.2 },
120
+ { type: 'sharpness', value: 0.4 },
121
+ ],
122
+ },
123
+ ],
124
+ []
125
+ );
126
+ ```
127
+
128
+ ### Real-time continuous mode (continuous player)
129
+
130
+ Use this when you _can't_ predefine a pattern. You start the player, update it in real time, then stop it.
131
+
132
+ **Using the hook (recommended):**
133
+
134
+ ```tsx
135
+ import { useContinuousPlayer } from 'react-native-tickle';
136
+
137
+ function MyComponent() {
138
+ const { start, stop, update } = useContinuousPlayer('my-player', 1.0, 0.5);
139
+
140
+ const gesture = Gesture.Pan()
141
+ .onBegin(() => {
142
+ start();
143
+ })
144
+ .onUpdate((e) => {
145
+ update(e.translationY / 100, 0.5);
146
+ })
147
+ .onEnd(() => {
148
+ stop();
149
+ });
150
+ }
151
+ ```
152
+
153
+ **Manual control:**
154
+
155
+ ```ts
156
+ import {
157
+ createContinuousPlayer,
158
+ startContinuousPlayer,
159
+ updateContinuousPlayer,
160
+ stopContinuousPlayer,
161
+ destroyContinuousPlayer,
162
+ } from 'react-native-tickle';
163
+
164
+ const PLAYER_ID = 'my-player';
165
+
166
+ createContinuousPlayer(PLAYER_ID, 1.0, 0.5);
167
+ startContinuousPlayer(PLAYER_ID);
168
+ updateContinuousPlayer(PLAYER_ID, 0.8, 0.1);
169
+ stopContinuousPlayer(PLAYER_ID);
170
+ destroyContinuousPlayer(PLAYER_ID);
171
+ ```
172
+
173
+ ### Opt out of the provider (manual engine control)
174
+
175
+ If you don’t want the provider behavior:
176
+
177
+ ```ts
178
+ import { initializeEngine, destroyEngine } from 'react-native-tickle';
179
+
180
+ initializeEngine();
181
+ // ...
182
+ destroyEngine();
183
+ ```
184
+
185
+ ### Stop everything (recommended in screen cleanups)
186
+
187
+ Call `stopAllHaptics()` in your cleanup functions to terminate any ongoing continuous haptics. This prevents haptics from bleeding through to the next screen when navigating.
188
+
189
+ ```ts
190
+ import { stopAllHaptics } from 'react-native-tickle';
191
+ import { useEffect } from 'react';
192
+
193
+ export function SomeScreen() {
194
+ useEffect(() => () => stopAllHaptics(), []);
195
+ return null;
196
+ }
197
+ ```
198
+
199
+ ### Global enable/disable (settings toggle)
200
+
201
+ Disable haptics globally for users who prefer no haptic feedback. The setting is **persisted** across app restarts. When disabled, all haptic calls become no-ops.
202
+
203
+ **Using the hook (recommended):**
204
+
205
+ ```tsx
206
+ import { useHapticsEnabled } from 'react-native-tickle';
207
+
208
+ function SettingsScreen() {
209
+ const [hapticsEnabled, setHapticsEnabled] = useHapticsEnabled();
210
+
211
+ return <Switch value={hapticsEnabled} onValueChange={setHapticsEnabled} />;
212
+ }
213
+ ```
214
+
215
+ **Manual control:**
216
+
217
+ ```ts
218
+ import { setHapticsEnabled, getHapticsEnabled } from 'react-native-tickle';
219
+
220
+ const isEnabled = getHapticsEnabled(); // true by default
221
+ setHapticsEnabled(false); // Disable all haptics
222
+ setHapticsEnabled(true); // Re-enable haptics
223
+ ```
224
+
225
+ ### System haptics (predefined OS-level feedback)
226
+
227
+ While the main purpose of this package is to support AHAP-style patterns (transient + continuous haptics with curves), system haptics are also available for completeness. These are simple, predefined OS-level feedback types that don't require pattern definitions. They can be called from the UI thread and don't require the haptic engine to be initialized.
228
+
229
+ ```ts
230
+ import {
231
+ triggerImpact,
232
+ triggerNotification,
233
+ triggerSelection,
234
+ } from 'react-native-tickle';
235
+
236
+ // Impact feedback - simulates a physical collision
237
+ triggerImpact('light'); // 'light' | 'medium' | 'heavy' | 'soft' | 'rigid'
238
+
239
+ // Notification feedback - for alerts and status updates
240
+ triggerNotification('success'); // 'success' | 'warning' | 'error'
241
+
242
+ // Selection feedback - for picker wheels and toggles
243
+ triggerSelection();
244
+ ```
245
+
246
+ ## API
247
+
248
+ | Function | Purpose |
249
+ | ---------------------------------------------------------------------- | --------------------------------------------------------------- |
250
+ | `HapticProvider` | Component that initializes engine; destroys on background |
251
+ | `useHapticEngine()` | Hook to manage engine lifecycle (used internally by provider) |
252
+ | `initializeEngine()` / `destroyEngine()` | Manual engine lifecycle |
253
+ | `startHaptic(events, curves)` | Play a pattern (transient + continuous events, optional curves) |
254
+ | `stopAllHaptics()` | Stop any running haptics (useful on unmount/navigation) |
255
+ | `useHapticsEnabled()` | Hook for reactive haptics enabled state |
256
+ | `setHapticsEnabled(enabled)` / `getHapticsEnabled()` | Manual enable/disable toggle (persisted) |
257
+ | `useContinuousPlayer(playerId, initialIntensity, initialSharpness)` | Hook to manage a continuous player lifecycle |
258
+ | `createContinuousPlayer(playerId, initialIntensity, initialSharpness)` | Create a continuous player with given ID |
259
+ | `startContinuousPlayer(playerId)` / `stopContinuousPlayer(playerId)` | Start/stop continuous playback for player |
260
+ | `updateContinuousPlayer(playerId, intensityControl, sharpnessControl)` | Update intensity/sharpness for player |
261
+ | `destroyContinuousPlayer(playerId)` | Destroy player and release resources |
262
+ | `triggerImpact(style)` | Trigger impact feedback (collision simulation) |
263
+ | `triggerNotification(type)` | Trigger notification feedback (alerts/status) |
264
+ | `triggerSelection()` | Trigger selection feedback (pickers/toggles) |
265
+
266
+ ## Types (inputs)
267
+
268
+ | Type | Values |
269
+ | ------------------------ | ----------------------------------------------------- |
270
+ | `HapticParameterType` | `'intensity' \| 'sharpness'` |
271
+ | `HapticImpactStyle` | `'light' \| 'medium' \| 'heavy' \| 'soft' \| 'rigid'` |
272
+ | `HapticNotificationType` | `'success' \| 'warning' \| 'error'` |
273
+
274
+ | `HapticEventParameter` | Type |
275
+ | ---------------------- | --------------------- |
276
+ | `type` | `HapticParameterType` |
277
+ | `value` | `number` |
278
+
279
+ **`HapticEvent`** (discriminated union)
280
+
281
+ - **Transient:**
282
+
283
+ - `type`: `'transient'`
284
+ - `relativeTime`: `number`
285
+ - `parameters`: `HapticEventParameter[]`
286
+
287
+ - **Continuous:**
288
+ - `type`: `'continuous'`
289
+ - `relativeTime`: `number`
290
+ - `duration`: `number`
291
+ - `parameters`: `HapticEventParameter[]`
292
+
293
+ | `HapticCurveControlPoint` | Type |
294
+ | ------------------------- | -------- |
295
+ | `relativeTime` | `number` |
296
+ | `value` | `number` |
297
+
298
+ | `HapticCurve` | Type |
299
+ | --------------- | --------------------------- |
300
+ | `type` | `HapticCurveType` |
301
+ | `relativeTime` | `number` |
302
+ | `controlPoints` | `HapticCurveControlPoint[]` |
303
+
304
+ ## Limitations
305
+
306
+ ### Parameter curves affect all events in a pattern
307
+
308
+ In CoreHaptics, `CHHapticParameterCurve` uses `hapticIntensityControl` and `hapticSharpnessControl` — these are **pattern-level multipliers**, not per-event modifiers. Any curve you define will multiply the intensity/sharpness of **all events** playing at that moment, including transients.
309
+
310
+ **Example problem:**
311
+
312
+ ```ts
313
+ startHaptic(
314
+ [
315
+ { type: 'continuous', relativeTime: 0, duration: 2000, parameters: [...] },
316
+ { type: 'transient', relativeTime: 1000, parameters: [{ type: 'intensity', value: 1.0 }, ...] },
317
+ ],
318
+ [
319
+ { type: 'intensity', relativeTime: 0, controlPoints: [
320
+ { relativeTime: 0, value: 1.0 },
321
+ { relativeTime: 1000, value: 0.3 }, // At t=1000ms, intensity control = 0.3
322
+ { relativeTime: 2000, value: 0.3 },
323
+ ]},
324
+ ]
325
+ );
326
+ ```
327
+
328
+ The transient at `t=1000ms` has base intensity `1.0`, but the curve sets `intensityControl=0.3` at that moment. **Effective intensity: 1.0 × 0.3 = 0.3**. The transient feels weaker than expected.
329
+
330
+ **Workaround:** Play continuous and transient events in separate `startHaptic()` calls:
331
+
332
+ ```ts
333
+ // Continuous with curves
334
+ startHaptic(continuousEvents, curves);
335
+
336
+ // Transients without curves (separate pattern)
337
+ startHaptic(transientEvents, []);
338
+ ```
339
+
340
+ Each call creates an isolated pattern/player — curves from one won't affect events in the other.
341
+
342
+ > **Note:** The library automatically resets control values to `1.0` at the end of each continuous event, so transients **after** a continuous event finishes are not affected. This limitation only applies to transients **during** a continuous event with active curves.
343
+
344
+ ## Contributing
345
+
346
+ - [Development workflow](CONTRIBUTING.md#development-workflow)
347
+ - [Sending a pull request](CONTRIBUTING.md#sending-a-pull-request)
348
+ - [Code of conduct](CODE_OF_CONDUCT.md)
349
+
350
+ ## License
351
+
352
+ MIT
package/Tickle.podspec ADDED
@@ -0,0 +1,29 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "Tickle"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://google.com.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = [
17
+ "ios/**/*.{swift}",
18
+ "ios/**/*.{m,mm}",
19
+ "cpp/**/*.{hpp,cpp}",
20
+ ]
21
+
22
+ s.dependency 'React-jsi'
23
+ s.dependency 'React-callinvoker'
24
+
25
+ load 'nitrogen/generated/ios/Tickle+autolinking.rb'
26
+ add_nitrogen_files(s)
27
+
28
+ install_modules_dependencies(s)
29
+ end
@@ -0,0 +1,24 @@
1
+ project(tickle)
2
+ cmake_minimum_required(VERSION 3.9.0)
3
+
4
+ set(PACKAGE_NAME tickle)
5
+ set(CMAKE_VERBOSE_MAKEFILE ON)
6
+ set(CMAKE_CXX_STANDARD 20)
7
+
8
+ # Define C++ library and add all sources
9
+ add_library(${PACKAGE_NAME} SHARED src/main/cpp/cpp-adapter.cpp)
10
+
11
+ # Add Nitrogen specs :)
12
+ include(${CMAKE_SOURCE_DIR}/../nitrogen/generated/android/tickle+autolinking.cmake)
13
+
14
+ # Set up local includes
15
+ include_directories("src/main/cpp" "../cpp")
16
+
17
+ find_library(LOG_LIB log)
18
+
19
+ # Link all libraries together
20
+ target_link_libraries(
21
+ ${PACKAGE_NAME}
22
+ ${LOG_LIB}
23
+ android # <-- Android core
24
+ )
@@ -0,0 +1,126 @@
1
+ buildscript {
2
+ ext.getExtOrDefault = {name ->
3
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['Tickle_' + name]
4
+ }
5
+
6
+ repositories {
7
+ google()
8
+ mavenCentral()
9
+ }
10
+
11
+ dependencies {
12
+ classpath "com.android.tools.build:gradle:8.7.2"
13
+ // noinspection DifferentKotlinGradleVersion
14
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}"
15
+ }
16
+ }
17
+
18
+ def reactNativeArchitectures() {
19
+ def value = rootProject.getProperties().get("reactNativeArchitectures")
20
+ return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
21
+ }
22
+
23
+ apply plugin: "com.android.library"
24
+ apply plugin: "kotlin-android"
25
+ apply from: '../nitrogen/generated/android/tickle+autolinking.gradle'
26
+
27
+ def getExtOrIntegerDefault(name) {
28
+ return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Tickle_" + name]).toInteger()
29
+ }
30
+
31
+ android {
32
+ namespace "com.margelo.nitro.tickle"
33
+
34
+ compileSdkVersion getExtOrIntegerDefault("compileSdkVersion")
35
+
36
+ defaultConfig {
37
+ minSdkVersion getExtOrIntegerDefault("minSdkVersion")
38
+ targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
39
+
40
+ externalNativeBuild {
41
+ cmake {
42
+ cppFlags "-frtti -fexceptions -Wall -fstack-protector-all"
43
+ arguments "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON"
44
+ abiFilters (*reactNativeArchitectures())
45
+
46
+ buildTypes {
47
+ debug {
48
+ cppFlags "-O1 -g"
49
+ }
50
+ release {
51
+ cppFlags "-O2"
52
+ }
53
+ }
54
+ }
55
+ }
56
+ }
57
+
58
+ externalNativeBuild {
59
+ cmake {
60
+ path "CMakeLists.txt"
61
+ }
62
+ }
63
+
64
+ packagingOptions {
65
+ excludes = [
66
+ "META-INF",
67
+ "META-INF/**",
68
+ "**/libc++_shared.so",
69
+ "**/libfbjni.so",
70
+ "**/libjsi.so",
71
+ "**/libfolly_json.so",
72
+ "**/libfolly_runtime.so",
73
+ "**/libglog.so",
74
+ "**/libhermes.so",
75
+ "**/libhermes-executor-debug.so",
76
+ "**/libhermes_executor.so",
77
+ "**/libreactnative.so",
78
+ "**/libreactnativejni.so",
79
+ "**/libturbomodulejsijni.so",
80
+ "**/libreact_nativemodule_core.so",
81
+ "**/libjscexecutor.so"
82
+ ]
83
+ }
84
+
85
+ buildFeatures {
86
+ buildConfig true
87
+ prefab true
88
+ }
89
+
90
+ buildTypes {
91
+ release {
92
+ minifyEnabled false
93
+ }
94
+ }
95
+
96
+ lintOptions {
97
+ disable "GradleCompatible"
98
+ }
99
+
100
+ compileOptions {
101
+ sourceCompatibility JavaVersion.VERSION_1_8
102
+ targetCompatibility JavaVersion.VERSION_1_8
103
+ }
104
+
105
+ sourceSets {
106
+ main {
107
+ java.srcDirs += [
108
+ "generated/java",
109
+ "generated/jni"
110
+ ]
111
+ }
112
+ }
113
+ }
114
+
115
+ repositories {
116
+ mavenCentral()
117
+ google()
118
+ }
119
+
120
+ def kotlin_version = getExtOrDefault("kotlinVersion")
121
+
122
+ dependencies {
123
+ implementation "com.facebook.react:react-android"
124
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
125
+ implementation project(":react-native-nitro-modules")
126
+ }
@@ -0,0 +1,5 @@
1
+ Tickle_kotlinVersion=2.0.21
2
+ Tickle_minSdkVersion=24
3
+ Tickle_targetSdkVersion=34
4
+ Tickle_compileSdkVersion=35
5
+ Tickle_ndkVersion=27.1.12297006
@@ -0,0 +1,2 @@
1
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2
+ </manifest>
@@ -0,0 +1,6 @@
1
+ #include <jni.h>
2
+ #include "tickleOnLoad.hpp"
3
+
4
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
5
+ return margelo::nitro::tickle::initialize(vm);
6
+ }
@@ -0,0 +1,71 @@
1
+ package com.margelo.nitro.tickle
2
+
3
+ import com.facebook.proguard.annotations.DoNotStrip
4
+
5
+ @DoNotStrip
6
+ class Tickle : HybridTickleSpec() {
7
+ override val memorySize: Long
8
+ get() = 0L
9
+
10
+ private var hapticsEnabled: Boolean = true
11
+
12
+ override fun startHaptic(events: Array<HapticEvent>, curves: Array<HapticCurve>) {
13
+ // TODO: Android haptics implementation not yet supported
14
+ }
15
+
16
+ override fun stopAllHaptics() {
17
+ // TODO: Android haptics implementation not yet supported
18
+ }
19
+
20
+ override fun initializeEngine() {
21
+ // TODO: Android haptics implementation not yet supported
22
+ }
23
+
24
+ override fun destroyEngine() {
25
+ // TODO: Android haptics implementation not yet supported
26
+ }
27
+
28
+ override fun createContinuousPlayer(playerId: String, initialIntensity: Double, initialSharpness: Double) {
29
+ // TODO: Android haptics implementation not yet supported
30
+ }
31
+
32
+ override fun startContinuousPlayer(playerId: String) {
33
+ // TODO: Android haptics implementation not yet supported
34
+ }
35
+
36
+ override fun updateContinuousPlayer(playerId: String, intensityControl: Double, sharpnessControl: Double) {
37
+ // TODO: Android haptics implementation not yet supported
38
+ }
39
+
40
+ override fun stopContinuousPlayer(playerId: String) {
41
+ // TODO: Android haptics implementation not yet supported
42
+ }
43
+
44
+ override fun destroyContinuousPlayer(playerId: String) {
45
+ // TODO: Android haptics implementation not yet supported
46
+ }
47
+
48
+ // MARK: - Global Haptics Enable/Disable
49
+
50
+ override fun setHapticsEnabled(enabled: Boolean) {
51
+ hapticsEnabled = enabled
52
+ }
53
+
54
+ override fun getHapticsEnabled(): Boolean {
55
+ return hapticsEnabled
56
+ }
57
+
58
+ // MARK: - System Haptics (Predefined OS-level feedback)
59
+
60
+ override fun triggerImpact(style: HapticImpactStyle) {
61
+ // TODO: Android haptics implementation not yet supported
62
+ }
63
+
64
+ override fun triggerNotification(type: HapticNotificationType) {
65
+ // TODO: Android haptics implementation not yet supported
66
+ }
67
+
68
+ override fun triggerSelection() {
69
+ // TODO: Android haptics implementation not yet supported
70
+ }
71
+ }
@@ -0,0 +1,22 @@
1
+ package com.margelo.nitro.tickle
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.bridge.NativeModule
5
+ import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.module.model.ReactModuleInfoProvider
7
+
8
+ class TicklePackage : BaseReactPackage() {
9
+ override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
10
+ return null
11
+ }
12
+
13
+ override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
14
+ return ReactModuleInfoProvider { HashMap() }
15
+ }
16
+
17
+ companion object {
18
+ init {
19
+ System.loadLibrary("tickle")
20
+ }
21
+ }
22
+ }