Browse Source

Support vibration for Android and iOS

volzhs 6 years ago
parent
commit
4061e5bb75

+ 1 - 0
core/os/input.cpp

@@ -80,6 +80,7 @@ void Input::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
 	ClassDB::bind_method(D_METHOD("get_joy_axis_index_from_string", "axis"), &Input::get_joy_axis_index_from_string);
 	ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("start_joy_vibration", "device", "weak_magnitude", "strong_magnitude", "duration"), &Input::start_joy_vibration, DEFVAL(0));
 	ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
 	ClassDB::bind_method(D_METHOD("stop_joy_vibration", "device"), &Input::stop_joy_vibration);
+	ClassDB::bind_method(D_METHOD("vibrate_handheld", "duration_ms"), &Input::vibrate_handheld, DEFVAL(500));
 	ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
 	ClassDB::bind_method(D_METHOD("get_gravity"), &Input::get_gravity);
 	ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
 	ClassDB::bind_method(D_METHOD("get_accelerometer"), &Input::get_accelerometer);
 	ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);
 	ClassDB::bind_method(D_METHOD("get_magnetometer"), &Input::get_magnetometer);

+ 1 - 0
core/os/input.h

@@ -100,6 +100,7 @@ public:
 	virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0;
 	virtual uint64_t get_joy_vibration_timestamp(int p_device) = 0;
 	virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0;
 	virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0) = 0;
 	virtual void stop_joy_vibration(int p_device) = 0;
 	virtual void stop_joy_vibration(int p_device) = 0;
+	virtual void vibrate_handheld(int p_duration_ms = 500) = 0;
 
 
 	virtual Point2 get_mouse_position() const = 0;
 	virtual Point2 get_mouse_position() const = 0;
 	virtual Point2 get_last_mouse_speed() const = 0;
 	virtual Point2 get_last_mouse_speed() const = 0;

+ 5 - 0
core/os/os.cpp

@@ -186,6 +186,11 @@ int OS::get_process_id() const {
 	return -1;
 	return -1;
 };
 };
 
 
+void OS::vibrate_handheld(int p_duration_ms) {
+
+	WARN_PRINTS("vibrate_handheld() only works with Android and iOS");
+}
+
 bool OS::is_stdout_verbose() const {
 bool OS::is_stdout_verbose() const {
 
 
 	return _verbose_stdout;
 	return _verbose_stdout;

+ 1 - 0
core/os/os.h

@@ -265,6 +265,7 @@ public:
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL) = 0;
 	virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false, Mutex *p_pipe_mutex = NULL) = 0;
 	virtual Error kill(const ProcessID &p_pid) = 0;
 	virtual Error kill(const ProcessID &p_pid) = 0;
 	virtual int get_process_id() const;
 	virtual int get_process_id() const;
+	virtual void vibrate_handheld(int p_duration_ms = 500);
 
 
 	virtual Error shell_open(String p_uri);
 	virtual Error shell_open(String p_uri);
 	virtual Error set_cwd(const String &p_cwd);
 	virtual Error set_cwd(const String &p_cwd);

+ 10 - 0
doc/classes/Input.xml

@@ -364,6 +364,16 @@
 				Stops the vibration of the joypad.
 				Stops the vibration of the joypad.
 			</description>
 			</description>
 		</method>
 		</method>
+		<method name="vibrate_handheld">
+			<return type="void">
+			</return>
+			<argument index="0" name="duration_ms" type="int" default="500">
+			</argument>
+			<description>
+				Vibrate Android and iOS devices.
+				[b]Note:[/b] It needs VIBRATE permission for Android at export settings. iOS does not support duration.
+			</description>
+		</method>
 		<method name="warp_mouse_position">
 		<method name="warp_mouse_position">
 			<return type="void">
 			<return type="void">
 			</return>
 			</return>

+ 4 - 0
main/input_default.cpp

@@ -472,6 +472,10 @@ void InputDefault::stop_joy_vibration(int p_device) {
 	joy_vibration[p_device] = vibration;
 	joy_vibration[p_device] = vibration;
 }
 }
 
 
