/// <summary>
        /// Plays a haptic pattern, the most complex type of haptic, defined by a JSON string on iOS, and a pattern on Android
        /// </summary>
        /// <param name="iOSJSONString"></param>
        /// <param name="androidPattern"></param>
        /// <param name="androidAmplitudes"></param>
        /// <param name="androidRepeat"></param>
        /// <param name="fallbackOldiOS"></param>
        public static void AdvancedHapticPattern(string iOSJSONString, long[] androidPattern, int[] androidAmplitudes, int androidRepeat, HapticTypes fallbackOldiOS = HapticTypes.None)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Advanced Haptic Pattern");

            if (Android())
            {
                MMNVAndroid.AndroidVibrate(androidPattern, androidAmplitudes, androidRepeat);
            }
            else if (iOS())
            {
                if (iOSVersion >= 13)
                {
                    MMNViOSCoreHaptics.PlayCoreHapticsFromJSON(iOSJSONString);
                }
                else
                {
                    MMNViOS.iOSTriggerHaptics(fallbackOldiOS);
                }
            }
        }
        /// <summary>
        /// Plays a continuous haptic of the specified intensity, sharpness and duration
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        /// <param name="duration"></param>
        public static void ContinuousHaptic(float intensity, float sharpness, float duration, HapticTypes fallbackOldiOS = HapticTypes.None, MonoBehaviour mono = null)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Continuous Haptic");

            if (Android())
            {
                intensity = NiceVibrationsDemoHelpers.Remap(intensity, 0f, 1f, 0, 255);

                MMNVAndroid.AndroidVibrate((long)(duration * 1000), (int)(intensity));
            }
            else if (iOS())
            {
                if (iOSVersion >= 13)
                {
                    MMNViOSCoreHaptics.PlayContinuousHapticPattern(intensity, sharpness, duration, mono);
                }
                else
                {
                    MMNViOS.iOSTriggerHaptics(fallbackOldiOS);
                }
            }
        }
        /// <summary>
        /// Triggers a haptic feedback of the specified type
        /// </summary>
        /// <param name="type">Type.</param>
        public static void Haptic(HapticTypes type, bool defaultToRegularVibrate = false)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Regular Haptic");

            if (Android())
            {
                switch (type)
                {
                case HapticTypes.None:
                    // do nothing
                    break;

                case HapticTypes.Selection:
                    MMNVAndroid.AndroidVibrate(LightDuration, LightAmplitude);
                    break;

                case HapticTypes.Success:
                    MMNVAndroid.AndroidVibrate(_successPattern, _successPatternAmplitude, -1);
                    break;

                case HapticTypes.Warning:
                    MMNVAndroid.AndroidVibrate(_warningPattern, _warningPatternAmplitude, -1);
                    break;

                case HapticTypes.Failure:
                    MMNVAndroid.AndroidVibrate(_failurePattern, _failurePatternAmplitude, -1);
                    break;

                case HapticTypes.LightImpact:
                    MMNVAndroid.AndroidVibrate(_lightImpactPattern, _lightImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.MediumImpact:
                    MMNVAndroid.AndroidVibrate(_mediumImpactPattern, _mediumImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.HeavyImpact:
                    MMNVAndroid.AndroidVibrate(_HeavyImpactPattern, _HeavyImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.RigidImpact:
                    MMNVAndroid.AndroidVibrate(_rigidImpactPattern, _rigidImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.SoftImpact:
                    MMNVAndroid.AndroidVibrate(_softImpactPattern, _softImpactPatternAmplitude, -1);
                    break;
                }
            }
            else if (iOS())
            {
                MMNViOS.iOSTriggerHaptics(type, defaultToRegularVibrate);
            }
        }
        /// <summary>
        /// Plays a transient haptic, signature with more fine control
        /// </summary>
        /// <param name="vibrateiOS"></param>
        /// <param name="iOSIntensity"></param>
        /// <param name="iOSSharpness"></param>
        /// <param name="vibrateAndroid"></param>
        /// <param name="androidIntensity"></param>
        /// <param name="androidSharpness"></param>
        /// <param name="vibrateAndroidIfNoSupport"></param>
        /// <param name="rumble"></param>
        /// <param name="rumbleLowFrequency"></param>
        /// <param name="rumbleHighFrequency"></param>
        /// <param name="controllerID"></param>
        /// <param name="coroutineSupport"></param>
        public static void TransientHaptic(bool vibrateiOS, float iOSIntensity, float iOSSharpness,
                                           bool vibrateAndroid, float androidIntensity = 1f, float androidSharpness = 1f,
                                           bool vibrateAndroidIfNoSupport = false,
                                           bool rumble = true, float rumbleLowFrequency         = 1f, float rumbleHighFrequency = 1f, int controllerID = -1,
                                           MonoBehaviour coroutineSupport = null, bool threaded = true)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Transient Haptic");

            if (Android() && vibrateAndroid)
            {
                if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport)
                {
                    return;
                }
                androidIntensity = Remap(androidIntensity, 0f, 1f, 0, 255);
                MMNVAndroid.AndroidVibrate(100, (int)(androidIntensity));
            }
            else if (iOS() && vibrateiOS)
            {
                if ((iOSVersion >= 13) && HapticsSupported())
                {
                    MMNViOSCoreHaptics.PlayTransientHapticPattern(iOSIntensity, iOSSharpness, threaded);
                    _hapticsPlayedOnce = true;
                }
                else
                {
                    if (iOSIntensity < 0.3f)
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.LightImpact);
                    }
                    else if ((iOSIntensity >= 0.3f) && (iOSIntensity < 0.6f))
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.MediumImpact);
                    }
                    else
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.HeavyImpact);
                    }
                }
            }
            if (rumble && (coroutineSupport != null))
            {
                #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
                MMNVRumble.Rumble(rumbleLowFrequency, rumbleHighFrequency, 0.08f, coroutineSupport, controllerID);
                #endif
            }
        }
        /// <summary>
        /// Displays the debug information (API version on Android, iOS sdk version, and error message otherwise)
        /// </summary>
        protected virtual void DisplayInformation()
        {
            if (MMVibrationManager.Android())
            {
                _platformString = "API version " + MMNVAndroid.AndroidSDKVersion().ToString();
            }
            else if (MMVibrationManager.iOS())
            {
                _platformString = "iOS " + MMNViOS.iOSSDKVersion();
            }
            else
            {
                _platformString = Application.platform + ", not supported by Nice Vibrations for now.";
            }

            DebugTextBox.text = "Platform : " + _platformString + "\n Nice Vibrations v" + _CURRENTVERSION;
        }
 /// <summary>
 /// Returns true if haptics are supported on this device
 /// </summary>
 /// <returns></returns>
 public static bool HapticsSupported()
 {
     if (iOS())
     {
         if (iOSVersion >= 13)
         {
             return(MMNViOSCoreHaptics.CoreHapticsSupported());
         }
         else
         {
             return(MMNViOS.iOSHapticsSupported());
         }
     }
     if (Android())
     {
         return(MMNVAndroid.AndroidHasVibrator());
     }
     return(false);
 }
        /// <summary>
        /// Plays a advanced haptic pattern,
        /// </summary>
        /// <param name="ios"></param>
        /// <param name="iOSJSONString"></param>
        /// <param name="android"></param>
        /// <param name="androidPattern"></param>
        /// <param name="androidAmplitudes"></param>
        /// <param name="androidRepeat"></param>
        /// <param name="vibrateAndroidIfNoSupport"></param>
        /// <param name="rumble"></param>
        /// <param name="rumblePattern"></param>
        /// <param name="rumbleLowFreqAmplitudes"></param>
        /// <param name="rumbleHighFreqAmplitudes"></param>
        /// <param name="rumbleRepeat"></param>
        /// <param name="fallbackOldiOS"></param>
        /// <param name="coroutineSupport"></param>
        /// <param name="controllerID"></param>
        public static void AdvancedHapticPattern(bool ios, string iOSJSONString,
                                                 bool android, long[] androidPattern, int[] androidAmplitudes, int androidRepeat,
                                                 bool vibrateAndroidIfNoSupport,
                                                 bool rumble,
                                                 long[] rumblePattern, int[] rumbleLowFreqAmplitudes, int[] rumbleHighFreqAmplitudes, int rumbleRepeat,
                                                 HapticTypes fallbackOldiOS     = HapticTypes.None,
                                                 MonoBehaviour coroutineSupport = null, int controllerID = -1, bool threaded = false)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Advanced Haptic Pattern");

            if (Android())
            {
                if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport)
                {
                    return;
                }
                MMNVAndroid.AndroidVibrate(androidPattern, androidAmplitudes, androidRepeat, threaded);
            }
            else if (iOS())
            {
                if ((iOSVersion >= 13) && HapticsSupported())
                {
                    MMNViOSCoreHaptics.PlayCoreHapticsFromJSON(iOSJSONString, threaded);
                    _hapticsPlayedOnce = true;
                }
                else
                {
                    MMNViOS.iOSTriggerHaptics(fallbackOldiOS);
                }
            }
