mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2024-11-26 15:45:42 +00:00
Android: Add hotkey binding support
This commit is contained in:
parent
2672e2b505
commit
22bb64e7b0
|
@ -1170,6 +1170,43 @@ DEFINE_JNI_ARGS_METHOD(jarray, AndroidHostInterface_getGameListEntries, jobject
|
||||||
return entry_array;
|
return entry_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DEFINE_JNI_ARGS_METHOD(jobjectArray , AndroidHostInterface_getHotkeyInfoList, jobject obj)
|
||||||
|
{
|
||||||
|
jclass entry_class = env->FindClass("com/github/stenzek/duckstation/HotkeyInfo");
|
||||||
|
Assert(entry_class != nullptr);
|
||||||
|
|
||||||
|
jmethodID entry_constructor =
|
||||||
|
env->GetMethodID(entry_class, "<init>",
|
||||||
|
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
||||||
|
Assert(entry_constructor != nullptr);
|
||||||
|
|
||||||
|
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||||
|
const CommonHostInterface::HotkeyInfoList& hotkeys = hi->GetHotkeyInfoList();
|
||||||
|
if (hotkeys.empty())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
jobjectArray entry_array = env->NewObjectArray(static_cast<jsize>(hotkeys.size()), entry_class, nullptr);
|
||||||
|
Assert(entry_array != nullptr);
|
||||||
|
|
||||||
|
u32 counter = 0;
|
||||||
|
for (const CommonHostInterface::HotkeyInfo& hk : hotkeys)
|
||||||
|
{
|
||||||
|
jstring category = env->NewStringUTF(hk.category.GetCharArray());
|
||||||
|
jstring name = env->NewStringUTF(hk.name.GetCharArray());
|
||||||
|
jstring display_name = env->NewStringUTF(hk.display_name.GetCharArray());
|
||||||
|
|
||||||
|
jobject entry_jobject = env->NewObject(entry_class, entry_constructor, category, name, display_name);
|
||||||
|
|
||||||
|
env->SetObjectArrayElement(entry_array, counter++, entry_jobject);
|
||||||
|
env->DeleteLocalRef(entry_jobject);
|
||||||
|
env->DeleteLocalRef(display_name);
|
||||||
|
env->DeleteLocalRef(name);
|
||||||
|
env->DeleteLocalRef(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry_array;
|
||||||
|
}
|
||||||
|
|
||||||
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_applySettings, jobject obj)
|
DEFINE_JNI_ARGS_METHOD(void, AndroidHostInterface_applySettings, jobject obj)
|
||||||
{
|
{
|
||||||
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
AndroidHostInterface* hi = AndroidHelpers::GetNativeClass(env, obj);
|
||||||
|
|
|
@ -81,6 +81,8 @@ public class AndroidHostInterface {
|
||||||
public native boolean loadInputProfile(String name);
|
public native boolean loadInputProfile(String name);
|
||||||
public native boolean saveInputProfile(String name);
|
public native boolean saveInputProfile(String name);
|
||||||
|
|
||||||
|
public native HotkeyInfo[] getHotkeyInfoList();
|
||||||
|
|
||||||
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase, AndroidProgressCallback progressCallback);
|
public native void refreshGameList(boolean invalidateCache, boolean invalidateDatabase, AndroidProgressCallback progressCallback);
|
||||||
|
|
||||||
public native GameListEntry[] getGameListEntries();
|
public native GameListEntry[] getGameListEntries();
|
||||||
|
|
|
@ -14,10 +14,16 @@ import androidx.preference.PreferenceViewHolder;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class ControllerBindingPreference extends Preference {
|
public class ControllerBindingPreference extends Preference {
|
||||||
private boolean mIsAxis;
|
private enum Type {
|
||||||
|
BUTTON,
|
||||||
|
AXIS,
|
||||||
|
HOTKEY
|
||||||
|
}
|
||||||
|
|
||||||
private String mBindingName;
|
private String mBindingName;
|
||||||
private String mValue;
|
private String mValue;
|
||||||
private TextView mValueView;
|
private TextView mValueView;
|
||||||
|
private Type mType = Type.BUTTON;
|
||||||
|
|
||||||
private static int getIconForButton(String buttonName) {
|
private static int getIconForButton(String buttonName) {
|
||||||
if (buttonName.equals("Up")) {
|
if (buttonName.equals("Up")) {
|
||||||
|
@ -57,6 +63,10 @@ public class ControllerBindingPreference extends Preference {
|
||||||
return R.drawable.ic_baseline_radio_button_checked_24;
|
return R.drawable.ic_baseline_radio_button_checked_24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getIconForHotkey(String hotkeyDisplayName) {
|
||||||
|
return R.drawable.ic_baseline_category_24;
|
||||||
|
}
|
||||||
|
|
||||||
public ControllerBindingPreference(Context context, AttributeSet attrs) {
|
public ControllerBindingPreference(Context context, AttributeSet attrs) {
|
||||||
this(context, attrs, 0);
|
this(context, attrs, 0);
|
||||||
setWidgetLayoutResource(R.layout.layout_controller_binding_preference);
|
setWidgetLayoutResource(R.layout.layout_controller_binding_preference);
|
||||||
|
@ -83,32 +93,47 @@ public class ControllerBindingPreference extends Preference {
|
||||||
TextView nameView = ((TextView)holder.findViewById(R.id.controller_binding_name));
|
TextView nameView = ((TextView)holder.findViewById(R.id.controller_binding_name));
|
||||||
mValueView = ((TextView)holder.findViewById(R.id.controller_binding_value));
|
mValueView = ((TextView)holder.findViewById(R.id.controller_binding_value));
|
||||||
|
|
||||||
iconView.setImageDrawable(ContextCompat.getDrawable(getContext(), getIconForButton(mBindingName)));
|
int drawableId = R.drawable.ic_baseline_radio_button_checked_24;
|
||||||
|
switch (mType)
|
||||||
|
{
|
||||||
|
case BUTTON: drawableId = getIconForButton(mBindingName); break;
|
||||||
|
case AXIS: drawableId = getIconForAxis(mBindingName); break;
|
||||||
|
case HOTKEY: drawableId = getIconForHotkey(mBindingName); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
iconView.setImageDrawable(ContextCompat.getDrawable(getContext(), drawableId));
|
||||||
nameView.setText(mBindingName);
|
nameView.setText(mBindingName);
|
||||||
updateValue();
|
updateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onClick() {
|
protected void onClick() {
|
||||||
ControllerBindingDialog dialog = new ControllerBindingDialog(getContext(), mBindingName, getKey(), mValue, mIsAxis);
|
ControllerBindingDialog dialog = new ControllerBindingDialog(getContext(), mBindingName, getKey(), mValue, (mType == Type.AXIS));
|
||||||
dialog.setOnDismissListener((dismissedDialog) -> updateValue());
|
dialog.setOnDismissListener((dismissedDialog) -> updateValue());
|
||||||
dialog.show();
|
dialog.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initButton(int controllerIndex, String buttonName) {
|
public void initButton(int controllerIndex, String buttonName) {
|
||||||
mBindingName = buttonName;
|
mBindingName = buttonName;
|
||||||
mIsAxis = false;
|
mType = Type.BUTTON;
|
||||||
setKey(String.format("Controller%d/Button%s", controllerIndex, buttonName));
|
setKey(String.format("Controller%d/Button%s", controllerIndex, buttonName));
|
||||||
updateValue();
|
updateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void initAxis(int controllerIndex, String axisName) {
|
public void initAxis(int controllerIndex, String axisName) {
|
||||||
mBindingName = axisName;
|
mBindingName = axisName;
|
||||||
mIsAxis = true;
|
mType = Type.AXIS;
|
||||||
setKey(String.format("Controller%d/Axis%s", controllerIndex, axisName));
|
setKey(String.format("Controller%d/Axis%s", controllerIndex, axisName));
|
||||||
updateValue();
|
updateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initHotkey(HotkeyInfo hotkeyInfo) {
|
||||||
|
mBindingName = hotkeyInfo.getDisplayName();
|
||||||
|
mType = Type.HOTKEY;
|
||||||
|
setKey(hotkeyInfo.getBindingConfigKey());
|
||||||
|
updateValue();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateValue(String value) {
|
private void updateValue(String value) {
|
||||||
mValue = value;
|
mValue = value;
|
||||||
if (mValueView != null) {
|
if (mValueView != null) {
|
||||||
|
|
|
@ -151,11 +151,11 @@ public class ControllerMappingActivity extends AppCompatActivity {
|
||||||
pref.updateValue();
|
pref.updateValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragmentCompat {
|
public static class ControllerPortFragment extends PreferenceFragmentCompat {
|
||||||
private ControllerMappingActivity activity;
|
private ControllerMappingActivity activity;
|
||||||
private int controllerIndex;
|
private int controllerIndex;
|
||||||
|
|
||||||
public SettingsFragment(ControllerMappingActivity activity, int controllerIndex) {
|
public ControllerPortFragment(ControllerMappingActivity activity, int controllerIndex) {
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.controllerIndex = controllerIndex;
|
this.controllerIndex = controllerIndex;
|
||||||
}
|
}
|
||||||
|
@ -189,6 +189,31 @@ public class ControllerMappingActivity extends AppCompatActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class HotkeyFragment extends PreferenceFragmentCompat {
|
||||||
|
private ControllerMappingActivity activity;
|
||||||
|
private HotkeyInfo[] mHotkeyInfo;
|
||||||
|
|
||||||
|
public HotkeyFragment(ControllerMappingActivity activity) {
|
||||||
|
this.activity = activity;
|
||||||
|
this.mHotkeyInfo = AndroidHostInterface.getInstance().getHotkeyInfoList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
|
final PreferenceScreen ps = getPreferenceManager().createPreferenceScreen(getContext());
|
||||||
|
if (mHotkeyInfo != null) {
|
||||||
|
for (HotkeyInfo hotkeyInfo : mHotkeyInfo) {
|
||||||
|
final ControllerBindingPreference cbp = new ControllerBindingPreference(getContext(), null);
|
||||||
|
cbp.initHotkey(hotkeyInfo);
|
||||||
|
ps.addPreference(cbp);
|
||||||
|
activity.mPreferences.add(cbp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setPreferenceScreen(ps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class SettingsCollectionFragment extends Fragment {
|
public static class SettingsCollectionFragment extends Fragment {
|
||||||
private ControllerMappingActivity activity;
|
private ControllerMappingActivity activity;
|
||||||
private SettingsCollectionAdapter adapter;
|
private SettingsCollectionAdapter adapter;
|
||||||
|
@ -211,9 +236,12 @@ public class ControllerMappingActivity extends AppCompatActivity {
|
||||||
viewPager.setAdapter(adapter);
|
viewPager.setAdapter(adapter);
|
||||||
|
|
||||||
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
|
TabLayout tabLayout = view.findViewById(R.id.tab_layout);
|
||||||
new TabLayoutMediator(tabLayout, viewPager,
|
new TabLayoutMediator(tabLayout, viewPager, (tab, position) -> {
|
||||||
(tab, position) -> tab.setText(String.format("Port %d", position + 1))
|
if (position == NUM_CONTROLLER_PORTS)
|
||||||
).attach();
|
tab.setText("Hotkeys");
|
||||||
|
else
|
||||||
|
tab.setText(String.format("Port %d", position + 1));
|
||||||
|
}).attach();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,12 +256,15 @@ public class ControllerMappingActivity extends AppCompatActivity {
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Fragment createFragment(int position) {
|
public Fragment createFragment(int position) {
|
||||||
return new SettingsFragment(activity, position + 1);
|
if (position != NUM_CONTROLLER_PORTS)
|
||||||
|
return new ControllerPortFragment(activity, position + 1);
|
||||||
|
else
|
||||||
|
return new HotkeyFragment(activity);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount() {
|
public int getItemCount() {
|
||||||
return NUM_CONTROLLER_PORTS;
|
return NUM_CONTROLLER_PORTS + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.github.stenzek.duckstation;
|
||||||
|
|
||||||
|
public class HotkeyInfo {
|
||||||
|
private String mCategory;
|
||||||
|
private String mName;
|
||||||
|
private String mDisplayName;
|
||||||
|
|
||||||
|
public HotkeyInfo(String category, String name, String displayName) {
|
||||||
|
mCategory = category;
|
||||||
|
mName = name;
|
||||||
|
mDisplayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getCategory() {
|
||||||
|
return mCategory;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return mDisplayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBindingConfigKey() {
|
||||||
|
return String.format("Hotkeys/%s", mName);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="?attr/colorControlNormal">
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M12,2l-5.5,9h11z"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M17.5,17.5m-4.5,0a4.5,4.5 0,1 1,9 0a4.5,4.5 0,1 1,-9 0"/>
|
||||||
|
<path
|
||||||
|
android:fillColor="@android:color/white"
|
||||||
|
android:pathData="M3,13.5h8v8H3z"/>
|
||||||
|
</vector>
|
Loading…
Reference in a new issue