+void InputDefault::vibrate_handheld(int p_duration_ms) {
+	OS::get_singleton()->vibrate_handheld(p_duration_ms);
+}
+
 void InputDefault::set_gravity(const Vector3 &p_gravity) {
 void InputDefault::set_gravity(const Vector3 &p_gravity) {
 
 
 	_THREAD_SAFE_METHOD_
 	_THREAD_SAFE_METHOD_

+ 1 - 0
main/input_default.h

@@ -226,6 +226,7 @@ public:
 
 
 	virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0);
 	virtual void start_joy_vibration(int p_device, float p_weak_magnitude, float p_strong_magnitude, float p_duration = 0);
 	virtual void stop_joy_vibration(int p_device);
 	virtual void stop_joy_vibration(int p_device);
+	virtual void vibrate_handheld(int p_duration_ms = 500);
 
 
 	void set_main_loop(MainLoop *p_main_loop);
 	void set_main_loop(MainLoop *p_main_loop);
 	void set_mouse_position(const Point2 &p_posf);
 	void set_mouse_position(const Point2 &p_posf);

+ 18 - 0
platform/android/java/src/org/godotengine/godot/Godot.java

@@ -56,6 +56,7 @@ import android.os.Build;
 import android.os.Bundle;
 import android.os.Bundle;
 import android.os.Environment;
 import android.os.Environment;
 import android.os.Messenger;
 import android.os.Messenger;
+import android.os.Vibrator;
 import android.provider.Settings.Secure;
 import android.provider.Settings.Secure;
 import android.support.v4.content.ContextCompat;
 import android.support.v4.content.ContextCompat;
 import android.view.Display;
 import android.view.Display;
@@ -98,6 +99,7 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 	static final int MAX_SINGLETONS = 64;
 	static final int MAX_SINGLETONS = 64;
 	static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
 	static final int REQUEST_RECORD_AUDIO_PERMISSION = 1;
 	static final int REQUEST_CAMERA_PERMISSION = 2;
 	static final int REQUEST_CAMERA_PERMISSION = 2;
+	static final int REQUEST_VIBRATE_PERMISSION = 3;
 	private IStub mDownloaderClientStub;
 	private IStub mDownloaderClientStub;
 	private IDownloaderService mRemoteService;
 	private IDownloaderService mRemoteService;
 	private TextView mStatusText;
 	private TextView mStatusText;
@@ -324,6 +326,15 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 		});
 		});
 	}
 	}
 
 
+	public void vibrate(int p_duration_ms) {
+		if (requestPermission("VIBRATE")) {
+			Vibrator v = (Vibrator)getSystemService(Context.VIBRATOR_SERVICE);
+			if (v != null) {
+				v.vibrate(p_duration_ms);
+			}
+		}
+	}
+
 	public void restart() {
 	public void restart() {
 		// HACK:
 		// HACK:
 		//
 		//
@@ -985,6 +996,13 @@ public class Godot extends Activity implements SensorEventListener, IDownloaderC
 				return false;
 				return false;
 			}
 			}
 		}
 		}
+
+		if (p_name.equals("VIBRATE")) {
+			if (ContextCompat.checkSelfPermission(this, Manifest.permission.VIBRATE) != PackageManager.PERMISSION_GRANTED) {
+				requestPermissions(new String[] { Manifest.permission.VIBRATE }, REQUEST_VIBRATE_PERMISSION);
+				return false;
+			}
+		}
 		return true;
 		return true;
 	}
 	}
 
 

+ 8 - 0
platform/android/java_godot_wrapper.cpp

@@ -62,6 +62,7 @@ GodotJavaWrapper::GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance) {
 	_init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V");
 	_init_input_devices = p_env->GetMethodID(cls, "initInputDevices", "()V");
 	_get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;");
 	_get_surface = p_env->GetMethodID(cls, "getSurface", "()Landroid/view/Surface;");
 	_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
 	_is_activity_resumed = p_env->GetMethodID(cls, "isActivityResumed", "()Z");
+	_vibrate = p_env->GetMethodID(cls, "vibrate", "(I)V");
 }
 }
 
 
 GodotJavaWrapper::~GodotJavaWrapper() {
 GodotJavaWrapper::~GodotJavaWrapper() {
@@ -211,3 +212,10 @@ bool GodotJavaWrapper::is_activity_resumed() {
 		return false;
 		return false;
 	}
 	}
 }
 }
+
+void GodotJavaWrapper::vibrate(int p_duration_ms) {
+	if (_vibrate) {
+		JNIEnv *env = ThreadAndroid::get_env();
+		env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
+	}
+}

+ 2 - 0
platform/android/java_godot_wrapper.h

@@ -57,6 +57,7 @@ private:
 	jmethodID _init_input_devices = 0;
 	jmethodID _init_input_devices = 0;
 	jmethodID _get_surface = 0;
 	jmethodID _get_surface = 0;
 	jmethodID _is_activity_resumed = 0;
 	jmethodID _is_activity_resumed = 0;
+	jmethodID _vibrate = 0;
 
 
 public:
 public:
 	GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
 	GodotJavaWrapper(JNIEnv *p_env, jobject p_godot_instance);
@@ -82,6 +83,7 @@ public:
 	void init_input_devices();
 	void init_input_devices();
 	jobject get_surface();
 	jobject get_surface();
 	bool is_activity_resumed();
 	bool is_activity_resumed();