#if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
            if (coroutineSupport != null)
            {
                MMNVRumble.Rumble(rumblePattern, rumbleLowFreqAmplitudes, rumbleHighFreqAmplitudes, rumbleRepeat, coroutineSupport, controllerID);
            }
#endif
        }
        /// <summary>
        /// Plays a continuous haptic, full options signature
        /// </summary>
        /// <param name="vibrateiOS"></param>
        /// <param name="iOSIntensity"></param>
        /// <param name="iOSSharpness"></param>
        /// <param name="fallbackOldiOS"></param>
        /// <param name="vibrateAndroid"></param>
        /// <param name="androidIntensity"></param>
        /// <param name="androidSharpness"></param>
        /// <param name="vibrateAndroidIfNoSupport"></param>
        /// <param name="rumble"></param>
        /// <param name="rumbleLowFrequency"></param>
        /// <param name="rumbleHighFrequency"></param>
        /// <param name="controllerID"></param>
        /// <param name="duration">the duration in seconds</param>
        /// <param name="mono">a monobehaviour to use to sustain this haptic</param>
        /// <param name="threaded">whether to call this on the main thread (false) or a secondary one (true)</param>
        /// <param name="fullIntensity">whether to allow for full intensity control for subsequent updates</param>
        public static void ContinuousHaptic(bool vibrateiOS, float iOSIntensity, float iOSSharpness, HapticTypes fallbackOldiOS,
                                            bool vibrateAndroid, float androidIntensity, float androidSharpness,
                                            bool vibrateAndroidIfNoSupport,
                                            bool rumble, float rumbleLowFrequency, float rumbleHighFrequency, int controllerID,
                                            float duration,
                                            MonoBehaviour mono = null, bool threaded = false, bool fullIntensity = true)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Continuous Haptic");

            if (Android() && vibrateAndroid)
            {
                if (!MMNVAndroid.AndroidHasAmplitudeControl() && !vibrateAndroidIfNoSupport)
                {
                    return;
                }
                androidIntensity = Remap(androidIntensity, 0f, 1f, 0, 255);
                MMNVAndroid.AndroidVibrate((long)(duration * 1000), (int)(androidIntensity));
            }
            else if (iOS() && vibrateiOS)
            {
                if ((iOSVersion >= 13) && HapticsSupported())
                {
                    MMNViOSCoreHaptics.PlayContinuousHapticPattern(iOSIntensity, iOSSharpness, duration, mono, threaded, fullIntensity);
                    _hapticsPlayedOnce = true;
                }
                else
                {
                    MMNViOS.iOSTriggerHaptics(fallbackOldiOS);
                }
            }
            if (rumble && (mono != null))
            {
#if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
                MMNVRumble.RumbleContinuous(rumbleLowFrequency, rumbleHighFrequency, controllerID);
#endif
            }
        }
        /// <summary>
        /// Plays a transient haptic, a single, short haptic feedback, of the specified intensity and sharpness
        /// </summary>
        /// <param name="intensity"></param>
        /// <param name="sharpness"></param>
        public static void TransientHaptic(float intensity, float sharpness)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Transient Haptic");

            if (Android())
            {
                intensity = NiceVibrationsDemoHelpers.Remap(intensity, 0f, 1f, 0, 255);

                MMNVAndroid.AndroidVibrate(100, (int)(intensity));
            }
            else if (iOS())
            {
                if (iOSVersion >= 13)
                {
                    MMNViOSCoreHaptics.PlayTransientHapticPattern(intensity, sharpness);
                }
                else
                {
                    if (intensity < 0.3f)
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.LightImpact);
                    }
                    else if ((intensity >= 0.3f) && (intensity < 0.6f))
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.MediumImpact);
                    }
                    else
                    {
                        MMNViOS.iOSTriggerHaptics(HapticTypes.HeavyImpact);
                    }
                }
            }
        }
 /// <summary>
 /// Triggers a simple vibration
 /// </summary>
 public static void Vibrate()
 {
     DebugLog("[MMVibrationManager] Vibrate");
     if (!_vibrationsActive)
     {
         return;
     }
     if (Android())
     {
         MMNVAndroid.AndroidVibrate(MediumDuration);
     }
     else if (iOS())
     {
         if (iOSVersion >= 13)
         {
             MMNViOSCoreHaptics.PlayTransientHapticPattern(0.8f, 0.8f);
         }
         else
         {
             MMNViOS.iOSTriggerHaptics(HapticTypes.MediumImpact);
         }
     }
 }
 /// <summary>
 /// On construction, computes the current iOS version
 /// </summary>
 static MMVibrationManager()
 {
     DebugLog("[MMVibrationManager] Initialize vibration manager");
     iOSVersion = MMNViOS.ComputeiOSVersion();
 }
        /// <summary>
        /// Triggers a haptic feedback of the specified type
        /// </summary>
        /// <param name="type">Type.</param>
        public static void Haptic(HapticTypes type, bool defaultToRegularVibrate = false, bool alsoRumble = false, MonoBehaviour coroutineSupport = null, int controllerID = -1)
        {
            if (!_vibrationsActive)
            {
                return;
            }

            DebugLog("[MMVibrationManager] Regular Haptic");

            if (Android())
            {
                switch (type)
                {
                case HapticTypes.None:
                    // do nothing
                    break;

                case HapticTypes.Selection:
                    MMNVAndroid.AndroidVibrate(LightDuration, LightAmplitude);
                    break;

                case HapticTypes.Success:
                    MMNVAndroid.AndroidVibrate(_successPattern, _successPatternAmplitude, -1);
                    break;

                case HapticTypes.Warning:
                    MMNVAndroid.AndroidVibrate(_warningPattern, _warningPatternAmplitude, -1);
                    break;

                case HapticTypes.Failure:
                    MMNVAndroid.AndroidVibrate(_failurePattern, _failurePatternAmplitude, -1);
                    break;

                case HapticTypes.LightImpact:
                    MMNVAndroid.AndroidVibrate(_lightImpactPattern, _lightImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.MediumImpact:
                    MMNVAndroid.AndroidVibrate(_mediumImpactPattern, _mediumImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.HeavyImpact:
                    MMNVAndroid.AndroidVibrate(_HeavyImpactPattern, _HeavyImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.RigidImpact:
                    MMNVAndroid.AndroidVibrate(_rigidImpactPattern, _rigidImpactPatternAmplitude, -1);
                    break;

                case HapticTypes.SoftImpact:
                    MMNVAndroid.AndroidVibrate(_softImpactPattern, _softImpactPatternAmplitude, -1);
                    break;
                }
            }
            else if (iOS())
            {
                MMNViOS.iOSTriggerHaptics(type, defaultToRegularVibrate);
            }

            if (alsoRumble && (coroutineSupport != null))
            {
                #if MOREMOUNTAINS_NICEVIBRATIONS_RUMBLE
                switch (type)
                {
                case HapticTypes.None:
                    // do nothing
                    break;

                case HapticTypes.Selection:
                    MMNVRumble.Rumble(_rumbleLight.x, _rumbleMedium.y, _rumbleLight.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.Success:
                    MMNVRumble.Rumble(_successPattern, _successPatternAmplitude, -1, coroutineSupport, controllerID);
                    break;

                case HapticTypes.Warning:
                    MMNVRumble.Rumble(_warningPattern, _warningPatternAmplitude, -1, coroutineSupport, controllerID);
                    break;

                case HapticTypes.Failure:
                    MMNVRumble.Rumble(_failurePattern, _failurePatternAmplitude, -1, coroutineSupport, controllerID);
                    break;

                case HapticTypes.LightImpact:
                    MMNVRumble.Rumble(_rumbleLight.x, _rumbleLight.y, _rumbleLight.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.MediumImpact:
                    MMNVRumble.Rumble(_rumbleMedium.x, _rumbleMedium.y, _rumbleMedium.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.HeavyImpact:
                    MMNVRumble.Rumble(_rumbleHeavy.x, _rumbleHeavy.y, _rumbleHeavy.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.RigidImpact:
                    MMNVRumble.Rumble(_rumbleRigid.x, _rumbleRigid.y, _rumbleRigid.z, coroutineSupport, controllerID);
                    break;

                case HapticTypes.SoftImpact:
                    MMNVRumble.Rumble(_rumbleSoft.x, _rumbleSoft.y, _rumbleSoft.z, coroutineSupport, controllerID);
                    break;
                }
                #endif
            }
        }
 /// <summary>
 /// On Disable, we release our iOS haptics (to save memory and avoid garbage).
 /// Of course, this only needs to be done when on iOS, or targeting iOS.
 /// A test will be done and this method will do nothing if running on anything else
 /// </summary>
 protected virtual void OnDisable()
 {
     MMNViOS.iOSReleaseHaptics();
 }
 /// <summary>
 /// On Awake, we initialize our iOS haptics.
 /// Of course, this only needs to be done when on iOS, or targeting iOS.
 /// A test will be done and this method will do nothing if running on anything else
 /// </summary>
 protected virtual void Awake()
 {
     MMNViOS.iOSInitializeHaptics();
 }