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.VibrationANEVibration ANEThis 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.