using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Runtime.InteropServices;
using System;
namespace MoreMountains.NiceVibrations
{
///
/// This class handles all Android haptics specific calls
///
public static class MMNVAndroid
{
///
/// A struct used to pass data to threaded calls
///
public struct MMNVAndroidVibrateThreadData
{
public long[] Pattern;
public int[] Amplitudes;
public int Repeat;
public MMNVAndroidVibrateThreadData(long[] pattern, int[] amplitudes, int repeat)
{
Pattern = pattern;
Amplitudes = amplitudes;
Repeat = repeat;
}
}
private static MMNVAltThread _androidVibrateThread;
private static MMNVAndroidVibrateThreadData _androidVibrateThreadData;
private static int _sdkVersion = -1;
#if UNITY_ANDROID && !UNITY_EDITOR
private static AndroidJavaClass UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
private static AndroidJavaObject CurrentActivity = UnityPlayer.GetStatic("currentActivity");
private static AndroidJavaObject AndroidVibrator = CurrentActivity.Call("getSystemService", "vibrator");
private static AndroidJavaClass VibrationEffectClass;
private static AndroidJavaObject VibrationEffect;
private static int DefaultAmplitude;
private static IntPtr AndroidVibrateMethodRawClass = AndroidJNIHelper.GetMethodID(AndroidVibrator.GetRawClass(), "vibrate", "(J)V", false);
private static jvalue[] AndroidVibrateMethodRawClassParameters = new jvalue[1];
#else
private static AndroidJavaClass UnityPlayer;
private static AndroidJavaObject CurrentActivity;
private static AndroidJavaObject AndroidVibrator = null;
private static AndroidJavaClass VibrationEffectClass = null;
private static AndroidJavaObject VibrationEffect;
private static int DefaultAmplitude;
private static IntPtr AndroidVibrateMethodRawClass = IntPtr.Zero;
private static jvalue[] AndroidVibrateMethodRawClassParameters = null;
#endif
///
/// Requests a default vibration on Android, for the specified duration, in milliseconds
///
/// Milliseconds.
public static void AndroidVibrate(long milliseconds)
{
if (!MMNVPlatform.Android()) { return; }
AndroidVibrateMethodRawClassParameters[0].j = milliseconds;
AndroidJNI.CallVoidMethod(AndroidVibrator.GetRawObject(), AndroidVibrateMethodRawClass, AndroidVibrateMethodRawClassParameters);
}
///
/// Requests a vibration of the specified amplitude and duration. If amplitude is not supported by the device's SDK, a default vibration will be requested
///
/// Milliseconds.
/// Amplitude.
public static void AndroidVibrate(long milliseconds, int amplitude)
{
if (!MMNVPlatform.Android()) { return; }
// amplitude is only supported after API26
if ((AndroidSDKVersion() < 26))
{
AndroidVibrate(milliseconds);
}
else
{
AndroidVibrationEffectClassInitialization();
VibrationEffect = VibrationEffectClass.CallStatic("createOneShot", new object[] { milliseconds, amplitude });
AndroidVibrator.Call("vibrate", VibrationEffect);
}
}
// Requests a vibration on Android for the specified pattern and optional repeat
// Straight out of the Android documentation :
// 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.
// repeat: the index into pattern at which to repeat, or -1 if you don't want to repeat.
public static void AndroidVibrate(long[] pattern, int repeat)
{
if (!MMNVPlatform.Android()) { return; }
if (pattern == null)
{
return;
}
if ((AndroidSDKVersion() < 26))
{
AndroidVibrator.Call("vibrate", pattern, repeat);
}
else
{
AndroidVibrationEffectClassInitialization();
try
{
VibrationEffect = VibrationEffectClass.CallStatic("createWaveform", new object[] { pattern, repeat });
AndroidVibrator.Call("vibrate", VibrationEffect);
}
catch (Exception e)
{
Debug.LogException(e);
}
}
}
///
/// Requests a vibration on Android for the specified pattern, amplitude and optional repeat
///
/// Pattern.
/// Amplitudes.
/// Repeat : -1 : no repeat, 0 : infinite, 1 : repeat once, 2 : repeat twice, etc
public static void AndroidVibrate(long[] pattern, int[] amplitudes, int repeat, bool threaded = false)
{
if (!MMNVPlatform.Android()) { return; }
if ((pattern == null) || (amplitudes == null))
{
return;
}
if ((pattern.Length == 0) || (amplitudes.Length == 0))
{
return;
}
if ((AndroidSDKVersion() < 26))
{
AndroidVibrator.Call("vibrate", pattern, repeat);
}
else
{
if (threaded)
{
if (_androidVibrateThread == null)
{
_androidVibrateThread = new MMNVAltThread();
_androidVibrateThreadData = new MMNVAndroidVibrateThreadData();
}
_androidVibrateThreadData.Pattern = pattern;
_androidVibrateThreadData.Amplitudes = amplitudes;
_androidVibrateThreadData.Repeat = repeat;
_androidVibrateThread.Run(AndroidVibrateThread, _androidVibrateThreadData);
}
else
{
AndroidVibrateNoThread(pattern, amplitudes, repeat);
}
}
}
///
/// An internal method used to vibrate on a pattern/amplitude model using a secondary thread
/// If you use that, you should call MMNVAndroid.ClearThreads() on exit (where exactly depends on your game, but OnDisable should cover most cases)
///
///
private static void AndroidVibrateThread(MMNVAndroidVibrateThreadData threadData)
{
AndroidJNI.AttachCurrentThread();
AndroidVibrateNoThread(threadData.Pattern, threadData.Amplitudes, threadData.Repeat);
AndroidJNI.DetachCurrentThread();
}
///
/// An internal method used to vibrate on a pattern/amplitude model using the main thread
///
///
private static void AndroidVibrateNoThread(long[] pattern, int[] amplitudes, int repeat)
{
if ((pattern == null) || (amplitudes == null))
{
return;
}
AndroidVibrationEffectClassInitialization();
VibrationEffect = VibrationEffectClass.CallStatic("createWaveform", new object[] { pattern, amplitudes, repeat });
AndroidVibrator.Call("vibrate", VibrationEffect);
}
///
/// A method to call once you're done with haptics on Android, usually OnDisable, to avoid memory leaks
///
public static void ClearThreads()
{
_androidVibrateThread = null;
_androidVibrateThread?.CloseThread();
}
///
/// Stops all Android vibrations that may be active
///
public static void AndroidCancelVibrations()
{
if (!MMNVPlatform.Android()) { return; }
AndroidVibrator.Call("cancel");
}
///
/// Returns true if the device running the game has vibrations
///
///
public static bool AndroidHasVibrator()
{
if (!MMNVPlatform.Android()) { return false; }
return AndroidVibrator.Call("hasVibrator");
}
///
/// Returns true if the device running the game has amplitude control
///
///
public static bool AndroidHasAmplitudeControl()
{
if ((AndroidSDKVersion() < 26))
{
return false;
}
if (!MMNVPlatform.Android()) { return false; }
return AndroidVibrator.Call("hasAmplitudeControl");
}
///
/// Initializes the VibrationEffectClass if needed.
///
private static void AndroidVibrationEffectClassInitialization()
{
if (VibrationEffectClass == null)
{
VibrationEffectClass = new AndroidJavaClass("android.os.VibrationEffect");
}
}
///
/// Returns the current Android SDK version as an int
///
/// The SDK version.
public static int AndroidSDKVersion()
{
if (_sdkVersion == -1)
{
int apiLevel = int.Parse(SystemInfo.operatingSystem.Substring(SystemInfo.operatingSystem.IndexOf("-") + 1, 3));
_sdkVersion = apiLevel;
return apiLevel;
}
else
{
return _sdkVersion;
}
}
}
}