diff --git a/CHANGELOG.md b/CHANGELOG.md index f27af9a..6e29715 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +### 1.3.0 +- AND: Updated to FreKotlin 1.9.5 +- ANE: `vibrator.android::vibrate()` now takes `VibrationEffect` param +- iOS: Updated to FreSwift 4.3.0 +- iOS: Add HapticEngine support for iOS 13.0+ + ### 1.2.0 - AND: Updated to FreKotlin 1.8.0 - Updated to AIR 33 ARM 64bit diff --git a/README.md b/README.md index 3a50b65..dc19ae2 100644 --- a/README.md +++ b/README.md @@ -63,11 +63,11 @@ You should use AIR 32 for iOS builds You will need: -- IntelliJ IDEA / Flash Builder -- AIR 32 or greater +- IntelliJ IDEA +- AIR 33.0.2.338+ +- Xcode 11.3 +- wget on macOS via `brew install wget` - Android Studio 3 if you wish to edit the Android source -- Xcode 10.1 -- wget on macOS - Powershell on Windows ### References diff --git a/example/.actionScriptProperties b/example/.actionScriptProperties deleted file mode 100644 index fc24c58..0000000 --- a/example/.actionScriptProperties +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/.idea/libraries/com_tuarua_frekotlin_1_8_0.xml b/example/.idea/libraries/com_tuarua_frekotlin_1_9_5.xml similarity index 67% rename from example/.idea/libraries/com_tuarua_frekotlin_1_8_0.xml rename to example/.idea/libraries/com_tuarua_frekotlin_1_9_5.xml index 1120a3d..dc3fad8 100644 --- a/example/.idea/libraries/com_tuarua_frekotlin_1_8_0.xml +++ b/example/.idea/libraries/com_tuarua_frekotlin_1_9_5.xml @@ -1,7 +1,7 @@ - + - + diff --git a/example/.project b/example/.project deleted file mode 100644 index 8f35c63..0000000 --- a/example/.project +++ /dev/null @@ -1,25 +0,0 @@ - - - VibrationANESample - - - - - - com.adobe.flexbuilder.project.flexbuilder - - - - - com.adobe.flexbuilder.project.apollobuilder - - - - - - com.adobe.flexide.project.multiplatform.multiplatformasnature - com.adobe.flexide.project.multiplatform.multiplatformnature - com.adobe.flexbuilder.project.apollonature - com.adobe.flexbuilder.project.actionscriptnature - - diff --git a/example/VibrationANESample.iml b/example/VibrationANESample.iml index c942f57..e1a70e2 100644 --- a/example/VibrationANESample.iml +++ b/example/VibrationANESample.iml @@ -11,7 +11,7 @@ - + @@ -46,7 +46,7 @@ - + @@ -60,8 +60,9 @@ + - + @@ -74,7 +75,7 @@ - + @@ -88,8 +89,9 @@ + - + @@ -100,11 +102,12 @@ - + + \ No newline at end of file diff --git a/example/assets/AHAP/Boing.ahap b/example/assets/AHAP/Boing.ahap new file mode 100644 index 0000000..806d24b --- /dev/null +++ b/example/assets/AHAP/Boing.ahap @@ -0,0 +1,62 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "A continuous event tweaked by sloped parameter curves, emphasized by a precisely placed transient event, creating the feeling of a spring or rubberband." + }, + "Pattern": + [ + { + "Event": + { + "Time": 0.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.4 } + ] + } + }, + { + "Event": + { + "Time": 0.015, + "EventType": "HapticContinuous", + "EventDuration": 0.25, + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.4 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticIntensityControl", + "Time": 0.015, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": 1 }, + { "Time": 0.1, "ParameterValue": 0.5 }, + { "Time": 0.25, "ParameterValue": 0.0 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticSharpnessControl", + "Time": 0.015, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": 0.0 }, + { "Time": 0.25, "ParameterValue": -0.3 } + ] + } + } + ] +} \ No newline at end of file diff --git a/example/assets/AHAP/Drums.ahap b/example/assets/AHAP/Drums.ahap new file mode 100755 index 0000000..bfec19a --- /dev/null +++ b/example/assets/AHAP/Drums.ahap @@ -0,0 +1,129 @@ +{ + "Version": 1, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "A sequence of haptic events paired with a custom audio file." + }, + "Pattern": + [ + {"Event": + {"Time":0,"EventType":"HapticTransient", + "EventParameters": + [ + {"ParameterID":"HapticSharpness","ParameterValue":0} + ] + } + }, + {"Event": + {"Time":0.391,"EventType":"HapticTransient", + "EventParameters": + [ + {"ParameterID":"HapticSharpness","ParameterValue":0}, + {"ParameterID":"HapticIntensity","ParameterValue":1} + ] + } + }, + {"Event": + {"Time":0.8,"EventType":"HapticTransient", + "EventParameters": + [ + {"ParameterID":"HapticSharpness","ParameterValue":0.3}, + {"ParameterID":"HapticIntensity","ParameterValue":1} + ] + } + }, + {"Event": + {"Time":1.2,"EventType":"HapticTransient", + "EventParameters": + [ + {"ParameterID":"HapticSharpness","ParameterValue":0}, + {"ParameterID":"HapticIntensity","ParameterValue":1} + ] + } + }, + {"Event": + {"Time":1.41,"EventType":"HapticTransient", + "EventParameters": + [ + {"ParameterID":"HapticSharpness","ParameterValue":0}, + {"ParameterID":"HapticIntensity","ParameterValue":1} + ] + } + }, + {"Event": + {"Time":1.6,"EventType":"HapticTransient", + "EventParameters": + [ + {"ParameterID":"HapticSharpness","ParameterValue":0} + ] + } + }, + {"Event": + {"Time":0,"EventType":"HapticContinuous","EventDuration":0.2, + "EventParameters": + [ + {"ParameterID":"HapticIntensity","ParameterValue":0.6} + ] + } + }, + {"Event": + {"Time":0.2,"EventType":"HapticContinuous","EventDuration":0.2, + "EventParameters": + [ + {"ParameterID":"HapticIntensity","ParameterValue":0.3} + ] + } + }, + {"Event": + {"Time":0.4,"EventType":"HapticContinuous","EventDuration":0.15, + "EventParameters": + [ + {"ParameterID":"HapticIntensity","ParameterValue":0.25}, + {"ParameterID":"HapticSharpness","ParameterValue":1.0} + ] + } + }, + {"Event": + {"Time":0.8,"EventType":"HapticContinuous","EventDuration":0.15, + "EventParameters": + [ + {"ParameterID":"HapticIntensity","ParameterValue":0.25}, + {"ParameterID":"HapticSharpness","ParameterValue":1.0} + ] + } + }, + {"Event": + {"Time":1.2,"EventType":"HapticContinuous","EventDuration":0.15, + "EventParameters": + [ + {"ParameterID":"HapticIntensity","ParameterValue":0.25}, + {"ParameterID":"HapticSharpness","ParameterValue":1.0} + ] + } + }, + {"Event": + {"Time":1.6,"EventType":"HapticContinuous","EventDuration":1, + "EventParameters": + [ + {"ParameterID":"HapticIntensity","ParameterValue":0.45}, + {"ParameterID":"HapticSharpness","ParameterValue":0.9}, + {"ParameterID":"DecayTime","ParameterValue":1}, + {"ParameterID":"Sustained","ParameterValue":0} + ] + } + }, + {"Event": + { + "Time":0, + "EventType":"AudioCustom", + "EventWaveformPath":"AHAP/Drums.wav", + "EventParameters": + [ + {"ParameterID":"AudioVolume","ParameterValue":0.75} + ] + } + } + ] +} diff --git a/example/assets/AHAP/Drums.wav b/example/assets/AHAP/Drums.wav new file mode 100755 index 0000000..0c82f2a Binary files /dev/null and b/example/assets/AHAP/Drums.wav differ diff --git a/example/assets/AHAP/Gravel.ahap b/example/assets/AHAP/Gravel.ahap new file mode 100644 index 0000000..38dfbe6 --- /dev/null +++ b/example/assets/AHAP/Gravel.ahap @@ -0,0 +1,2566 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "A sequence of tightly spaced transient events and parameter variations to create a gravel-like texture." + }, + "Pattern": + [ + { + "ParameterCurve": { + "ParameterID": "HapticIntensityControl", + "Time": 0.0, + "ParameterCurveControlPoints": + [ + { + "Time": 0, + "ParameterValue": 0.3 + }, + { + "Time": 1.0, + "ParameterValue": 0.6 + }, + { + "Time": 3.0, + "ParameterValue": 0.0 + } + ] + } + }, + { + "Event": { + "Time": 0.04, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.05, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.07, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.09, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.11, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.14, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.16, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.17, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.2, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.21, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.24, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.25, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.28, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.29, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.31, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.33, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.35, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.38, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.39, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.42, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.43, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.46, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.48, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.5, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.52, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.53, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.56, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.57, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.6, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.62, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.64, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.65, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.67, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.69, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.72, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.74, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.76, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.78, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.8, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.81, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.84, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.85, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.88, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.89, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 0.92, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.94, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.96, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 0.98, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.01, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.04, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.06, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.07, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.09, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.11, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.14, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.15, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.18, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.19, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.21, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.24, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.26, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.27, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.3, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.31, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.34, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.35, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.38, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.4, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.41, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.43, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.46, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.48, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.49, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.51, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.54, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.56, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.57, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.59, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.61, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.64, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.66, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.68, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.69, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.72, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.73, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.76, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.77, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.79, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.81, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.84, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.85, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.87, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.89, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.91, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.94, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 1.95, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 1.97, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.02, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.03, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.05, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.08, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.09, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.11, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.13, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.15, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.18, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.19, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.21, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.23, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.25, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.28, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.29, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.32, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.33, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.36, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.38, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.4, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.42, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.43, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.46, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.48, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.5, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.51, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.54, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.55, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.58, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.59, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.61, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.63, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.65, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.67, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.7, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.72, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.74, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.75, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.77, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.8, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.82, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.83, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.85, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.88, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.89, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.91, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.94, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 2.95, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 1.0 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.5 + } + ] + } + }, + { + "Event": { + "Time": 2.98, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + }, + { + "Event": { + "Time": 3.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { + "ParameterID": "HapticIntensity", + "ParameterValue": 0.7 + }, + { + "ParameterID": "HapticSharpness", + "ParameterValue": 0.3 + } + ] + } + } + ] +} \ No newline at end of file diff --git a/example/assets/AHAP/Heartbeats.ahap b/example/assets/AHAP/Heartbeats.ahap new file mode 100644 index 0000000..447914c --- /dev/null +++ b/example/assets/AHAP/Heartbeats.ahap @@ -0,0 +1,156 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "Three organic heartbeats over three seconds, made using precisely spaced transient events at varying parameters." + }, + "Pattern": + [ + { + "Event": + { + "Time": 0.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.2 } + ] + } + }, + { + "Event": + { + "Time": 0.013, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.3 } + ] + } + }, + { + "Event": + { + "Time": 0.220, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.255, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.7 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.0 } + ] + } + }, + { + "Event": + { + "Time": 1.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.2 } + ] + } + }, + { + "Event": + { + "Time": 1.013, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.3 } + ] + } + }, + { + "Event": + { + "Time": 1.220, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.255, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.7 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.0 } + ] + } + }, + { + "Event": + { + "Time": 2.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.2 } + ] + } + }, + { + "Event": + { + "Time": 2.013, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.3 } + ] + } + }, + { + "Event": + { + "Time": 2.220, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 2.255, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.7 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.0 } + ] + } + } + ] +} diff --git a/example/assets/AHAP/Inflate.ahap b/example/assets/AHAP/Inflate.ahap new file mode 100644 index 0000000..d14b78a --- /dev/null +++ b/example/assets/AHAP/Inflate.ahap @@ -0,0 +1,50 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "An effect that builds in sharpness and intensity." + }, + "Pattern": + [ + { + "Event": + { + "Time": 0.0, + "EventType": "HapticContinuous", + "EventDuration": 1.7, + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticIntensityControl", + "Time": 0.0, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": 0.0 }, + { "Time": 1.1, "ParameterValue": 0.5 }, + { "Time": 1.7, "ParameterValue": 0.0 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticSharpnessControl", + "Time": 0.0, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": -0.8 }, + { "Time": 1.7, "ParameterValue": 0.8 } + ] + } + } + ] +} diff --git a/example/assets/AHAP/Oscillate.ahap b/example/assets/AHAP/Oscillate.ahap new file mode 100644 index 0000000..03bc882 --- /dev/null +++ b/example/assets/AHAP/Oscillate.ahap @@ -0,0 +1,51 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "A mixture of two continuous events, shifting the sharpness of one to create a smooth oscillating feel." + }, + "Pattern": + [ + { + "Event": + { + "Time": 0.0, + "EventType": "HapticContinuous", + "EventDuration": 3.0, + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.5 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.0 } + ] + } + }, + { + "Event": + { + "Time": 0.0, + "EventType": "HapticContinuous", + "EventDuration": 3.0, + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.5 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.075 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticIntensityControl", + "Time": 0.0, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": 1.0 }, + { "Time": 0.5, "ParameterValue": 1.0 }, + { "Time": 3.0, "ParameterValue": 0.0 } + ] + } + } + ] +} diff --git a/example/assets/AHAP/Rumble.ahap b/example/assets/AHAP/Rumble.ahap new file mode 100644 index 0000000..3d8d856 --- /dev/null +++ b/example/assets/AHAP/Rumble.ahap @@ -0,0 +1,494 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "A sequence of transient events with decreasing sharpness, to create a precise rumble, reminiscent of driving over uneven ground." + }, + "Pattern": + [ + { + "ParameterCurve": + { + "ParameterID": "HapticIntensityControl", + "Time": 0.0, + "ParameterCurveControlPoints": + [ + { "Time": 0.0, "ParameterValue": 0.3 }, + { "Time": 0.1, "ParameterValue": 1.0 }, + { "Time": 1.0, "ParameterValue": 1.0 }, + { "Time": 1.6, "ParameterValue": 0.0 } + ] + } + }, + { + "Event": + { + "Time": 0.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.04, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.08, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.12, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.16, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.2, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.24, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.28, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.32, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.36, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.40, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.44, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.48, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.52, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.56, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.6, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.64, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.68, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.72, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.76, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.8, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.84, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.88, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.92, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 0.96, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.04, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.08, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.12, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.16, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.2, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.24, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.28, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.32, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.36, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.40, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.44, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.48, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "Event": + { + "Time": 1.52, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + } + ] +} diff --git a/example/assets/AHAP/Sparkle.ahap b/example/assets/AHAP/Sparkle.ahap new file mode 100644 index 0000000..a4987d4 --- /dev/null +++ b/example/assets/AHAP/Sparkle.ahap @@ -0,0 +1,304 @@ +{ + "Version": 1.0, + "Metadata": + { + "Project" : "Haptic Sampler", + "Created" : "5 June 2019", + "Description" : "A combination of transient and continuous events to create a pop with trailing sparkles." + }, + "Pattern": + [ + { + "Event": + { + "Time": 0.0, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.6 } + ] + } + }, + { + "Event": + { + "Time": 0.024, + "EventType": "HapticContinuous", + "EventDuration": 0.150, + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.6 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.1 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticIntensityControl", + "Time": 0.024, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": 1.0 }, + { "Time": 0.025, "ParameterValue": 0.45 }, + { "Time": 0.15, "ParameterValue": 0.0 } + ] + } + }, + { + "ParameterCurve": + { + "ParameterID": "HapticIntensityControl", + "Time": 0.174, + "ParameterCurveControlPoints": + [ + { "Time": 0, "ParameterValue": 0.75 }, + { "Time": 0.087, "ParameterValue": 0.40 }, + { "Time": 0.667, "ParameterValue": 0.40 }, + { "Time": 0.846, "ParameterValue": 0.2 } + ] + } + }, + { + "Event": + { + "Time": 0.174, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.4 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 } + ] + } + }, + { + "Event": + { + "Time": 0.188, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.6 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 } + ] + } + }, + { + "Event": + { + "Time": 0.203, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.7 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.231, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.5 } + ] + } + }, + { + "Event": + { + "Time": 0.261, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.278, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.9 } + ] + } + }, + { + "Event": + { + "Time": 0.318, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.336, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.9 } + ] + } + }, + { + "Event": + { + "Time": 0.368, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.6 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.425, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.473, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.6 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.517, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.531, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.573, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.6 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.595, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.647, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 1.0 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.678, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.751, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 0.841, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 0.8 } + ] + } + }, + { + "Event": + { + "Time": 1.020, + "EventType": "HapticTransient", + "EventParameters": + [ + { "ParameterID": "HapticIntensity", "ParameterValue": 0.8 }, + { "ParameterID": "HapticSharpness", "ParameterValue": 1.0 } + ] + } + } + ] +} diff --git a/example/get_android_dependencies.ps1 b/example/get_android_dependencies.ps1 index ff6205f..b4694e0 100644 --- a/example/get_android_dependencies.ps1 +++ b/example/get_android_dependencies.ps1 @@ -1,5 +1,5 @@ -$AneVersion = "1.2.0" -$FreKotlinVersion = "1.8.0" +$AneVersion = "1.3.0" +$FreKotlinVersion = "1.9.5" $currentDir = (Get-Item -Path ".\" -Verbose).FullName [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 diff --git a/example/get_android_dependencies.sh b/example/get_android_dependencies.sh index 6570865..7a2dffe 100644 --- a/example/get_android_dependencies.sh +++ b/example/get_android_dependencies.sh @@ -1,7 +1,7 @@ #!/bin/sh -AneVersion="1.2.0" -FreKotlinVersion="1.8.0" +AneVersion="1.3.0" +FreKotlinVersion="1.9.5" wget -O android_dependencies/com.tuarua.frekotlin-$FreKotlinVersion.ane https://github.com/tuarua/Android-ANE-Dependencies/blob/master/anes/kotlin/com.tuarua.frekotlin-$FreKotlinVersion.ane?raw=true wget -O ../native_extension/ane/VibrationANE.ane https://github.com/tuarua/Vibration-ANE/releases/download/$AneVersion/VibrationANE.ane?raw=true diff --git a/example/get_ios_dependencies.sh b/example/get_ios_dependencies.sh index 60eee57..eb9797d 100755 --- a/example/get_ios_dependencies.sh +++ b/example/get_ios_dependencies.sh @@ -1,7 +1,7 @@ #!/bin/sh -AneVersion="1.2.0" -FreSwiftVersion="3.1.0" +AneVersion="1.3.0" +FreSwiftVersion="4.3.0" rm -r ios_dependencies/device rm -r ios_dependencies/simulator diff --git a/example/src/Main-app-android.xml b/example/src/Main-app-android.xml index 134e834..0444309 100644 --- a/example/src/Main-app-android.xml +++ b/example/src/Main-app-android.xml @@ -29,11 +29,20 @@ - - - - + + + + + + + + + + + + + + ]]> diff --git a/example/src/StarlingRoot.as b/example/src/StarlingRoot.as index 1d5b612..3405a17 100644 --- a/example/src/StarlingRoot.as +++ b/example/src/StarlingRoot.as @@ -1,11 +1,16 @@ package { -import com.tuarua.VibrationANE; +import com.tuarua.Vibrator; import com.tuarua.android; +import com.tuarua.fre.ANEError; import com.tuarua.ios; import com.tuarua.utils.os; +import com.tuarua.vibration.android.OneShot; +import com.tuarua.vibration.android.Waveform; +import com.tuarua.vibration.ios.HapticEngine; import com.tuarua.vibration.ios.NotificationFeedbackGenerator; import com.tuarua.vibration.ios.NotificationFeedbackType; +import com.tuarua.vibration.ios.StoppedReason; import com.tuarua.vibration.ios.SystemSoundID; import flash.desktop.NativeApplication; @@ -25,7 +30,9 @@ public class StarlingRoot extends Sprite { private var btnRepeat:SimpleButton = new SimpleButton("Repeat Vibrate"); private var btnCancel:SimpleButton = new SimpleButton("Cancel Vibrate"); private var btnTaptic:SimpleButton = new SimpleButton("Notification Taptic"); - private var vibrator:VibrationANE; + private var btnHapticEngine:SimpleButton = new SimpleButton("Haptic Engine"); + private var vibrator:Vibrator; + private var hapticEngine:HapticEngine; public function StarlingRoot() { TextField.registerCompositor(Fonts.getFont("fira-sans-semi-bold-13"), "Fira Sans Semi-Bold 13"); @@ -33,10 +40,11 @@ public class StarlingRoot extends Sprite { } public function start():void { - vibrator = VibrationANE.vibrator; - trace("vibrator.hasVibrator", vibrator.hasVibrator); - trace("vibrator.hasTapticEngine", vibrator.hasTapticEngine); - trace("vibrator.hasHapticFeedback", vibrator.hasHapticFeedback); + vibrator = Vibrator.shared(); + trace("vibrator.hasVibrator:", vibrator.hasVibrator); + trace("vibrator.hasTapticEngine:", vibrator.hasTapticEngine); + trace("vibrator.hasHapticFeedback:", vibrator.hasHapticFeedback); + trace("HapticEngine.supportsHaptics:", HapticEngine.supportsHaptics); if (vibrator.hasVibrator) { initMenu(); } @@ -66,6 +74,41 @@ public class StarlingRoot extends Sprite { btnTaptic.y = 180; btnTaptic.addEventListener(TouchEvent.TOUCH, onTapticClick); addChild(btnTaptic); + + if (HapticEngine.supportsHaptics) { + try { + hapticEngine = new HapticEngine(); + } catch (e:ANEError) { + trace(e.message); + return; + } + hapticEngine.stoppedHandler = function (reason:int):void { + switch (reason) { + case StoppedReason.applicationSuspended: + break; + case StoppedReason.audioSessionInterrupt: + break; + case StoppedReason.idleTimeout: + break; + case StoppedReason.notifyWhenFinished: + break; + case StoppedReason.systemError: + break; + } + }; + hapticEngine.resetHandler = function ():void { + try { + trace("The engine reset --> Restarting now!"); + hapticEngine.start(); + } catch (e:ANEError) { + trace(e.message); + } + }; + btnHapticEngine.y = 260; + btnHapticEngine.addEventListener(TouchEvent.TOUCH, onHapticEngineClick); + addChild(btnHapticEngine); + } + } } @@ -83,7 +126,7 @@ public class StarlingRoot extends Sprite { var touch:Touch = event.getTouch(btnSimple); if (touch != null && touch.phase == TouchPhase.ENDED) { if (os.isAndroid) { - vibrator.android::vibrate(150); + vibrator.android::vibrate(new OneShot(150)); } else if (os.isIos) { vibrator.ios::vibrate(vibrator.hasTapticEngine ? SystemSoundID.POP : SystemSoundID.DEFAULT); } @@ -93,7 +136,7 @@ public class StarlingRoot extends Sprite { private function onMultiClick(event:TouchEvent):void { var touch:Touch = event.getTouch(btnMulti); if (touch != null && touch.phase == TouchPhase.ENDED) { - vibrator.android::vibrate(0, [0, 100, 1000, 300]); + vibrator.android::vibrate(new Waveform([0, 100, 1000, 300])); } } @@ -101,7 +144,7 @@ public class StarlingRoot extends Sprite { var touch:Touch = event.getTouch(btnRepeat); if (touch != null && touch.phase == TouchPhase.ENDED) { btnCancel.visible = true; - vibrator.android::vibrate(0, [0, 100, 2000, 500], 0); + vibrator.android::vibrate(new Waveform([0, 100, 1000, 300], null, 0)); } } @@ -113,8 +156,20 @@ public class StarlingRoot extends Sprite { } } + private function onHapticEngineClick(event:TouchEvent):void { + var touch:Touch = event.getTouch(btnHapticEngine); + if (touch != null && touch.phase == TouchPhase.ENDED) { + try { + hapticEngine.start(); + hapticEngine.playPattern("AHAP/Heartbeats"); + } catch (e:ANEError) { + trace(e.message); + } + } + } + private function onExiting(event:Event):void { - VibrationANE.dispose(); + Vibrator.dispose(); } } diff --git a/native_extension/.actionScriptProperties b/native_extension/.actionScriptProperties deleted file mode 100644 index a9cb147..0000000 --- a/native_extension/.actionScriptProperties +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/native_extension/.flexLibProperties b/native_extension/.flexLibProperties deleted file mode 100644 index 4c03086..0000000 --- a/native_extension/.flexLibProperties +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/native_extension/.idea/codeStyles/codeStyleConfig.xml b/native_extension/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/native_extension/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/native_extension/.project b/native_extension/.project deleted file mode 100644 index a53fc09..0000000 --- a/native_extension/.project +++ /dev/null @@ -1,18 +0,0 @@ - - - VibrationANE - - - - - - com.adobe.flexbuilder.project.flexbuilder - - - - - - com.adobe.flexbuilder.project.aslibnature - com.adobe.flexbuilder.project.actionscriptnature - - diff --git a/native_extension/ane/build_mobile.sh b/native_extension/ane/build.sh similarity index 97% rename from native_extension/ane/build_mobile.sh rename to native_extension/ane/build.sh index ee12ae0..9e6b833 100755 --- a/native_extension/ane/build_mobile.sh +++ b/native_extension/ane/build.sh @@ -1,7 +1,5 @@ #!/bin/sh -#Get the path to the script and trim to get the directory. -echo "Setting path to current directory to:" pathtome=$0 pathtome="${pathtome%/*}" @@ -127,7 +125,7 @@ mv "$pathtome/platforms/android/res" "$pathtome/platforms/android/com.tuarua.$PR #Run the build command. echo "Building ANE." "$AIR_SDK"/bin/adt -package \ --target ane "$pathtome/$PROJECTNAME.ane" "$pathtome/extension_mobile.xml" \ +-target ane "$pathtome/$PROJECTNAME.ane" "$pathtome/extension.xml" \ -swc "$pathtome/$PROJECTNAME.swc" \ -platform Android-x86 \ -C "$pathtome/platforms/android" "library.swf" "classes.jar" \ diff --git a/native_extension/ane/docs/com.tuarua.vibration.ios.xml b/native_extension/ane/docs/com.tuarua.vibration.ios.xml index 6bc662c..4b1d602 100644 --- a/native_extension/ane/docs/com.tuarua.vibration.ios.xml +++ b/native_extension/ane/docs/com.tuarua.vibration.ios.xml @@ -2,11 +2,59 @@ any feedback generated safe to call more than once before the generator receives an event, if events are still imminently possible Informs self that it will likely receive events soon, so that it can ensure minimal latency for any feedback generated safe to call more than once before the generator receives an event, - if events are still imminently possible release Release the SelectionFeedbackGenerator. Release the SelectionFeedbackGenerator. selectionChanged Call when the selection changes (not on initial selection) Call when the selection changes (not on initial selection) ImpactFeedbackGenerator ImpactFeedbackGenerator is used to give user feedback when an impact between UI elements occurs Object ImpactFeedbackGenerator is used to give user feedback when an impact between UI elements occurs ImpactFeedbackGeneratortypeimpactOccurred Call when your UI element impacts something else Call when your UI element impacts something else prepare Informs self that it will likely receive events soon, so that it can ensure minimal latency for + if events are still imminently possible release Release the SelectionFeedbackGenerator. Release the SelectionFeedbackGenerator. selectionChanged Call when the selection changes (not on initial selection) Call when the selection changes (not on initial selection) StoppedReason An enumeration of possible reasons the haptic engine stopped running.Object An enumeration of possible reasons the haptic engine stopped running. applicationSuspended The app with haptics was suspended.2 The app with haptics was suspended. audioSessionInterrupt The audio session was interrupted.1 The audio session was interrupted. idleTimeout The haptic engine timed out during a task.3 The haptic engine timed out during a task. notifyWhenFinished You've asked to be notified notifyWhenPlayersFinished shuts down the engine.4 You've asked to be notified notifyWhenPlayersFinished shuts down the engine. systemError A system error stopped the engine.-1 A system error stopped the engine. ImpactFeedbackGenerator ImpactFeedbackGenerator is used to give user feedback when an impact between UI elements occurs Object ImpactFeedbackGenerator is used to give user feedback when an impact between UI elements occurs ImpactFeedbackGeneratortypeimpactOccurred Call when your UI element impacts something else Call when your UI element impacts something else prepare Informs self that it will likely receive events soon, so that it can ensure minimal latency for any feedback generated safe to call more than once before the generator receives an event, if events are still imminently possible Informs self that it will likely receive events soon, so that it can ensure minimal latency for any feedback generated safe to call more than once before the generator receives an event, - if events are still imminently possible release Release the ImpactFeedbackGenerator. Release the ImpactFeedbackGenerator. ImpactFeedbackStyleObjectHEAVY2LIGHT0MEDIUM1NotificationFeedbackTypeObjectERROR2SUCCESS0WARNING1NotificationFeedbackGenerator NotificationFeedbackGenerator is used to give user feedback when an notification is displayed Object NotificationFeedbackGenerator is used to give user feedback when an notification is displayed NotificationFeedbackGeneratornotificationOccurred Call when a notification is displayed, passing the corresponding type notificationType Call when a notification is displayed, passing the corresponding type prepare Informs self that it will likely receive events soon, so that it can ensure minimal latency for + if events are still imminently possible release Release the ImpactFeedbackGenerator. Release the ImpactFeedbackGenerator. ImpactFeedbackStyleObjectHEAVY2LIGHT0MEDIUM1HapticEngineObjectHapticEngine + An object that manages your app's requests to play haptic patterns.ANEError if the engine cannot be created + AnAn + An object that manages your app's requests to play haptic patterns. + +

