Android: Implement vibrate-on-press and dualshock vibration
This commit is contained in:
parent
cf2d9b86b0
commit
c1c88eb41c
|
@ -6,6 +6,7 @@
|
||||||
#include "common/log.h"
|
#include "common/log.h"
|
||||||
#include "common/string.h"
|
#include "common/string.h"
|
||||||
#include "common/string_util.h"
|
#include "common/string_util.h"
|
||||||
|
#include "common/timer.h"
|
||||||
#include "common/timestamp.h"
|
#include "common/timestamp.h"
|
||||||
#include "core/bios.h"
|
#include "core/bios.h"
|
||||||
#include "core/cheats.h"
|
#include "core/cheats.h"
|
||||||
|
@ -39,6 +40,7 @@ static jmethodID s_EmulationActivity_method_reportMessage;
|
||||||
static jmethodID s_EmulationActivity_method_onEmulationStarted;
|
static jmethodID s_EmulationActivity_method_onEmulationStarted;
|
||||||
static jmethodID s_EmulationActivity_method_onEmulationStopped;
|
static jmethodID s_EmulationActivity_method_onEmulationStopped;
|
||||||
static jmethodID s_EmulationActivity_method_onGameTitleChanged;
|
static jmethodID s_EmulationActivity_method_onGameTitleChanged;
|
||||||
|
static jmethodID s_EmulationActivity_method_setVibration;
|
||||||
static jclass s_PatchCode_class;
|
static jclass s_PatchCode_class;
|
||||||
static jmethodID s_PatchCode_constructor;
|
static jmethodID s_PatchCode_constructor;
|
||||||
|
|
||||||
|
@ -185,6 +187,8 @@ void AndroidHostInterface::LoadAndConvertSettings()
|
||||||
&g_settings.cpu_overclock_denominator);
|
&g_settings.cpu_overclock_denominator);
|
||||||
g_settings.cpu_overclock_enable = (overclock_percent != 100);
|
g_settings.cpu_overclock_enable = (overclock_percent != 100);
|
||||||
g_settings.UpdateOverclockActive();
|
g_settings.UpdateOverclockActive();
|
||||||
|
|
||||||
|
m_vibration_enabled = m_settings_interface.GetBoolValue("Controller1", "Vibration", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::UpdateInputMap()
|
void AndroidHostInterface::UpdateInputMap()
|
||||||
|
@ -353,8 +357,13 @@ void AndroidHostInterface::EmulationThreadLoop()
|
||||||
|
|
||||||
// simulate the system if not paused
|
// simulate the system if not paused
|
||||||
if (System::IsRunning())
|
if (System::IsRunning())
|
||||||
|
{
|
||||||
System::RunFrame();
|
System::RunFrame();
|
||||||
|
|
||||||
|
if (m_vibration_enabled)
|
||||||
|
UpdateVibration();
|
||||||
|
}
|
||||||
|
|
||||||
// rendering
|
// rendering
|
||||||
{
|
{
|
||||||
DrawImGuiWindows();
|
DrawImGuiWindows();
|
||||||
|
@ -432,10 +441,21 @@ std::unique_ptr<AudioStream> AndroidHostInterface::CreateAudioStream(AudioBacken
|
||||||
return CommonHostInterface::CreateAudioStream(backend);
|
return CommonHostInterface::CreateAudioStream(backend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidHostInterface::OnSystemPaused(bool paused)
|
||||||
|
{
|
||||||
|
CommonHostInterface::OnSystemPaused(paused);
|
||||||
|
|
||||||
|
if (m_vibration_enabled)
|
||||||
|
SetVibration(false);
|
||||||
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::OnSystemDestroyed()
|
void AndroidHostInterface::OnSystemDestroyed()
|
||||||
{
|
{
|
||||||
CommonHostInterface::OnSystemDestroyed();
|
CommonHostInterface::OnSystemDestroyed();
|
||||||
ClearOSDMessages();
|
ClearOSDMessages();
|
||||||
|
|
||||||
|
if (m_vibration_enabled)
|
||||||
|
SetVibration(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidHostInterface::OnRunningGameChanged()
|
void AndroidHostInterface::OnRunningGameChanged()
|
||||||
|
@ -612,6 +632,51 @@ bool AndroidHostInterface::ImportPatchCodesFromString(const std::string& str)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidHostInterface::SetVibration(bool enabled)
|
||||||
|
{
|
||||||
|
const u64 current_time = Common::Timer::GetValue();
|
||||||
|
if (Common::Timer::ConvertValueToSeconds(current_time - m_last_vibration_update_time) < 0.1f &&
|
||||||
|
m_last_vibration_state == enabled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_last_vibration_state = enabled;
|
||||||
|
m_last_vibration_update_time = current_time;
|
||||||
|
|
||||||
|
JNIEnv* env = AndroidHelpers::GetJNIEnv();
|
||||||
|
if (m_emulation_activity_object) {
|
||||||
|
env->CallVoidMethod(m_emulation_activity_object, s_EmulationActivity_method_setVibration,
|
||||||
|
static_cast<jboolean>(enabled));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidHostInterface::UpdateVibration()
|
||||||
|
{
|
||||||
|
static constexpr float THRESHOLD = 0.5f;
|
||||||
|
|
||||||
|
bool vibration_state = false;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < NUM_CONTROLLER_AND_CARD_PORTS; i++)
|
||||||
|
{
|
||||||
|
Controller* controller = System::GetController(i);
|
||||||
|
if (!controller)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const u32 motors = controller->GetVibrationMotorCount();
|
||||||
|
for (u32 j = 0; j < motors; j++)
|
||||||
|
{
|
||||||
|
if (controller->GetVibrationMotorStrength(j) >= THRESHOLD)
|
||||||
|
{
|
||||||
|
vibration_state = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SetVibration(vibration_state);
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
{
|
{
|
||||||
Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV);
|
Log::SetDebugOutputParams(true, nullptr, LOGLEVEL_DEV);
|
||||||
|
@ -653,6 +718,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
||||||
env->GetMethodID(emulation_activity_class, "onEmulationStopped", "()V")) == nullptr ||
|
env->GetMethodID(emulation_activity_class, "onEmulationStopped", "()V")) == nullptr ||
|
||||||
(s_EmulationActivity_method_onGameTitleChanged =
|
(s_EmulationActivity_method_onGameTitleChanged =
|
||||||
env->GetMethodID(emulation_activity_class, "onGameTitleChanged", "(Ljava/lang/String;)V")) == nullptr ||
|
env->GetMethodID(emulation_activity_class, "onGameTitleChanged", "(Ljava/lang/String;)V")) == nullptr ||
|
||||||
|
(s_EmulationActivity_method_setVibration =
|
||||||
|
env->GetMethodID(emulation_activity_class, "setVibration", "(Z)V")) == nullptr ||
|
||||||
(s_PatchCode_constructor = env->GetMethodID(s_PatchCode_class, "<init>", "(ILjava/lang/String;Z)V")) == nullptr)
|
(s_PatchCode_constructor = env->GetMethodID(s_PatchCode_class, "<init>", "(ILjava/lang/String;Z)V")) == nullptr)
|
||||||
{
|
{
|
||||||
Log_ErrorPrint("AndroidHostInterface lookups failed");
|
Log_ErrorPrint("AndroidHostInterface lookups failed");
|
||||||
|
|
|
@ -65,6 +65,7 @@ protected:
|
||||||
void ReleaseHostDisplay() override;
|
void ReleaseHostDisplay() override;
|
||||||
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
std::unique_ptr<AudioStream> CreateAudioStream(AudioBackend backend) override;
|
||||||
|
|
||||||
|
void OnSystemPaused(bool paused) override;
|
||||||
void OnSystemDestroyed() override;
|
void OnSystemDestroyed() override;
|
||||||
void OnRunningGameChanged() override;
|
void OnRunningGameChanged() override;
|
||||||
|
|
||||||
|
@ -77,6 +78,8 @@ private:
|
||||||
void DestroyImGuiContext();
|
void DestroyImGuiContext();
|
||||||
|
|
||||||
void LoadAndConvertSettings();
|
void LoadAndConvertSettings();
|
||||||
|
void SetVibration(bool enabled);
|
||||||
|
void UpdateVibration();
|
||||||
|
|
||||||
jobject m_java_object = {};
|
jobject m_java_object = {};
|
||||||
jobject m_emulation_activity_object = {};
|
jobject m_emulation_activity_object = {};
|
||||||
|
@ -92,6 +95,10 @@ private:
|
||||||
|
|
||||||
std::thread m_emulation_thread;
|
std::thread m_emulation_thread;
|
||||||
std::atomic_bool m_emulation_thread_stop_request{false};
|
std::atomic_bool m_emulation_thread_stop_request{false};
|
||||||
|
|
||||||
|
u64 m_last_vibration_update_time = 0;
|
||||||
|
bool m_last_vibration_state = false;
|
||||||
|
bool m_vibration_enabled = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace AndroidHelpers {
|
namespace AndroidHelpers {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
package="com.github.stenzek.duckstation">
|
package="com.github.stenzek.duckstation">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
|
|
@ -15,6 +15,7 @@ public class AndroidHostInterface {
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
|
||||||
static public native String getScmVersion();
|
static public native String getScmVersion();
|
||||||
|
|
||||||
static public native AndroidHostInterface create(Context context, String userDirectory);
|
static public native AndroidHostInterface create(Context context, String userDirectory);
|
||||||
|
|
||||||
public AndroidHostInterface(Context context) {
|
public AndroidHostInterface(Context context) {
|
||||||
|
@ -71,7 +72,9 @@ public class AndroidHostInterface {
|
||||||
public native void setDisplayAlignment(int alignment);
|
public native void setDisplayAlignment(int alignment);
|
||||||
|
|
||||||
public native PatchCode[] getPatchCodeList();
|
public native PatchCode[] getPatchCodeList();
|
||||||
|
|
||||||
public native void setPatchCodeEnabled(int index, boolean enabled);
|
public native void setPatchCodeEnabled(int index, boolean enabled);
|
||||||
|
|
||||||
public native boolean importPatchCodesFromString(String str);
|
public native boolean importPatchCodesFromString(String str);
|
||||||
|
|
||||||
public native void addOSDMessage(String message, float duration);
|
public native void addOSDMessage(String message, float duration);
|
||||||
|
@ -81,10 +84,13 @@ public class AndroidHostInterface {
|
||||||
public native String importBIOSImage(byte[] data);
|
public native String importBIOSImage(byte[] data);
|
||||||
|
|
||||||
public native boolean isFastForwardEnabled();
|
public native boolean isFastForwardEnabled();
|
||||||
|
|
||||||
public native void setFastForwardEnabled(boolean enabled);
|
public native void setFastForwardEnabled(boolean enabled);
|
||||||
|
|
||||||
public native String[] getMediaPlaylistPaths();
|
public native String[] getMediaPlaylistPaths();
|
||||||
|
|
||||||
public native int getMediaPlaylistIndex();
|
public native int getMediaPlaylistIndex();
|
||||||
|
|
||||||
public native boolean setMediaPlaylistIndex(int index);
|
public native boolean setMediaPlaylistIndex(int index);
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import android.content.res.Configuration;
|
||||||
import android.hardware.input.InputManager;
|
import android.hardware.input.InputManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Vibrator;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -124,6 +125,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
private void doApplySettings() {
|
private void doApplySettings() {
|
||||||
AndroidHostInterface.getInstance().applySettings();
|
AndroidHostInterface.getInstance().applySettings();
|
||||||
updateRequestedOrientation();
|
updateRequestedOrientation();
|
||||||
|
updateControllers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySettings() {
|
private void applySettings() {
|
||||||
|
@ -163,9 +165,6 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
doApplySettings();
|
doApplySettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AndroidHostInterface.getInstance().isEmulationThreadPaused())
|
|
||||||
AndroidHostInterface.getInstance().pauseEmulationThread(false);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,8 +322,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
|
|
||||||
private void showMenu() {
|
private void showMenu() {
|
||||||
if (getBooleanSetting("Main/PauseOnMenu", false) &&
|
if (getBooleanSetting("Main/PauseOnMenu", false) &&
|
||||||
!AndroidHostInterface.getInstance().isEmulationThreadPaused())
|
!AndroidHostInterface.getInstance().isEmulationThreadPaused()) {
|
||||||
{
|
|
||||||
AndroidHostInterface.getInstance().pauseEmulationThread(true);
|
AndroidHostInterface.getInstance().pauseEmulationThread(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -485,8 +483,7 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
private void showDiscChangeMenu() {
|
private void showDiscChangeMenu() {
|
||||||
final String[] paths = AndroidHostInterface.getInstance().getMediaPlaylistPaths();
|
final String[] paths = AndroidHostInterface.getInstance().getMediaPlaylistPaths();
|
||||||
final int currentPath = AndroidHostInterface.getInstance().getMediaPlaylistIndex();
|
final int currentPath = AndroidHostInterface.getInstance().getMediaPlaylistIndex();
|
||||||
if (paths == null)
|
if (paths == null) {
|
||||||
{
|
|
||||||
onMenuClosed();
|
onMenuClosed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -515,6 +512,8 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
final String controllerType = getStringSetting("Controller1/Type", "DigitalController");
|
final String controllerType = getStringSetting("Controller1/Type", "DigitalController");
|
||||||
final String viewType = getStringSetting("Controller1/TouchscreenControllerView", "digital");
|
final String viewType = getStringSetting("Controller1/TouchscreenControllerView", "digital");
|
||||||
final boolean autoHideTouchscreenController = getBooleanSetting("Controller1/AutoHideTouchscreenController", false);
|
final boolean autoHideTouchscreenController = getBooleanSetting("Controller1/AutoHideTouchscreenController", false);
|
||||||
|
final boolean hapticFeedback = getBooleanSetting("Controller1/HapticFeedback", false);
|
||||||
|
final boolean vibration = getBooleanSetting("Controller1/Vibration", false);
|
||||||
final FrameLayout activityLayout = findViewById(R.id.frameLayout);
|
final FrameLayout activityLayout = findViewById(R.id.frameLayout);
|
||||||
|
|
||||||
Log.i("EmulationActivity", "Controller type: " + controllerType);
|
Log.i("EmulationActivity", "Controller type: " + controllerType);
|
||||||
|
@ -526,14 +525,18 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
if (mTouchscreenController != null) {
|
if (mTouchscreenController != null) {
|
||||||
activityLayout.removeView(mTouchscreenController);
|
activityLayout.removeView(mTouchscreenController);
|
||||||
mTouchscreenController = null;
|
mTouchscreenController = null;
|
||||||
|
mVibratorService = null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mTouchscreenController == null) {
|
if (mTouchscreenController == null) {
|
||||||
mTouchscreenController = new TouchscreenControllerView(this);
|
mTouchscreenController = new TouchscreenControllerView(this);
|
||||||
|
if (vibration)
|
||||||
|
mVibratorService = (Vibrator) getSystemService(VIBRATOR_SERVICE);
|
||||||
|
|
||||||
activityLayout.addView(mTouchscreenController);
|
activityLayout.addView(mTouchscreenController);
|
||||||
}
|
}
|
||||||
|
|
||||||
mTouchscreenController.init(0, controllerType, viewType);
|
mTouchscreenController.init(0, controllerType, viewType, hapticFeedback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -578,4 +581,21 @@ public class EmulationActivity extends AppCompatActivity implements SurfaceHolde
|
||||||
|
|
||||||
mInputDeviceListener = null;
|
mInputDeviceListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Vibrator mVibratorService;
|
||||||
|
|
||||||
|
public void setVibration(boolean enabled) {
|
||||||
|
if (mVibratorService == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
runOnUiThread(() -> {
|
||||||
|
if (mVibratorService == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (enabled)
|
||||||
|
mVibratorService.vibrate(1000);
|
||||||
|
else
|
||||||
|
mVibratorService.cancel();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,19 +9,14 @@ import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManager;
|
||||||
import android.provider.DocumentsContract;
|
import android.provider.DocumentsContract;
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.appcompat.app.AlertDialog;
|
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.StringWriter;
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
|
@ -73,7 +73,9 @@ public class GameListEntry {
|
||||||
return mTitle;
|
return mTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFileTitle() { return mFileTitle; }
|
public String getFileTitle() {
|
||||||
|
return mFileTitle;
|
||||||
|
}
|
||||||
|
|
||||||
public String getModifiedTime() {
|
public String getModifiedTime() {
|
||||||
return mModifiedTime;
|
return mModifiedTime;
|
||||||
|
|
|
@ -5,6 +5,7 @@ import android.content.res.TypedArray;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.view.HapticFeedbackConstants;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -14,6 +15,7 @@ public class TouchscreenControllerButtonView extends View {
|
||||||
private Drawable mUnpressedDrawable;
|
private Drawable mUnpressedDrawable;
|
||||||
private Drawable mPressedDrawable;
|
private Drawable mPressedDrawable;
|
||||||
private boolean mPressed = false;
|
private boolean mPressed = false;
|
||||||
|
private boolean mHapticFeedback = false;
|
||||||
private int mControllerIndex = -1;
|
private int mControllerIndex = -1;
|
||||||
private int mButtonCode = -1;
|
private int mButtonCode = -1;
|
||||||
|
|
||||||
|
@ -81,6 +83,10 @@ public class TouchscreenControllerButtonView extends View {
|
||||||
mPressed = pressed;
|
mPressed = pressed;
|
||||||
invalidate();
|
invalidate();
|
||||||
updateControllerState();
|
updateControllerState();
|
||||||
|
|
||||||
|
if (mHapticFeedback) {
|
||||||
|
performHapticFeedback(pressed ? HapticFeedbackConstants.VIRTUAL_KEY : HapticFeedbackConstants.VIRTUAL_KEY_RELEASE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setButtonCode(int controllerIndex, int code) {
|
public void setButtonCode(int controllerIndex, int code) {
|
||||||
|
@ -88,6 +94,10 @@ public class TouchscreenControllerButtonView extends View {
|
||||||
mButtonCode = code;
|
mButtonCode = code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHapticFeedback(boolean enabled) {
|
||||||
|
mHapticFeedback = enabled;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateControllerState() {
|
private void updateControllerState() {
|
||||||
if (mButtonCode >= 0)
|
if (mButtonCode >= 0)
|
||||||
AndroidHostInterface.getInstance().setControllerButtonState(mControllerIndex, mButtonCode, mPressed);
|
AndroidHostInterface.getInstance().setControllerButtonState(mControllerIndex, mButtonCode, mPressed);
|
||||||
|
|
|
@ -20,6 +20,7 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||||
private View mMainView;
|
private View mMainView;
|
||||||
private ArrayList<TouchscreenControllerButtonView> mButtonViews = new ArrayList<>();
|
private ArrayList<TouchscreenControllerButtonView> mButtonViews = new ArrayList<>();
|
||||||
private ArrayList<TouchscreenControllerAxisView> mAxisViews = new ArrayList<>();
|
private ArrayList<TouchscreenControllerAxisView> mAxisViews = new ArrayList<>();
|
||||||
|
private boolean mHapticFeedback;
|
||||||
|
|
||||||
public TouchscreenControllerView(Context context) {
|
public TouchscreenControllerView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -33,9 +34,10 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(int controllerIndex, String controllerType, String viewType) {
|
public void init(int controllerIndex, String controllerType, String viewType, boolean hapticFeedback) {
|
||||||
mControllerIndex = controllerIndex;
|
mControllerIndex = controllerIndex;
|
||||||
mControllerType = controllerType;
|
mControllerType = controllerType;
|
||||||
|
mHapticFeedback = hapticFeedback;
|
||||||
|
|
||||||
mButtonViews.clear();
|
mButtonViews.clear();
|
||||||
mAxisViews.clear();
|
mAxisViews.clear();
|
||||||
|
@ -99,6 +101,7 @@ public class TouchscreenControllerView extends FrameLayout {
|
||||||
|
|
||||||
if (code >= 0) {
|
if (code >= 0) {
|
||||||
buttonView.setButtonCode(mControllerIndex, code);
|
buttonView.setButtonCode(mControllerIndex, code);
|
||||||
|
buttonView.setHapticFeedback(mHapticFeedback);
|
||||||
mButtonViews.add(buttonView);
|
mButtonViews.add(buttonView);
|
||||||
} else {
|
} else {
|
||||||
Log.e("TouchscreenController", String.format("Unknown button name '%s' " +
|
Log.e("TouchscreenController", String.format("Unknown button name '%s' " +
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="CPU/Overclock"
|
app:key="CPU/Overclock"
|
||||||
app:title="CPU Overclocking"
|
app:title="CPU Overclocking"
|
||||||
|
@ -80,7 +79,7 @@
|
||||||
app:title="Log To File"
|
app:title="Log To File"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
app:summary="Writes log messages to duckstation.log in your user directory. Only use for debugging as it slows down emulation."
|
app:summary="Writes log messages to duckstation.log in your user directory. Only use for debugging as it slows down emulation."
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false" />
|
||||||
<SwitchPreferenceCompat
|
<SwitchPreferenceCompat
|
||||||
app:key="Logging/LogToDebug"
|
app:key="Logging/LogToDebug"
|
||||||
app:title="Log To Logcat"
|
app:title="Log To Logcat"
|
||||||
|
|
|
@ -45,7 +45,7 @@
|
||||||
app:entryValues="@array/settings_audio_backend_values"
|
app:entryValues="@array/settings_audio_backend_values"
|
||||||
app:defaultValue="OpenSLES"
|
app:defaultValue="OpenSLES"
|
||||||
app:useSimpleSummaryProvider="true"
|
app:useSimpleSummaryProvider="true"
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false" />
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="Audio/BufferSize"
|
app:key="Audio/BufferSize"
|
||||||
app:title="Audio Buffer Size"
|
app:title="Audio Buffer Size"
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="Controller1/Type"
|
app:key="Controller1/Type"
|
||||||
|
@ -43,7 +42,20 @@
|
||||||
app:title="Auto-Hide Touchscreen Controller"
|
app:title="Auto-Hide Touchscreen Controller"
|
||||||
app:defaultValue="false"
|
app:defaultValue="false"
|
||||||
app:summary="Hides the touchscreen controller when an external controller is detected."
|
app:summary="Hides the touchscreen controller when an external controller is detected."
|
||||||
app:iconSpaceReserved="false"/>
|
app:iconSpaceReserved="false" />
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
app:key="Controller1/HapticFeedback"
|
||||||
|
app:title="Vibrate On Press"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:summary="Enables a short vibration when a touchscreen button is pressed. Requires "Vibrate on Touch" to be enabled on your device."
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
|
<SwitchPreferenceCompat
|
||||||
|
app:key="Controller1/Vibration"
|
||||||
|
app:title="Enable Vibration"
|
||||||
|
app:defaultValue="false"
|
||||||
|
app:summary="Forwards rumble from the game to the phone's vibration motor."
|
||||||
|
app:iconSpaceReserved="false" />
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="MemoryCards/Card1Type"
|
app:key="MemoryCards/Card1Type"
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="Display/CropMode"
|
app:key="Display/CropMode"
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="CDROM/ReadSpeedup"
|
app:key="CDROM/ReadSpeedup"
|
||||||
app:title="CD-ROM Read Speedup"
|
app:title="CD-ROM Read Speedup"
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
~ limitations under the License.
|
~ limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<ListPreference
|
<ListPreference
|
||||||
app:key="Main/EmulationSpeed"
|
app:key="Main/EmulationSpeed"
|
||||||
|
|
Loading…
Reference in New Issue