+	void vibrate(int p_duration_ms);
 };
 };
 
 
 #endif /* !JAVA_GODOT_WRAPPER_H */
 #endif /* !JAVA_GODOT_WRAPPER_H */

+ 4 - 0
platform/android/os_android.cpp

@@ -700,6 +700,10 @@ String OS_Android::get_joy_guid(int p_device) const {
 	return input->get_joy_guid_remapped(p_device);
 	return input->get_joy_guid_remapped(p_device);
 }
 }
 
 
+void OS_Android::vibrate_handheld(int p_duration_ms) {
+	godot_java->vibrate(p_duration_ms);
+}
+
 bool OS_Android::_check_internal_feature_support(const String &p_feature) {
 bool OS_Android::_check_internal_feature_support(const String &p_feature) {
 	if (p_feature == "mobile") {
 	if (p_feature == "mobile") {
 		//TODO support etc2 only if GLES3 driver is selected
 		//TODO support etc2 only if GLES3 driver is selected

+ 1 - 0
platform/android/os_android.h

@@ -198,6 +198,7 @@ public:
 	virtual bool is_joy_known(int p_device);
 	virtual bool is_joy_known(int p_device);
 	virtual String get_joy_guid(int p_device) const;
 	virtual String get_joy_guid(int p_device) const;
 	void joy_connection_changed(int p_device, bool p_connected, String p_name);
 	void joy_connection_changed(int p_device, bool p_connected, String p_name);
+	void vibrate_handheld(int p_duration_ms);
 
 
 	virtual bool _check_internal_feature_support(const String &p_feature);
 	virtual bool _check_internal_feature_support(const String &p_feature);
 	OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);
 	OS_Android(GodotJavaWrapper *p_godot_java, GodotIOJavaWrapper *p_godot_io_java, bool p_use_apk_expansion);

+ 5 - 0
platform/iphone/app_delegate.mm

@@ -37,6 +37,7 @@
 #include "os_iphone.h"
 #include "os_iphone.h"
 
 
 #import "GameController/GameController.h"
 #import "GameController/GameController.h"
+#import <AudioToolbox/AudioServices.h>
 
 
 #define kFilteringFactor 0.1
 #define kFilteringFactor 0.1
 #define kRenderingFrequency 60
 #define kRenderingFrequency 60
@@ -61,6 +62,10 @@ void _set_keep_screen_on(bool p_enabled) {
 	[[UIApplication sharedApplication] setIdleTimerDisabled:(BOOL)p_enabled];
 	[[UIApplication sharedApplication] setIdleTimerDisabled:(BOOL)p_enabled];
 };
 };
 
 
+void _vibrate() {
+	AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
+};
+
 @implementation AppDelegate
 @implementation AppDelegate
 
 
 @synthesize window;
 @synthesize window;

+ 6 - 0
platform/iphone/os_iphone.cpp

@@ -470,6 +470,7 @@ extern void _show_keyboard(String p_existing);
 extern void _hide_keyboard();
 extern void _hide_keyboard();
 extern Error _shell_open(String p_uri);
 extern Error _shell_open(String p_uri);
 extern void _set_keep_screen_on(bool p_enabled);
 extern void _set_keep_screen_on(bool p_enabled);
+extern void _vibrate();
 
 
 void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect) {
 void OSIPhone::show_virtual_keyboard(const String &p_existing_text, const Rect2 &p_screen_rect) {
 	_show_keyboard(p_existing_text);
 	_show_keyboard(p_existing_text);
@@ -585,6 +586,11 @@ void OSIPhone::native_video_stop() {
 		_stop_video();
 		_stop_video();
 }
 }
 
 
+void OSIPhone::vibrate_handheld(int p_duration_ms) {
+	// iOS does not support duration for vibration
+	_vibrate();
+}
+
 bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
 bool OSIPhone::_check_internal_feature_support(const String &p_feature) {
 
 
 	return p_feature == "mobile";
 	return p_feature == "mobile";

+ 1 - 0
platform/iphone/os_iphone.h

@@ -195,6 +195,7 @@ public:
 	virtual void native_video_unpause();
 	virtual void native_video_unpause();
 	virtual void native_video_focus_out();
 	virtual void native_video_focus_out();
 	virtual void native_video_stop();
 	virtual void native_video_stop();
+	virtual void vibrate_handheld(int p_duration_ms = 500);
 
 
 	virtual bool _check_internal_feature_support(const String &p_feature);
 	virtual bool _check_internal_feature_support(const String &p_feature);
 	OSIPhone(int width, int height, String p_data_dir);
 	OSIPhone(int width, int height, String p_data_dir);