If you want your app to play custom haptics, you need to create a haptic engine. + The haptic engine establishes the connection between your app and the underlying device hardware. + Even though you can define a haptic pattern without an engine, you need the engine to play that pattern. + Even though your app makes a request through the haptic engine, the operating system could still + override the request with system services, like haptics from system notifications.

+ +
playPattern + Plays a plist pattern from the specified URL.ANEError if the pattern cannot be played. + AnAnfileNameThe file name of the AHAP file containing the haptic event dictionary. + + Plays a plist pattern from the specified URL. + +

This method blocks processing on the current thread until the pattern has finished playing.

+
start + Synchronously starts the haptic engine.ANEError if the pattern cannot be played. + AnAn + Synchronously starts the haptic engine. + +

This method blocks all subsequent event processing on the current thread until the engine has started. + It throws an error if the engine can't start.

+
stop + Asynchronously stop the engine. + Asynchronously stop the engine. The handler will be called when the operation completes. + +

The handler is guaranteed to be called on either success or failure.

+
supportsHaptics + A Boolean value that indicates whether the device supports haptic event playback. + A Boolean value that indicates whether the device supports haptic event playback. + resetHandler + A function that the haptic engine calls when it stops due to external causes. + A function that the haptic engine calls when it stops due to external causes. + +

If the handler has to reset itself after a server failure, the system calls this block asynchronously. + In this block, release all haptic pattern players and recreate them. The system preserves HapticPattern + objects and HapticEngine properties across restarts. Consider trying to restart the engine inside the + reset block.

+
stoppedHandler + A function that the haptic engine calls after recovering from a haptic server error. + A function that the haptic engine calls after recovering from a haptic server error. + +

External causes that can trigger this block include audio session interruption, application suspension, + or system error. Calling stop doesn't trigger this block. + Callbacks to this block arrive on a non-main thread, so handle them in a thread-safe manner.

+
NotificationFeedbackTypeObjectERROR2SUCCESS0WARNING1NotificationFeedbackGenerator NotificationFeedbackGenerator is used to give user feedback when an notification is displayed Object NotificationFeedbackGenerator is used to give user feedback when an notification is displayed NotificationFeedbackGeneratornotificationOccurred Call when a notification is displayed, passing the corresponding type notificationType Call when a notification is displayed, passing the corresponding type prepare Informs self that it will likely receive events soon, so that it can ensure minimal latency for any feedback generated safe to call more than once before the generator receives an event, if events are still imminently possible Informs self that it will likely receive events soon, so that it can ensure minimal latency for any feedback generated safe to call more than once before the generator receives an event, diff --git a/native_extension/ane/extension_mobile.xml b/native_extension/ane/extension.xml similarity index 98% rename from native_extension/ane/extension_mobile.xml rename to native_extension/ane/extension.xml index cada79f..23edf59 100644 --- a/native_extension/ane/extension_mobile.xml +++ b/native_extension/ane/extension.xml @@ -3,7 +3,7 @@ com.tuarua.VibrationANE Vibration ANE This work is licensed under Apache License, Copyright (c) 2018 Tua Rua Ltd. - 1.2.0 + 1.3.0 diff --git a/native_extension/ane/platforms/default/library.swf b/native_extension/ane/platforms/default/library.swf index 8b9bc4f..3f4312c 100644 Binary files a/native_extension/ane/platforms/default/library.swf and b/native_extension/ane/platforms/default/library.swf differ diff --git a/native_extension/bin/VibrationANE.swc b/native_extension/bin/VibrationANE.swc index a098245..a8fc2d2 100644 Binary files a/native_extension/bin/VibrationANE.swc and b/native_extension/bin/VibrationANE.swc differ diff --git a/native_extension/src/avmplus/DescribeTypeJSON.as b/native_extension/src/avmplus/DescribeTypeJSON.as new file mode 100644 index 0000000..ac61e0a --- /dev/null +++ b/native_extension/src/avmplus/DescribeTypeJSON.as @@ -0,0 +1,71 @@ +// ================================================================================================= +// +// Modified by Rodrigo Lopez [roipekerâ„¢] on 20/01/2019. +// +// original class: +// https://github.com/tschneidereit/SwiftSuspenders/blob/master/src/avmplus/DescribeTypeJSON.as +// +// Check https://jacksondunstan.com/articles/2609 for reference. +// +// +// ================================================================================================= + +package avmplus { + +/** + * -- Basic Usage: + * + * var desc:Object = DescribeTypeJSON.run( AnyClassOrInstance ); + * trace( JSON.stringify( desc )) ; + * + */ + +/** + * Makes the hidden, unofficial function describeTypeJSON available outside of the avmplus + * package. + * + * As Adobe doen't officially support this method and it is only visible to client + * code by accident, it should only ever be used with runtime-detection and automatic fallback + * on describeType. + * + * @see http://www.tillschneidereit.de/2009/11/22/improved-reflection-support-in-flash-player-10-1/ + * @private + */ +public class DescribeTypeJSON { + //---------------------- Public Properties ----------------------// + public static var available:Boolean = describeTypeJSON != null; + + public static const INSTANCE_FLAGS:uint = INCLUDE_VARIABLES | USE_ITRAITS | HIDE_OBJECT; + public static const CLASS_FLAGS:uint = INCLUDE_VARIABLES | INCLUDE_TRAITS | HIDE_OBJECT; + + + private static var _instance:DescribeTypeJSON; + + public static function get():DescribeTypeJSON { + if (!_instance) _instance = new DescribeTypeJSON(); + return _instance; + } + + public static function run(obj:Object):Object { + // little hack to call Class from instance + if (!(obj is Class)) obj = Object(obj).constructor; + return get().describeType(obj, CLASS_FLAGS | INSTANCE_FLAGS); + } + + //---------------------- Public Methods ----------------------// + public function DescribeTypeJSON() { + } + + public function describeType(target:Object, flags:uint):Object { + return describeTypeJSON(target, flags); + } + + public function getInstanceDescription(type:Object):Object { + return describeTypeJSON(type, INSTANCE_FLAGS); + } + + public function getClassDescription(type:Class):Object { + return describeTypeJSON(type, CLASS_FLAGS); + } +} +} \ No newline at end of file diff --git a/native_extension/src/com/tuarua/VibrationANE.as b/native_extension/src/com/tuarua/VibrationANE.as deleted file mode 100644 index 7a83dc5..0000000 --- a/native_extension/src/com/tuarua/VibrationANE.as +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2018 Tua Rua Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.tuarua { -import com.tuarua.fre.ANEError; -import com.tuarua.vibration.ios.SystemSoundID; - -import flash.events.EventDispatcher; - -public class VibrationANE extends EventDispatcher { - private static var _isInited:Boolean; - private static var _vibrator:VibrationANE; - - /** @private */ - public function VibrationANE() { - if (_vibrator) { - throw new Error(VibrationANEContext.NAME + " is a singleton, use .vibrator"); - } - - if (VibrationANEContext.context) { - var theRet:* = VibrationANEContext.context.call("init"); - if (theRet is ANEError) throw theRet as ANEError; - _isInited = theRet as Boolean; - } - _vibrator = this; - } - - public static function get vibrator():VibrationANE { - if (!_vibrator) new VibrationANE(); - return _vibrator; - } - - /** - * Vibrate with a given pattern. - * - *

- * Pass in an array of ints that are the durations for which to turn on or off - * the vibrator in milliseconds. The first value indicates the number of milliseconds - * to wait before turning the vibrator on. The next value indicates the number of milliseconds - * for which to keep the vibrator on before turning it off. Subsequent values alternate - * between durations in milliseconds to turn the vibrator off or to turn the vibrator on. - *

- * To cause the pattern to repeat, pass the index into the pattern array at which - * to start the repeat, or -1 to disable repeating. - *

- * - * @param milliseconds the number of milliseconds to vibrate. Set to 0 when using a pattern. Ignored on iOS - * @param pattern an array of times for which to turn the vibrator on or off. Ignored on iOS - * @param repeat the index into pattern at which to repeat, or -1 if - * you don't want to repeat. Ignored on iOS - */ - android function vibrate(milliseconds:int = 0, pattern:Array = null, repeat:int = -1):void { - if (!safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("vibrate", milliseconds, pattern ? pattern : [], repeat); - if (theRet is ANEError) throw theRet as ANEError; - } - - /** Vibrate with a given pattern. */ - ios function vibrate(systemSound:int = SystemSoundID.DEFAULT):void { - if (!safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("vibrate", systemSound); - if (theRet is ANEError) throw theRet as ANEError; - } - - /** Turn the vibrator off. */ - public function cancel():void { - if (!safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("cancel"); - if (theRet is ANEError) throw theRet as ANEError; - } - - /** - * Check whether the hardware has a vibrator. - * - * @return True if the hardware has a vibrator, else false. - */ - public function get hasVibrator():Boolean { - if (!safetyCheck()) return false; - var theRet:* = VibrationANEContext.context.call("hasVibrator"); - if (theRet is ANEError) throw theRet as ANEError; - return theRet as Boolean; - } - - /** - * Check whether the hardware has haptic feedback. - * - * @return True if the hardware has haptic feedback, else false. Always returns false on Android. - */ - public function get hasHapticFeedback():Boolean { - if (!safetyCheck()) return false; - var theRet:* = VibrationANEContext.context.call("hasHapticFeedback"); - if (theRet is ANEError) throw theRet as ANEError; - return theRet as Boolean; - } - - /** - * Check whether the hardware has taptic engine. - * - * @return True if the hardware has taptic engine, else false. Always returns false on Android. - */ - public function get hasTapticEngine():Boolean { - if (!safetyCheck()) return false; - var theRet:* = VibrationANEContext.context.call("hasTapticEngine"); - if (theRet is ANEError) throw theRet as ANEError; - return theRet as Boolean; - } - - /** Disposes the ANE */ - public static function dispose():void { - if (VibrationANEContext.context) { - VibrationANEContext.dispose(); - } - } - - /** @return whether we have inited */ - public function get isInited():Boolean { - return _isInited; - } - - /** @private */ - public static function safetyCheck():Boolean { - if (!_isInited || VibrationANEContext.isDisposed) { - trace("You need to init first"); - return false; - } - return true; - } - -} -} \ No newline at end of file diff --git a/native_extension/src/com/tuarua/VibrationANEContext.as b/native_extension/src/com/tuarua/VibrationANEContext.as index dfce238..ffc9fd8 100644 --- a/native_extension/src/com/tuarua/VibrationANEContext.as +++ b/native_extension/src/com/tuarua/VibrationANEContext.as @@ -16,12 +16,17 @@ package com.tuarua { import flash.events.StatusEvent; import flash.external.ExtensionContext; +import flash.utils.Dictionary; + /** @private */ public class VibrationANEContext { internal static const NAME:String = "VibrationANE"; internal static const TRACE:String = "TRACE"; + private static const HAPTIC_ENGINE_STOPPED:String = "HapticEngineEvent.Stopped"; + private static const HAPTIC_ENGINE_RESTART:String = "HapticEngineEvent.Restart"; private static var _context:ExtensionContext; - private static var _isDisposed:Boolean; + public static var callbacks:Dictionary = new Dictionary(); + private static var argsAsJSON:Object; public function VibrationANEContext() { } public static function get context():ExtensionContext { @@ -29,7 +34,6 @@ public class VibrationANEContext { try { _context = ExtensionContext.createExtensionContext("com.tuarua." + NAME, null); _context.addEventListener(StatusEvent.STATUS, gotEvent); - _isDisposed = false; } catch (e:Error) { throw new Error("ANE " + NAME + " not created properly. Future calls will fail."); } @@ -37,27 +41,47 @@ public class VibrationANEContext { return _context; } + public static function createCallback(listener:Function):String { + var id:String; + if (listener) { + id = context.call("createGUID") as String; + callbacks[id] = listener; + } + return id; + } + + public static function callCallback(callbackId:String, clear:Boolean = true, ... args):void { + var callback:Function = callbacks[callbackId]; + if (callback == null) return; + callback.apply(null, args); + if (clear) { + delete callbacks[callbackId]; + } + } + private static function gotEvent(event:StatusEvent):void { switch (event.level) { case TRACE: trace("[" + NAME + "]", event.code); break; + case HAPTIC_ENGINE_STOPPED: + argsAsJSON = JSON.parse(event.code); + callCallback(argsAsJSON.callbackId, false, argsAsJSON.reason); + break; + case HAPTIC_ENGINE_RESTART: + argsAsJSON = JSON.parse(event.code); + callCallback(argsAsJSON.callbackId); + break; } } public static function dispose():void { - if (!_context) { - return; - } - _isDisposed = true; + if (!_context) return; trace("[" + NAME + "] Unloading ANE..."); _context.removeEventListener(StatusEvent.STATUS, gotEvent); _context.dispose(); _context = null; } - public static function get isDisposed():Boolean { - return _isDisposed; - } } } diff --git a/native_extension/src/com/tuarua/Vibrator.as b/native_extension/src/com/tuarua/Vibrator.as new file mode 100644 index 0000000..55f9a29 --- /dev/null +++ b/native_extension/src/com/tuarua/Vibrator.as @@ -0,0 +1,106 @@ +/* + * Copyright 2018 Tua Rua Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.tuarua { +import com.tuarua.fre.ANEError; +import com.tuarua.vibration.android.VibrationEffect; +import com.tuarua.vibration.ios.SystemSoundID; + +public class Vibrator { + private static var _shared:Vibrator; + + /** @private */ + public function Vibrator() { + if (_shared) { + throw new Error(VibrationANEContext.NAME + " is a singleton, use .shared()"); + } + + if (VibrationANEContext.context) { + var ret:* = VibrationANEContext.context.call("init"); + if (ret is ANEError) throw ret as ANEError; + } + _shared = this; + } + + public static function shared():Vibrator { + if (!_shared) new Vibrator(); + return _shared; + } + + /** + * Vibrate with a given pattern. + * + * @param vibe + */ + android function vibrate(vibe:VibrationEffect):void { + var ret:* = VibrationANEContext.context.call("vibrate", vibe); + if (ret is ANEError) throw ret as ANEError; + } + + /** Vibrate with a given pattern. */ + ios function vibrate(systemSound:int = SystemSoundID.DEFAULT):void { + var ret:* = VibrationANEContext.context.call("vibrate", systemSound); + if (ret is ANEError) throw ret as ANEError; + } + + /** Turn the vibrator off. */ + public function cancel():void { + var ret:* = VibrationANEContext.context.call("cancel"); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * Check whether the hardware has a vibrator. + * + * @return True if the hardware has a vibrator, else false. + */ + public function get hasVibrator():Boolean { + var ret:* = VibrationANEContext.context.call("hasVibrator"); + if (ret is ANEError) throw ret as ANEError; + return ret as Boolean; + } + + /** + * Check whether the hardware has haptic feedback. + * + * @return True if the hardware has haptic feedback, else false. Always returns false on Android. + */ + public function get hasHapticFeedback():Boolean { + var ret:* = VibrationANEContext.context.call("hasHapticFeedback"); + if (ret is ANEError) throw ret as ANEError; + return ret as Boolean; + } + + /** + * Check whether the hardware has taptic engine. + * + * @return True if the hardware has taptic engine, else false. Always returns false on Android. + */ + public function get hasTapticEngine():Boolean { + var ret:* = VibrationANEContext.context.call("hasTapticEngine"); + if (ret is ANEError) throw ret as ANEError; + return ret as Boolean; + } + + /** Disposes the ANE */ + public static function dispose():void { + if (VibrationANEContext.context) { + VibrationANEContext.dispose(); + } + } + + +} +} \ No newline at end of file diff --git a/native_extension/src/com/tuarua/fre/ANEError.as b/native_extension/src/com/tuarua/fre/ANEError.as index b4083d8..e67474b 100644 --- a/native_extension/src/com/tuarua/fre/ANEError.as +++ b/native_extension/src/com/tuarua/fre/ANEError.as @@ -1,5 +1,17 @@ -/** - * Created by Eoin Landy on 29/04/2017. +/* + * Copyright 2017 Tua Rua Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.tuarua.fre { import flash.system.Capabilities; diff --git a/native_extension/src/com/tuarua/fre/ANEUtils.as b/native_extension/src/com/tuarua/fre/ANEUtils.as index 322fd13..deb9d78 100644 --- a/native_extension/src/com/tuarua/fre/ANEUtils.as +++ b/native_extension/src/com/tuarua/fre/ANEUtils.as @@ -1,5 +1,5 @@ /* - * Copyright 2018 Tua Rua Ltd. + * Copyright 2017 Tua Rua Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ */ package com.tuarua.fre { +import avmplus.DescribeTypeJSON; + import flash.utils.describeType; import flash.utils.getDefinitionByName; import flash.utils.getQualifiedClassName; @@ -33,7 +35,7 @@ public class ANEUtils { return Class(getDefinitionByName(getQualifiedClassName(obj))); } - public static function getClassProps(clz:*):Vector. { + private static function _getClassProps(clz:*):Vector. { var ret:Vector. = new []; var isObject:Boolean = false; for (var id:String in clz) { @@ -49,62 +51,46 @@ public class ANEUtils { if (isObject) { return ret; } - var xml:XML = describeType(clz); - if (xml.variable && xml.variable.length() > 0) { - for each (var prop:XML in xml.variable) { - var obj:Object = {}; - obj.name = prop.@name.toString(); - obj.type = prop.@type.toString(); - obj.cls = obj.type == "*" ? null : getClass(Class(getDefinitionByName(obj.type))); - ret.push(obj); + if (DescribeTypeJSON.available) { + var json:Object = DescribeTypeJSON.run(clz); + if (json.traits.variables) { + for each (var propd:Object in json.traits.variables) { + var objd:Object = {}; + objd.name = propd.name; + objd.type = propd.type; + objd.cls = objd.type == "*" ? null : getClass(Class(getDefinitionByName(objd.type))); + ret.push(objd); + } } - } else if (xml.factory && xml.factory.variable && xml.factory.variable.length() > 0) { - for each (var propb:XML in xml.factory.variable) { - var objb:Object = {}; - objb.name = propb.@name.toString(); - objb.type = propb.@type.toString(); - objb.cls = objb.type == "*" ? null : getClass(Class(getDefinitionByName(objb.type))); - ret.push(objb); + } else { + var xml:XML = describeType(clz); + if (xml.variable && xml.variable.length() > 0) { + for each (var prop:XML in xml.variable) { + var obj:Object = {}; + obj.name = prop.@name.toString(); + obj.type = prop.@type.toString(); + obj.cls = obj.type == "*" ? null : getClass(Class(getDefinitionByName(obj.type))); + ret.push(obj); + } + } else if (xml.factory && xml.factory.variable && xml.factory.variable.length() > 0) { + for each (var propb:XML in xml.factory.variable) { + var objb:Object = {}; + objb.name = propb.@name.toString(); + objb.type = propb.@type.toString(); + objb.cls = objb.type == "*" ? null : getClass(Class(getDefinitionByName(objb.type))); + ret.push(objb); + } } } return ret; } + public static function getClassProps(clz:*):Vector. { + return _getClassProps(clz); + } + public function getClassProps(clz:*):Vector. { - var ret:Vector. = new []; - var isObject:Boolean = false; - for (var id:String in clz) { - var objc:Object = {}; - objc.name = id; - if (clz.hasOwnProperty(id)) { - objc.type = getClassType(clz[id]); - objc.cls = objc.type == "*" ? null : getClass(Class(getDefinitionByName(objc.type))); - ret.push(objc); - isObject = true; - } - } - if (isObject) { - return ret; - } - var xml:XML = describeType(clz); - if (xml.variable && xml.variable.length() > 0) { - for each (var prop:XML in xml.variable) { - var obj:Object = {}; - obj.name = prop.@name.toString(); - obj.type = prop.@type.toString(); - obj.cls = obj.type == "*" ? null : getClass(Class(getDefinitionByName(obj.type))); - ret.push(obj); - } - } else if (xml.factory && xml.factory.variable && xml.factory.variable.length() > 0) { - for each (var propb:XML in xml.factory.variable) { - var objb:Object = {}; - objb.name = propb.@name.toString(); - objb.type = propb.@type.toString(); - objb.cls = objb.type == "*" ? null : getClass(Class(getDefinitionByName(objb.type))); - ret.push(objb); - } - } - return ret; + return _getClassProps(clz); } private static function getPropClass(name:String, cls:Class):Class { @@ -145,13 +131,24 @@ public class ANEUtils { case Vector.: case Vector.: case Vector.: - classInstance[name] = from[name]; + if (classInstance.hasOwnProperty(name)) classInstance[name] = from[name]; break; case Date: - classInstance[name] = new Date(Date.parse(from[name])); + if (classInstance.hasOwnProperty(name)) classInstance[name] = new Date(Date.parse(from[name])); break; - default: //Object or Class - classInstance[name] = (propCls == null) ? from[name] : map(from[name], getPropClass(name, to)); + default: //Object or Class or Vector. + // handle Vector. + if (propCls && propCls.toString().indexOf("Vector.") > -1) { + var vec:* = new propCls(); + var vecClsName:String = propCls.toString().replace("[class Vector.<","").replace(">]",""); + var vecCls:Class = getClass(Class(getDefinitionByName(vecClsName))); + for each(var o:* in from[name]) { + vec.push(map(o, vecCls)); + } + if (classInstance.hasOwnProperty(name)) classInstance[name] = vec; + } else { + if (classInstance.hasOwnProperty(name)) classInstance[name] = (propCls == null) ? from[name] : map(from[name], getPropClass(name, to)); + } break; } diff --git a/native_extension/src/com/tuarua/vibration/android/OneShot.as b/native_extension/src/com/tuarua/vibration/android/OneShot.as new file mode 100644 index 0000000..4ccffa1 --- /dev/null +++ b/native_extension/src/com/tuarua/vibration/android/OneShot.as @@ -0,0 +1,32 @@ +package com.tuarua.vibration.android { +public class OneShot extends VibrationEffect { + private var _milliseconds:uint; + private var _amplitude:int = DEFAULT_AMPLITUDE; + + /** + * Create a one shot vibration. + * + * One shot vibrations will vibrate constantly for the specified period of time at the + * specified amplitude, and then stop. + * + * @param milliseconds The number of milliseconds to vibrate. This must be a positive number. + * @param amplitude The strength of the vibration. This must be a value between 1 and 255, or + * DEFAULT_AMPLITUDE. + * + */ + public function OneShot(milliseconds:uint, amplitude:int = -1) { + this._milliseconds = milliseconds; + this._amplitude = amplitude; + } + + /** @private */ + public function get milliseconds():int { + return _milliseconds; + } + + /** @private */ + public function get amplitude():int { + return _amplitude; + } +} +} diff --git a/native_extension/src/com/tuarua/vibration/android/VibrationEffect.as b/native_extension/src/com/tuarua/vibration/android/VibrationEffect.as new file mode 100644 index 0000000..c21caf6 --- /dev/null +++ b/native_extension/src/com/tuarua/vibration/android/VibrationEffect.as @@ -0,0 +1,16 @@ +package com.tuarua.vibration.android { +import flash.utils.getDefinitionByName; +import flash.utils.getQualifiedClassName; + +public class VibrationEffect { + /** The default vibration strength of the device.*/ + public static var DEFAULT_AMPLITUDE:int = -1; + + public function VibrationEffect() { + var className:String = getQualifiedClassName(this); + if (getDefinitionByName(className) == VibrationEffect) { + throw new ArgumentError(getQualifiedClassName(this) + "Class can not be instantiated."); + } + } +} +} diff --git a/native_extension/src/com/tuarua/vibration/android/Waveform.as b/native_extension/src/com/tuarua/vibration/android/Waveform.as new file mode 100644 index 0000000..df37ffe --- /dev/null +++ b/native_extension/src/com/tuarua/vibration/android/Waveform.as @@ -0,0 +1,55 @@ +package com.tuarua.vibration.android { +public class Waveform extends VibrationEffect { + private var _timings:Array; + private var _amplitudes:Array; + private var _repeat:int; + + /** + * Create a waveform vibration. + * + * Waveform vibrations are a potentially repeating series of timing and amplitude pairs. For + * each pair, the value in the amplitude array determines the strength of the vibration and the + * value in the timing array determines how long it vibrates for. An amplitude of 0 implies no + * vibration (i.e. off), and any pairs with a timing value of 0 will be ignored. + *

+ * The amplitude array of the generated waveform will be the same size as the given + * timing array with alternating values of 0 (i.e. off) and DEFAULT_AMPLITUDE, + * starting with 0. Therefore the first timing value will be the period to wait before turning + * the vibrator on, the second value will be how long to vibrate at DEFAULT_AMPLITUDE + * strength, etc. + *

+ * To cause the pattern to repeat, pass the index into the timings array at which to start the + * repetition, or -1 to disable repeating. + *

+ * + * @param timings The pattern of alternating on-off timings, starting with off. Timing values + * of 0 will cause the timing / amplitude pair to be ignored. + * @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values + * must be between 0 and 255, or equal to DEFAULT_AMPLITUDE. An + * amplitude value of 0 implies the motor is off. + * @param repeat The index into the timings array at which to repeat, or -1 if you you don't + * want to repeat. + * + */ + public function Waveform(timings:Array, amplitudes:Array = null, repeat:int = -1) { + this._timings = timings; + this._amplitudes = amplitudes ? amplitudes : []; + this._repeat = repeat; + } + + /** @private*/ + public function get timings():Array { + return _timings; + } + + /** @private*/ + public function get amplitudes():Array { + return _amplitudes; + } + + /** @private*/ + public function get repeat():int { + return _repeat; + } +} +} diff --git a/native_extension/src/com/tuarua/vibration/ios/HapticEngine.as b/native_extension/src/com/tuarua/vibration/ios/HapticEngine.as new file mode 100644 index 0000000..c52a6d4 --- /dev/null +++ b/native_extension/src/com/tuarua/vibration/ios/HapticEngine.as @@ -0,0 +1,91 @@ +package com.tuarua.vibration.ios { +import com.tuarua.VibrationANEContext; +import com.tuarua.fre.ANEError; + +public class HapticEngine { + /** + * An object that manages your app's requests to play haptic patterns. + * + *

If you want your app to play custom haptics, you need to create a haptic engine. + * The haptic engine establishes the connection between your app and the underlying device hardware. + * Even though you can define a haptic pattern without an engine, you need the engine to play that pattern. + * Even though your app makes a request through the haptic engine, the operating system could still + * override the request with system services, like haptics from system notifications.

+ * + * @throws An ANEError if the engine cannot be created + * */ + public function HapticEngine() { + var ret:* = VibrationANEContext.context.call("initHapticEngine"); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * Plays a plist pattern from the specified URL. + * + *

This method blocks processing on the current thread until the pattern has finished playing.

+ * @throws An ANEError if the pattern cannot be played. + * @param fileName The file name of the AHAP file containing the haptic event dictionary. + * */ + public function playPattern(fileName:String):void { + var ret:* = VibrationANEContext.context.call("playPattern", fileName); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * Synchronously starts the haptic engine. + * + *

This method blocks all subsequent event processing on the current thread until the engine has started. + * It throws an error if the engine can't start.

+ * @throws An ANEError if the pattern cannot be played. + * */ + public function start():void { + var ret:* = VibrationANEContext.context.call("startHapticEngine"); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * Asynchronously stop the engine. The handler will be called when the operation completes. + * + *

The handler is guaranteed to be called on either success or failure.

+ * */ + public function stop():void { + var ret:* = VibrationANEContext.context.call("stopHapticEngine"); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * A function that the haptic engine calls after recovering from a haptic server error. + * + *

External causes that can trigger this block include audio session interruption, application suspension, + * or system error. Calling stop doesn't trigger this block. + * Callbacks to this block arrive on a non-main thread, so handle them in a thread-safe manner.

+ * */ + public function set stoppedHandler(value:Function):void { + var ret:* = VibrationANEContext.context.call("stoppedHandler", + VibrationANEContext.createCallback(value)); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * A function that the haptic engine calls when it stops due to external causes. + * + *

If the handler has to reset itself after a server failure, the system calls this block asynchronously. + * In this block, release all haptic pattern players and recreate them. The system preserves HapticPattern + * objects and HapticEngine properties across restarts. Consider trying to restart the engine inside the + * reset block.

+ * */ + public function set resetHandler(value:Function):void { + var ret:* = VibrationANEContext.context.call("resetHandler", + VibrationANEContext.createCallback(value)); + if (ret is ANEError) throw ret as ANEError; + } + + /** + * A Boolean value that indicates whether the device supports haptic event playback. + * */ + public static function get supportsHaptics():Boolean { + return VibrationANEContext.context.call("hasHapticEngine") as Boolean; + } + +} +} diff --git a/native_extension/src/com/tuarua/vibration/ios/ImpactFeedbackGenerator.as b/native_extension/src/com/tuarua/vibration/ios/ImpactFeedbackGenerator.as index 2a56852..becddbc 100644 --- a/native_extension/src/com/tuarua/vibration/ios/ImpactFeedbackGenerator.as +++ b/native_extension/src/com/tuarua/vibration/ios/ImpactFeedbackGenerator.as @@ -15,7 +15,6 @@ */ package com.tuarua.vibration.ios { -import com.tuarua.VibrationANE; import com.tuarua.VibrationANEContext; import com.tuarua.fre.ANEError; @@ -25,32 +24,28 @@ public class ImpactFeedbackGenerator { public function ImpactFeedbackGenerator(type:uint) { this.type = type; - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("initImpact", type); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("initImpact", type); + if (ret is ANEError) throw ret as ANEError; } /** Informs self that it will likely receive events soon, so that it can ensure minimal latency for * any feedback generated safe to call more than once before the generator receives an event, * if events are still imminently possible */ public function prepare():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("prepareImpact", type); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("prepareImpact", type); + if (ret is ANEError) throw ret as ANEError; } /** Release the ImpactFeedbackGenerator. */ public function release():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("releaseImpact", type); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("releaseImpact", type); + if (ret is ANEError) throw ret as ANEError; } /** Call when your UI element impacts something else */ public function impactOccurred():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("impactOccurred", type); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("impactOccurred", type); + if (ret is ANEError) throw ret as ANEError; } } diff --git a/native_extension/src/com/tuarua/vibration/ios/NotificationFeedbackGenerator.as b/native_extension/src/com/tuarua/vibration/ios/NotificationFeedbackGenerator.as index 120c7bc..f3361d7 100644 --- a/native_extension/src/com/tuarua/vibration/ios/NotificationFeedbackGenerator.as +++ b/native_extension/src/com/tuarua/vibration/ios/NotificationFeedbackGenerator.as @@ -15,7 +15,6 @@ */ package com.tuarua.vibration.ios { -import com.tuarua.VibrationANE; import com.tuarua.VibrationANEContext; import com.tuarua.fre.ANEError; @@ -28,23 +27,20 @@ public class NotificationFeedbackGenerator { * any feedback generated safe to call more than once before the generator receives an event, * if events are still imminently possible */ public function prepare():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("prepareNotification"); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("prepareNotification"); + if (ret is ANEError) throw ret as ANEError; } /** Release the NotificationFeedbackGenerator.*/ public function release():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("releaseNotification"); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("releaseNotification"); + if (ret is ANEError) throw ret as ANEError; } /** Call when a notification is displayed, passing the corresponding type */ public function notificationOccurred(notificationType:uint):void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("notificationOccurred", notificationType); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("notificationOccurred", notificationType); + if (ret is ANEError) throw ret as ANEError; } diff --git a/native_extension/src/com/tuarua/vibration/ios/SelectionFeedbackGenerator.as b/native_extension/src/com/tuarua/vibration/ios/SelectionFeedbackGenerator.as index 22f674b..ba1d552 100644 --- a/native_extension/src/com/tuarua/vibration/ios/SelectionFeedbackGenerator.as +++ b/native_extension/src/com/tuarua/vibration/ios/SelectionFeedbackGenerator.as @@ -15,7 +15,6 @@ */ package com.tuarua.vibration.ios { -import com.tuarua.VibrationANE; import com.tuarua.VibrationANEContext; import com.tuarua.fre.ANEError; @@ -28,23 +27,20 @@ public class SelectionFeedbackGenerator { * any feedback generated safe to call more than once before the generator receives an event, * if events are still imminently possible */ public function prepare():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("prepareSelection"); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("prepareSelection"); + if (ret is ANEError) throw ret as ANEError; } /** Release the SelectionFeedbackGenerator. */ public function release():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("releaseSelection"); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("releaseSelection"); + if (ret is ANEError) throw ret as ANEError; } /** Call when the selection changes (not on initial selection) */ public function selectionChanged():void { - if (!VibrationANE.safetyCheck()) return; - var theRet:* = VibrationANEContext.context.call("selectionChanged"); - if (theRet is ANEError) throw theRet as ANEError; + var ret:* = VibrationANEContext.context.call("selectionChanged"); + if (ret is ANEError) throw ret as ANEError; } } diff --git a/native_extension/src/com/tuarua/vibration/ios/StoppedReason.as b/native_extension/src/com/tuarua/vibration/ios/StoppedReason.as new file mode 100644 index 0000000..5d7fc0b --- /dev/null +++ b/native_extension/src/com/tuarua/vibration/ios/StoppedReason.as @@ -0,0 +1,15 @@ +package com.tuarua.vibration.ios { +/** An enumeration of possible reasons the haptic engine stopped running. */ +public final class StoppedReason { + /** The audio session was interrupted. */ + public static const audioSessionInterrupt:int = 1; + /** The app with haptics was suspended. */ + public static const applicationSuspended:int = 2; + /** The haptic engine timed out during a task. */ + public static const idleTimeout:int = 3; + /** You've asked to be notified notifyWhenPlayersFinished shuts down the engine. */ + public static const notifyWhenFinished:int = 4; + /** A system error stopped the engine. */ + public static const systemError:int = -1; +} +} diff --git a/native_library/android/VibrationANE/.idea/codeStyles/Project.xml b/native_library/android/VibrationANE/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..681f41a --- /dev/null +++ b/native_library/android/VibrationANE/.idea/codeStyles/Project.xml @@ -0,0 +1,116 @@ + + + + + + + +
+ + + + xmlns:android + + ^$ + + + +
+
+ + + + xmlns:.* + + ^$ + + + BY_NAME + +
+
+ + + + .*:id + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + .*:name + + http://schemas.android.com/apk/res/android + + + +
+
+ + + + name + + ^$ + + + +
+
+ + + + style + + ^$ + + + +
+
+ + + + .* + + ^$ + + + BY_NAME + +
+
+ + + + .* + + http://schemas.android.com/apk/res/android + + + ANDROID_ATTRIBUTE_ORDER + +
+
+ + + + .* + + .* + + + BY_NAME + +
+
+
+
+
+
\ No newline at end of file diff --git a/native_library/android/VibrationANE/.idea/codeStyles/codeStyleConfig.xml b/native_library/android/VibrationANE/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/native_library/android/VibrationANE/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/native_library/android/VibrationANE/app/app.iml b/native_library/android/VibrationANE/app/app.iml index 0d6de00..c52c333 100644 --- a/native_library/android/VibrationANE/app/app.iml +++ b/native_library/android/VibrationANE/app/app.iml @@ -4,6 +4,8 @@ @@ -24,16 +26,17 @@ - + @@ -71,14 +75,14 @@ - - + + - - + + @@ -122,57 +126,14 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - + + + + \ No newline at end of file diff --git a/native_library/android/VibrationANE/app/build.gradle b/native_library/android/VibrationANE/app/build.gradle index 49b50a5..1d4d1f6 100644 --- a/native_library/android/VibrationANE/app/build.gradle +++ b/native_library/android/VibrationANE/app/build.gradle @@ -7,9 +7,9 @@ android { buildToolsVersion "28.0.3" defaultConfig { minSdkVersion 19 - targetSdkVersion compileSdkVersion + targetSdkVersion 28 versionCode 1 - versionName "1.2.0" + versionName "1.3.0" } buildTypes { release { @@ -21,6 +21,9 @@ android { sourceCompatibility = '1.8' targetCompatibility = '1.8' } + kotlinOptions { + jvmTarget = "1.8" + } } dependencies { diff --git a/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANE.java b/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANE.java index e4e148d..e14b327 100644 --- a/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANE.java +++ b/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANE.java @@ -39,8 +39,14 @@ public class VibrationANE implements FREExtension { ,"selectionChanged" ,"hasHapticFeedback" ,"hasTapticEngine" + ,"hasHapticEngine" + ,"initHapticEngine" + ,"stoppedHandler" + ,"resetHandler" + ,"startHapticEngine" + ,"stopHapticEngine" + ,"playPattern" }; - public static VibrationANEContext extensionContext; diff --git a/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANEContext.java b/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANEContext.java index e497e5e..5acf0dd 100644 --- a/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANEContext.java +++ b/native_library/android/VibrationANE/app/src/main/java/com/tuarua/VibrationANEContext.java @@ -19,19 +19,9 @@ import com.tuarua.frekotlin.FreKotlinMainController; +@SuppressWarnings("WeakerAccess") public class VibrationANEContext extends FreKotlinContext { - private final FreKotlinMainController controller; - - @SuppressWarnings("WeakerAccess") public VibrationANEContext(String name, FreKotlinMainController controller, String[] functions) { super(name, controller, functions); - this.controller = controller; - } - - - @Override - public void dispose() { - super.dispose(); - this.controller.dispose(); } } diff --git a/native_library/android/VibrationANE/app/src/main/java/com/tuarua/vibrationane/KotlinController.kt b/native_library/android/VibrationANE/app/src/main/java/com/tuarua/vibrationane/KotlinController.kt index 24a0d46..62e8cca 100644 --- a/native_library/android/VibrationANE/app/src/main/java/com/tuarua/vibrationane/KotlinController.kt +++ b/native_library/android/VibrationANE/app/src/main/java/com/tuarua/vibrationane/KotlinController.kt @@ -33,6 +33,18 @@ class KotlinController : FreKotlinMainController { private var packageInfo: PackageInfo? = null private var permissionsGranted = false private val permissionsNeeded: Array = arrayOf(Manifest.permission.VIBRATE) + private val oneShot = "OneShot" + private val waveform = "Waveform" + private fun hasRequiredPermissions(): Boolean { + val pi = packageInfo ?: return false + permissionsNeeded.forEach { p -> + if (p !in pi.requestedPermissions) { + trace("Please add $p to uses-permission list in your AIR manifest") + return false + } + } + return true + } fun init(ctx: FREContext, argv: FREArgv): FREObject? { val appActivity = ctx.activity ?: return false.toFREObject() @@ -44,23 +56,39 @@ class KotlinController : FreKotlinMainController { } fun vibrate(ctx: FREContext, argv: FREArgv): FREObject? { - argv.takeIf { argv.size > 2 } ?: return FreArgException("vibrate") - val milliseconds = Long(argv[0]) ?: return null - val pattern = LongArray(argv[1]) ?: return null - val repeat = Int(argv[2]) ?: return null + argv.takeIf { argv.size > 0 } ?: return FreArgException() + val freVibe = argv[0] + val className = freVibe.className?.split("::")?.last() ?: return null + var milliseconds: Long = 0 + var amplitude = 0 + var timings = longArrayOf() + var amplitudes = intArrayOf() + var repeat = 0 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - when { - pattern.isEmpty() -> vibrator?.vibrate(VibrationEffect.createOneShot(milliseconds, - VibrationEffect.DEFAULT_AMPLITUDE)) - else -> vibrator?.vibrate(VibrationEffect.createWaveform(pattern, repeat)) + when (className) { + oneShot -> { + milliseconds = Long(freVibe["milliseconds"]) ?: milliseconds + amplitude = Int(freVibe["amplitude"]) ?: amplitude } - } else { - when { - pattern.isEmpty() -> vibrator?.vibrate(milliseconds) - else -> vibrator?.vibrate(pattern, repeat) + waveform -> { + timings = LongArray(freVibe["timings"]) ?: timings + amplitudes = IntArray(freVibe["amplitudes"]) + repeat = Int(freVibe["repeat"]) ?: repeat } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) when (className) { + oneShot -> vibrator?.vibrate(VibrationEffect.createOneShot(milliseconds, amplitude)) + waveform -> if (amplitudes.isEmpty()) { + vibrator?.vibrate(VibrationEffect.createWaveform(timings, repeat)) + } else { + vibrator?.vibrate(VibrationEffect.createWaveform(timings, amplitudes, repeat)) + } + } else { + when (className) { + oneShot -> vibrator?.vibrate(milliseconds) + waveform -> vibrator?.vibrate(timings, repeat) + } } return null } @@ -85,19 +113,15 @@ class KotlinController : FreKotlinMainController { fun selectionChanged(ctx: FREContext, argv: FREArgv): FREObject? = null fun hasHapticFeedback(ctx: FREContext, argv: FREArgv): FREObject? = false.toFREObject() fun hasTapticEngine(ctx: FREContext, argv: FREArgv): FREObject? = false.toFREObject() + fun hasHapticEngine(ctx: FREContext, argv: FREArgv): FREObject? = false.toFREObject() + fun initHapticEngine(ctx: FREContext, argv: FREArgv): FREObject? = null + fun stoppedHandler(ctx: FREContext, argv: FREArgv): FREObject? = null + fun resetHandler(ctx: FREContext, argv: FREArgv): FREObject? = null + fun startHapticEngine(ctx: FREContext, argv: FREArgv): FREObject? = null + fun stopHapticEngine(ctx: FREContext, argv: FREArgv): FREObject? = null + fun playPattern(ctx: FREContext, argv: FREArgv): FREObject? = null - private fun hasRequiredPermissions(): Boolean { - val pi = packageInfo ?: return false - permissionsNeeded.forEach { p -> - if (p !in pi.requestedPermissions) { - trace("Please add $p to uses-permission list in your AIR manifest") - return false - } - } - return true - } - - override val TAG: String + override val TAG: String? get() = this::class.java.simpleName private var _context: FREContext? = null override var context: FREContext? diff --git a/native_library/android/VibrationANE/build.gradle b/native_library/android/VibrationANE/build.gradle index 0f9d03d..db90b3b 100644 --- a/native_library/android/VibrationANE/build.gradle +++ b/native_library/android/VibrationANE/build.gradle @@ -1,8 +1,8 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.40' - ext.frekotlin_version = '1.8.0' + ext.kotlin_version = '1.3.61' + ext.frekotlin_version = '1.9.5' repositories { google() jcenter() diff --git a/native_library/apple/VibrationANE/.swiftlint.yml b/native_library/apple/VibrationANE/.swiftlint.yml index 4796208..076d452 100644 --- a/native_library/apple/VibrationANE/.swiftlint.yml +++ b/native_library/apple/VibrationANE/.swiftlint.yml @@ -3,7 +3,6 @@ disabled_rules: # rule identifiers to exclude from running - identifier_name - function_body_length - cyclomatic_complexity -- todo - file_length - unused_optional_binding - notification_center_detachment diff --git a/native_library/apple/VibrationANE/Cartfile b/native_library/apple/VibrationANE/Cartfile index ff5cd47..5ba3e0d 100644 --- a/native_library/apple/VibrationANE/Cartfile +++ b/native_library/apple/VibrationANE/Cartfile @@ -1 +1 @@ -binary "https://github.com/tuarua/Swift-IOS-ANE/releases/download/3.1.0/FreSwift.json" ~> 3.1.0 \ No newline at end of file +binary "https://github.com/tuarua/Swift-IOS-ANE/releases/download/4.3.0/FreSwift.json" ~> 4.3.0 \ No newline at end of file diff --git a/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.pbxproj b/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.pbxproj index 13b408f..00be068 100644 --- a/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.pbxproj +++ b/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.pbxproj @@ -3,22 +3,25 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ - 9055E18E217141C80006CD4C /* TapticController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9055E18D217141C80006CD4C /* TapticController.swift */; }; - 906787BF217017D200100455 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906787BE217017D200100455 /* UIDevice.swift */; }; - CE05603F1FB25CE200D06976 /* FreSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE05603E1FB25CE200D06976 /* FreSwift.framework */; }; - CE0560401FB25CE200D06976 /* FreSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE05603E1FB25CE200D06976 /* FreSwift.framework */; }; - CE0560431FB25D1B00D06976 /* SwiftController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0560411FB25D1B00D06976 /* SwiftController.swift */; }; - CE0560451FB25DD000D06976 /* VibrationANE_FW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE87F4831FB259FA005CF7DF /* VibrationANE_FW.framework */; }; + 901D78F023E376D500C92385 /* HapticEngineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 901D78EF23E376D500C92385 /* HapticEngineController.swift */; }; + 9055E18E217141C80006CD4C /* TapticController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9055E18D217141C80006CD4C /* TapticController.swift */; platformFilter = ios; }; + 906787BF217017D200100455 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 906787BE217017D200100455 /* UIDevice.swift */; platformFilter = ios; }; + 90884CD823E4C2F10071FEC4 /* HapticEngineEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90884CD723E4C2F10071FEC4 /* HapticEngineEvent.swift */; }; + 90884CDB23E4C4CF0071FEC4 /* SwiftyJSON in Frameworks */ = {isa = PBXBuildFile; productRef = 90884CDA23E4C4CF0071FEC4 /* SwiftyJSON */; }; + CE05603F1FB25CE200D06976 /* FreSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE05603E1FB25CE200D06976 /* FreSwift.framework */; platformFilter = ios; }; + CE0560401FB25CE200D06976 /* FreSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE05603E1FB25CE200D06976 /* FreSwift.framework */; platformFilter = ios; }; + CE0560431FB25D1B00D06976 /* SwiftController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE0560411FB25D1B00D06976 /* SwiftController.swift */; platformFilter = ios; }; + CE0560451FB25DD000D06976 /* VibrationANE_FW.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CE87F4831FB259FA005CF7DF /* VibrationANE_FW.framework */; platformFilter = ios; }; CE05604A1FB25DF600D06976 /* VibrationANE_oc.h in Headers */ = {isa = PBXBuildFile; fileRef = CE87F4771FB259DB005CF7DF /* VibrationANE_oc.h */; }; CE05604E1FB25E8E00D06976 /* FreMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE05604D1FB25E8E00D06976 /* FreMacros.h */; }; - CE05604F1FB25E9F00D06976 /* FreMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE05604D1FB25E8E00D06976 /* FreMacros.h */; }; - CE87F4871FB259FA005CF7DF /* VibrationANE_FW.h in Headers */ = {isa = PBXBuildFile; fileRef = CE87F4851FB259FA005CF7DF /* VibrationANE_FW.h */; settings = {ATTRIBUTES = (Public, ); }; }; - CEBD78C92057198700ABE05A /* SwiftController+FreSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBD78C82057198700ABE05A /* SwiftController+FreSwift.swift */; }; - CEBD78CB20571A5200ABE05A /* VibrationANE.m in Sources */ = {isa = PBXBuildFile; fileRef = CE87F4781FB259DB005CF7DF /* VibrationANE.m */; }; + CE05604F1FB25E9F00D06976 /* FreMacros.h in Headers */ = {isa = PBXBuildFile; fileRef = CE05604D1FB25E8E00D06976 /* FreMacros.h */; platformFilter = ios; }; + CE87F4871FB259FA005CF7DF /* VibrationANE_FW.h in Headers */ = {isa = PBXBuildFile; fileRef = CE87F4851FB259FA005CF7DF /* VibrationANE_FW.h */; platformFilter = ios; settings = {ATTRIBUTES = (Public, ); }; }; + CEBD78C92057198700ABE05A /* SwiftController+FreSwift.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEBD78C82057198700ABE05A /* SwiftController+FreSwift.swift */; platformFilter = ios; }; + CEBD78CB20571A5200ABE05A /* VibrationANE.m in Sources */ = {isa = PBXBuildFile; fileRef = CE87F4781FB259DB005CF7DF /* VibrationANE.m */; platformFilter = ios; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -32,8 +35,10 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 901D78EF23E376D500C92385 /* HapticEngineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticEngineController.swift; sourceTree = ""; }; 9055E18D217141C80006CD4C /* TapticController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TapticController.swift; sourceTree = ""; }; 906787BE217017D200100455 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; + 90884CD723E4C2F10071FEC4 /* HapticEngineEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HapticEngineEvent.swift; sourceTree = ""; }; CE05603E1FB25CE200D06976 /* FreSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = FreSwift.framework; path = Carthage/Build/iOS/FreSwift.framework; sourceTree = ""; }; CE0560411FB25D1B00D06976 /* SwiftController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftController.swift; sourceTree = ""; }; CE05604D1FB25E8E00D06976 /* FreMacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FreMacros.h; sourceTree = ""; }; @@ -61,6 +66,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 90884CDB23E4C4CF0071FEC4 /* SwiftyJSON in Frameworks */, CE0560401FB25CE200D06976 /* FreSwift.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -76,6 +82,14 @@ path = Extensions; sourceTree = ""; }; + 90884CD623E4C2D60071FEC4 /* Events */ = { + isa = PBXGroup; + children = ( + 90884CD723E4C2F10071FEC4 /* HapticEngineEvent.swift */, + ); + path = Events; + sourceTree = ""; + }; CE05603D1FB25CD100D06976 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -106,6 +120,7 @@ CEBD78C52057192600ABE05A /* VibrationANE */ = { isa = PBXGroup; children = ( + 90884CD623E4C2D60071FEC4 /* Events */, 906787BD217017BE00100455 /* Extensions */, CEBD78C72057195500ABE05A /* Misc */, CEBD78C62057194000ABE05A /* Headers */, @@ -113,6 +128,7 @@ CE87F4781FB259DB005CF7DF /* VibrationANE.m */, CEBD78C82057198700ABE05A /* SwiftController+FreSwift.swift */, 9055E18D217141C80006CD4C /* TapticController.swift */, + 901D78EF23E376D500C92385 /* HapticEngineController.swift */, ); path = VibrationANE; sourceTree = ""; @@ -192,6 +208,9 @@ dependencies = ( ); name = VibrationANE_FW; + packageProductDependencies = ( + 90884CDA23E4C4CF0071FEC4 /* SwiftyJSON */, + ); productName = HelloWorldANE_FW; productReference = CE87F4831FB259FA005CF7DF /* VibrationANE_FW.framework */; productType = "com.apple.product-type.framework"; @@ -202,7 +221,7 @@ CE87F45D1FB259C9005CF7DF /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0930; + LastUpgradeCheck = 1120; ORGANIZATIONNAME = "Tua Rua Ltd"; TargetAttributes = { CE87F4741FB259DB005CF7DF = { @@ -212,19 +231,23 @@ }; CE87F4821FB259FA005CF7DF = { CreatedOnToolsVersion = 8.3; - LastSwiftMigration = 0910; + LastSwiftMigration = 1120; ProvisioningStyle = Manual; }; }; }; buildConfigurationList = CE87F4601FB259C9005CF7DF /* Build configuration list for PBXProject "VibrationANE" */; compatibilityVersion = "Xcode 8.0"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = CE87F45C1FB259C9005CF7DF; + packageReferences = ( + 90884CD923E4C4CF0071FEC4 /* XCRemoteSwiftPackageReference "SwiftyJSON" */, + ); productRefGroup = CE87F4661FB259C9005CF7DF /* Products */; projectDirPath = ""; projectRoot = ""; @@ -247,7 +270,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi"; + shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; }; CEDDADE01FBFB84900D6EB5F /* ShellScript */ = { isa = PBXShellScriptBuildPhase; @@ -260,7 +283,7 @@ ); runOnlyForDeploymentPostprocessing = 1; shellPath = /bin/sh; - shellScript = "../../../native_extension/ane/build_mobile.sh\n"; + shellScript = "../../../native_extension/ane/build.sh\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -279,6 +302,8 @@ files = ( CE0560431FB25D1B00D06976 /* SwiftController.swift in Sources */, 906787BF217017D200100455 /* UIDevice.swift in Sources */, + 901D78F023E376D500C92385 /* HapticEngineController.swift in Sources */, + 90884CD823E4C2F10071FEC4 /* HapticEngineEvent.swift in Sources */, 9055E18E217141C80006CD4C /* TapticController.swift in Sources */, CEBD78C92057198700ABE05A /* SwiftController+FreSwift.swift in Sources */, ); @@ -289,6 +314,7 @@ /* Begin PBXTargetDependency section */ CE0560471FB25DD600D06976 /* PBXTargetDependency */ = { isa = PBXTargetDependency; + platformFilter = ios; target = CE87F4821FB259FA005CF7DF /* VibrationANE_FW */; targetProxy = CE0560461FB25DD600D06976 /* PBXContainerItemProxy */; }; @@ -298,7 +324,9 @@ CE87F46C1FB259C9005CF7DF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -356,7 +384,9 @@ CE87F46D1FB259C9005CF7DF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; @@ -401,7 +431,8 @@ MTL_ENABLE_DEBUG_INFO = NO; OTHER_LDFLAGS = "-ObjC"; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; VALIDATE_PRODUCT = YES; }; name = Release; @@ -409,6 +440,7 @@ CE87F47C1FB259DB005CF7DF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_CXX_LIBRARY = "compiler-default"; CLANG_ENABLE_MODULES = YES; @@ -420,7 +452,11 @@ "$(PROJECT_DIR)/Carthage/Build/iOS", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -432,6 +468,7 @@ CE87F47D1FB259DB005CF7DF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_CXX_LIBRARY = "compiler-default"; CLANG_ENABLE_MODULES = YES; @@ -443,7 +480,11 @@ "$(PROJECT_DIR)/Carthage/Build/iOS", ); IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -454,7 +495,7 @@ CE87F4891FB259FA005CF7DF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_CXX_LIBRARY = "compiler-default"; CLANG_ENABLE_MODULES = YES; @@ -473,7 +514,11 @@ INFOPLIST_FILE = "$(SRCROOT)/VibrationANE/Misc/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.tuarua.VibrationANE; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -482,7 +527,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -492,7 +537,7 @@ CE87F48A1FB259FA005CF7DF /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; CLANG_CXX_LANGUAGE_STANDARD = "compiler-default"; CLANG_CXX_LIBRARY = "compiler-default"; CLANG_ENABLE_MODULES = YES; @@ -511,15 +556,20 @@ INFOPLIST_FILE = "$(SRCROOT)/VibrationANE/Misc/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; IPHONEOS_DEPLOYMENT_TARGET = 9.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.tuarua.VibrationANE; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -557,6 +607,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + 90884CD923E4C4CF0071FEC4 /* XCRemoteSwiftPackageReference "SwiftyJSON" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/SwiftyJSON/SwiftyJSON.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 5.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 90884CDA23E4C4CF0071FEC4 /* SwiftyJSON */ = { + isa = XCSwiftPackageProductDependency; + package = 90884CD923E4C4CF0071FEC4 /* XCRemoteSwiftPackageReference "SwiftyJSON" */; + productName = SwiftyJSON; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = CE87F45D1FB259C9005CF7DF /* Project object */; } diff --git a/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 1f5dbc5..919434a 100644 --- a/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..8a63c30 --- /dev/null +++ b/native_library/apple/VibrationANE/VibrationANE.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "SwiftyJSON", + "repositoryURL": "https://github.com/SwiftyJSON/SwiftyJSON.git", + "state": { + "branch": null, + "revision": "2b6054efa051565954e1d2b9da831680026cd768", + "version": "5.0.0" + } + } + ] + }, + "version": 1 +} diff --git a/native_library/apple/VibrationANE/VibrationANE/Events/HapticEngineEvent.swift b/native_library/apple/VibrationANE/VibrationANE/Events/HapticEngineEvent.swift new file mode 100644 index 0000000..f52cff6 --- /dev/null +++ b/native_library/apple/VibrationANE/VibrationANE/Events/HapticEngineEvent.swift @@ -0,0 +1,43 @@ +/* + * Copyright 2018 Tua Rua Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Foundation +import CoreHaptics +import SwiftyJSON + +@available(iOS 13.0, *) +class HapticEngineEvent: NSObject { + public static let HAPTIC_ENGINE_STOPPED = "HapticEngineEvent.Stopped" + public static let HAPTIC_ENGINE_RESTART = "HapticEngineEvent.Restart" + + var callbackId: String + var reason: CHHapticEngine.StoppedReason? + + init(callbackId: String, reason: CHHapticEngine.StoppedReason? = nil) { + self.callbackId = callbackId + self.reason = reason + } + + public func toJSONString() -> String { + var props = [String: Any]() + props["callbackId"] = callbackId + if let reason = reason?.rawValue { + props["reason"] = reason + } + return JSON(props).description + } + +} diff --git a/native_library/apple/VibrationANE/VibrationANE/Extensions/UIDevice.swift b/native_library/apple/VibrationANE/VibrationANE/Extensions/UIDevice.swift index a80b28a..e7bab78 100644 --- a/native_library/apple/VibrationANE/VibrationANE/Extensions/UIDevice.swift +++ b/native_library/apple/VibrationANE/VibrationANE/Extensions/UIDevice.swift @@ -14,6 +14,7 @@ extension UIDevice { case iPhoneXS = "iPhone XS" case iPhoneXSMax = "iPhone XS Max" case iPhoneXR = "iPhone XR" + case iPhone11 = "iPhone 11" } var platform: DevicePlatform { @@ -27,6 +28,8 @@ extension UIDevice { return .iPhoneXSMax case "iPhone11,8": return .iPhoneXR + case "iPhone12,1", "iPhone12,3", "iPhone12,5": + return .iPhone11 case "iPhone10,3", "iPhone10,6": return .iPhoneX case "iPhone10,2", "iPhone10,5": @@ -64,5 +67,6 @@ extension UIDevice { || platform == .iPhoneXR || platform == .iPhoneXS || platform == .iPhoneXSMax + || platform == .iPhone11 } } diff --git a/native_library/apple/VibrationANE/VibrationANE/HapticEngineController.swift b/native_library/apple/VibrationANE/VibrationANE/HapticEngineController.swift new file mode 100644 index 0000000..9d456a0 --- /dev/null +++ b/native_library/apple/VibrationANE/VibrationANE/HapticEngineController.swift @@ -0,0 +1,86 @@ +/* Copyright 2018 Tua Rua Ltd. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +import Foundation +import CoreHaptics + +@available(iOS 13.0, *) +class HapticEngineController: FreSwiftController { + var context: FreContextSwift! + static var TAG: String = "HapticEngineController" + lazy var supportsHaptics = CHHapticEngine.capabilitiesForHardware().supportsHaptics + private var engine: CHHapticEngine? + private var _stoppedCallbackId: String? + var stoppedCallbackId: String? { + set { _stoppedCallbackId = newValue } + get { return _stoppedCallbackId } + } + + private var _resetCallbackId: String? + var resetCallbackId: String? { + set { _resetCallbackId = newValue } + get { return _resetCallbackId } + } + + init(ctx: FreContextSwift) { + self.context = ctx + } + + func createEngine() -> String? { + do { + engine = try CHHapticEngine() + } catch let error { + return error.localizedDescription + } + + engine?.stoppedHandler = { reason in + if let callbackId = self.resetCallbackId { + self.dispatchEvent(name: HapticEngineEvent.HAPTIC_ENGINE_STOPPED, + value: HapticEngineEvent(callbackId: callbackId, reason: reason).toJSONString()) + } + } + + engine?.resetHandler = { + if let callbackId = self.stoppedCallbackId { + self.dispatchEvent(name: HapticEngineEvent.HAPTIC_ENGINE_RESTART, + value: HapticEngineEvent(callbackId: callbackId).toJSONString()) + } + } + return nil + } + + func start() -> String? { + do { + try engine?.start() + } catch let error { + return error.localizedDescription + } + return nil + } + + func stop() -> String? { + engine?.stop() + return nil + } + + func playPattern(path: String) -> String? { + do { + try engine?.playPattern(from: URL(fileURLWithPath: path)) + } catch let error { + return error.localizedDescription + } + return nil + } +} diff --git a/native_library/apple/VibrationANE/VibrationANE/SwiftController+FreSwift.swift b/native_library/apple/VibrationANE/VibrationANE/SwiftController+FreSwift.swift index 8e54148..a8bf5e1 100644 --- a/native_library/apple/VibrationANE/VibrationANE/SwiftController+FreSwift.swift +++ b/native_library/apple/VibrationANE/VibrationANE/SwiftController+FreSwift.swift @@ -16,8 +16,6 @@ import Foundation import FreSwift extension SwiftController: FreSwiftMainController { - // must have this function !! - // Make sure these funcs match those in MLANE.m @objc public func getFunctions(prefix: String) -> [String] { functionsToSet["\(prefix)init"] = initController functionsToSet["\(prefix)vibrate"] = vibrate @@ -35,6 +33,13 @@ extension SwiftController: FreSwiftMainController { functionsToSet["\(prefix)selectionChanged"] = selectionChanged functionsToSet["\(prefix)hasHapticFeedback"] = hasHapticFeedback functionsToSet["\(prefix)hasTapticEngine"] = hasTapticEngine + functionsToSet["\(prefix)hasHapticEngine"] = hasHapticEngine + functionsToSet["\(prefix)initHapticEngine"] = initHapticEngine + functionsToSet["\(prefix)stoppedHandler"] = stoppedHandler + functionsToSet["\(prefix)resetHandler"] = resetHandler + functionsToSet["\(prefix)startHapticEngine"] = startHapticEngine + functionsToSet["\(prefix)stopHapticEngine"] = stopHapticEngine + functionsToSet["\(prefix)playPattern"] = playPattern var arr: [String] = [] for key in functionsToSet.keys { @@ -44,7 +49,6 @@ extension SwiftController: FreSwiftMainController { return arr } - // Must have this function. It exposes the methods to our entry ObjC. @objc public func callSwiftFunction(name: String, ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { if let fm = functionsToSet[name] { return fm(ctx, argc, argv) @@ -53,7 +57,7 @@ extension SwiftController: FreSwiftMainController { } @objc public func setFREContext(ctx: FREContext) { - self.context = FreContextSwift.init(freContext: ctx) + self.context = FreContextSwift(freContext: ctx) } @objc public func onLoad() { diff --git a/native_library/apple/VibrationANE/VibrationANE/SwiftController.swift b/native_library/apple/VibrationANE/VibrationANE/SwiftController.swift index 3c8ccd1..84315e6 100644 --- a/native_library/apple/VibrationANE/VibrationANE/SwiftController.swift +++ b/native_library/apple/VibrationANE/VibrationANE/SwiftController.swift @@ -25,6 +25,9 @@ public class SwiftController: NSObject { @available(iOS 10.0, *) lazy var tapticController: TapticController = TapticController() + @available(iOS 13.0, *) + lazy var hapticEngineController: HapticEngineController? = nil + private var isPhone: Bool { let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom switch deviceIdiom { @@ -43,7 +46,7 @@ public class SwiftController: NSObject { guard argc > 0, let systemSound = Int(argv[0]) else { - return FreArgError(message: "vibrate").getError(#file, #line, #column) + return FreArgError().getError() } var systemSoundID = SystemSoundID(kSystemSoundID_Vibrate) @@ -78,7 +81,7 @@ public class SwiftController: NSObject { guard argc > 0, let type = Int(argv[0]) else { - return FreArgError(message: "notificationOccurred").getError(#file, #line, #column) + return FreArgError().getError() } guard #available(iOS 10.0, *), isPhone, UIDevice.current.hasHapticFeedback else { return nil } tapticController.notificationOccurred(type: type) @@ -90,7 +93,7 @@ public class SwiftController: NSObject { guard argc > 0, let type = Int(argv[0]) else { - return FreArgError(message: "initImpact").getError(#file, #line, #column) + return FreArgError().getError() } tapticController.initImpact(type: type) return nil @@ -101,7 +104,7 @@ public class SwiftController: NSObject { guard argc > 0, let type = Int(argv[0]) else { - return FreArgError(message: "prepareImpact").getError(#file, #line, #column) + return FreArgError().getError() } tapticController.prepareImpact(type: type) return nil @@ -112,7 +115,7 @@ public class SwiftController: NSObject { guard argc > 0, let type = Int(argv[0]) else { - return FreArgError(message: "prepareImpact").getError(#file, #line, #column) + return FreArgError().getError() } tapticController.releaseImpact(type: type) return nil @@ -123,7 +126,7 @@ public class SwiftController: NSObject { guard argc > 0, let type = Int(argv[0]) else { - return FreArgError(message: "impactOccurred").getError(#file, #line, #column) + return FreArgError().getError() } tapticController.impactOccurred(type: type) return nil @@ -155,6 +158,77 @@ public class SwiftController: NSObject { return UIDevice.current.hasTapticEngine.toFREObject() } + func hasHapticEngine(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *) else { return false.toFREObject()} + return hapticEngineController?.supportsHaptics.toFREObject() + } + + func initHapticEngine(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *) else { return nil } + hapticEngineController = HapticEngineController(ctx: self.context) + if let error = hapticEngineController?.createEngine() { + return FreError(message: error, type: FreError.Code.ok).getError() + } + return nil + } + + func stoppedHandler(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *) else { return nil } + guard argc > 0, + let callbackId = String(argv[0]) + else { + return FreArgError().getError() + } + hapticEngineController?.stoppedCallbackId = callbackId + return nil + } + + func resetHandler(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *) else { return nil } + guard argc > 0, + let callbackId = String(argv[0]) + else { + return FreArgError().getError() + } + hapticEngineController?.resetCallbackId = callbackId + return nil + } + + func startHapticEngine(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *) else { return nil } + if let error = hapticEngineController?.start() { + return FreError(message: error, type: FreError.Code.ok).getError() + } + return nil + } + + func stopHapticEngine(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *) else { return nil } + if let error = hapticEngineController?.stop() { + return FreError(message: error, type: FreError.Code.ok).getError() + } + return nil + } + + func playPattern(ctx: FREContext, argc: FREArgc, argv: FREArgv) -> FREObject? { + guard #available(iOS 13.0, *), + let hapticEngineController = hapticEngineController, + hapticEngineController.supportsHaptics + else { + return FreError(message: "error", type: FreError.Code.ok).getError() + } + guard argc > 0, + let filename = String(argv[0]), + let path = Bundle.main.path(forResource: filename, ofType: "ahap") + else { + return FreArgError().getError() + } + if let error = hapticEngineController.playPattern(path: path) { + return FreError(message: error, type: FreError.Code.ok).getError() + } + return nil + } + @objc public func dispose() { guard #available(iOS 10.0, *), isPhone, UIDevice.current.hasTapticEngine else { return } tapticController.dispose() diff --git a/native_library/apple/VibrationANE/VibrationANE/VibrationANE.m b/native_library/apple/VibrationANE/VibrationANE/VibrationANE.m index f9c8aeb..69973a0 100644 --- a/native_library/apple/VibrationANE/VibrationANE/VibrationANE.m +++ b/native_library/apple/VibrationANE/VibrationANE/VibrationANE.m @@ -17,7 +17,7 @@ #import "VibrationANE_oc.h" #import -#define FRE_OBJC_BRIDGE TRVIB_FlashRuntimeExtensionsBridge // use unique prefix throughout to prevent clashes with other ANEs +#define FRE_OBJC_BRIDGE TRVIB_FlashRuntimeExtensionsBridge @interface FRE_OBJC_BRIDGE : NSObject @end @implementation FRE_OBJC_BRIDGE { @@ -26,14 +26,10 @@ @implementation FRE_OBJC_BRIDGE { @end @implementation VibrationANE_LIB -SWIFT_DECL(TRVIB) // use unique prefix throughout to prevent clashes with other ANEs +SWIFT_DECL(TRVIB) CONTEXT_INIT(TRVIB) { SWIFT_INITS(TRVIB) - - /**************************************************************************/ - /******* MAKE SURE TO ADD FUNCTIONS HERE THE SAME AS SWIFT CONTROLLER *****/ - /**************************************************************************/ - + static FRENamedFunction extensionFunctions[] = { MAP_FUNCTION(TRVIB, init) @@ -52,11 +48,15 @@ @implementation VibrationANE_LIB ,MAP_FUNCTION(TRVIB, selectionChanged) ,MAP_FUNCTION(TRVIB, hasHapticFeedback) ,MAP_FUNCTION(TRVIB, hasTapticEngine) + ,MAP_FUNCTION(TRVIB, hasHapticEngine) + ,MAP_FUNCTION(TRVIB, initHapticEngine) + ,MAP_FUNCTION(TRVIB, stoppedHandler) + ,MAP_FUNCTION(TRVIB, resetHandler) + ,MAP_FUNCTION(TRVIB, startHapticEngine) + ,MAP_FUNCTION(TRVIB, stopHapticEngine) + ,MAP_FUNCTION(TRVIB, playPattern) }; - /**************************************************************************/ - /**************************************************************************/ - SET_FUNCTIONS }