mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-07-23 06:09:50 -06:00
Android: Add the advanced input mapping dialog
It's missing a lot of features from the PC version for now, like buttons for inserting functions and the ability to see what the expression evaluates to. I mostly just wanted to get something in place so you can set up rumble. Co-authored-by: Charles Lombardo <clombardo169@gmail.com>
This commit is contained in:
@ -97,6 +97,9 @@ public final class ControllerInterface
|
||||
|
||||
public static native String[] getAllDeviceStrings();
|
||||
|
||||
@Nullable
|
||||
public static native CoreDevice getDevice(String deviceString);
|
||||
|
||||
@Keep
|
||||
private static void registerInputDeviceListener()
|
||||
{
|
||||
|
@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.input.model;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
/**
|
||||
* Represents a C++ ciface::Core::Device.
|
||||
*/
|
||||
public final class CoreDevice
|
||||
{
|
||||
/**
|
||||
* Represents a C++ ciface::Core::Device::Control.
|
||||
*
|
||||
* This class is non-static to ensure that the CoreDevice parent does not get garbage collected
|
||||
* while a Control is still accessible. (CoreDevice's finalizer may delete the native controls.)
|
||||
*/
|
||||
@SuppressWarnings("InnerClassMayBeStatic")
|
||||
public final class Control
|
||||
{
|
||||
@Keep
|
||||
private final long mPointer;
|
||||
|
||||
@Keep
|
||||
private Control(long pointer)
|
||||
{
|
||||
mPointer = pointer;
|
||||
}
|
||||
|
||||
public native String getName();
|
||||
}
|
||||
|
||||
@Keep
|
||||
private final long mPointer;
|
||||
|
||||
@Keep
|
||||
private CoreDevice(long pointer)
|
||||
{
|
||||
mPointer = pointer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected native void finalize();
|
||||
|
||||
public native Control[] getInputs();
|
||||
|
||||
public native Control[] getOutputs();
|
||||
}
|
@ -27,5 +27,8 @@ public final class MappingCommon
|
||||
public static native String detectInput(@NonNull EmulatedController controller,
|
||||
boolean allDevices);
|
||||
|
||||
public static native String getExpressionForControl(String control, String device,
|
||||
String defaultDevice);
|
||||
|
||||
public static native void save();
|
||||
}
|
||||
|
@ -34,4 +34,6 @@ public class ControlReference
|
||||
*/
|
||||
@Nullable
|
||||
public native String setExpression(String expr);
|
||||
|
||||
public native boolean isInput();
|
||||
}
|
||||
|
@ -52,4 +52,14 @@ public final class InputMappingControlSetting extends SettingsItem
|
||||
{
|
||||
return mController;
|
||||
}
|
||||
|
||||
public ControlReference getControlReference()
|
||||
{
|
||||
return mControlReference;
|
||||
}
|
||||
|
||||
public boolean isInput()
|
||||
{
|
||||
return mControlReference.isInput();
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,55 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.input.ui;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public final class AdvancedMappingControlAdapter
|
||||
extends RecyclerView.Adapter<AdvancedMappingControlViewHolder>
|
||||
{
|
||||
private final Consumer<String> mOnClickCallback;
|
||||
|
||||
private String[] mControls = new String[0];
|
||||
|
||||
public AdvancedMappingControlAdapter(Consumer<String> onClickCallback)
|
||||
{
|
||||
mOnClickCallback = onClickCallback;
|
||||
}
|
||||
|
||||
@NonNull @Override
|
||||
public AdvancedMappingControlViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
|
||||
int viewType)
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||
|
||||
ListItemAdvancedMappingControlBinding binding =
|
||||
ListItemAdvancedMappingControlBinding.inflate(inflater);
|
||||
return new AdvancedMappingControlViewHolder(binding, mOnClickCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AdvancedMappingControlViewHolder holder, int position)
|
||||
{
|
||||
holder.bind(mControls[position]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount()
|
||||
{
|
||||
return mControls.length;
|
||||
}
|
||||
|
||||
public void setControls(String[] controls)
|
||||
{
|
||||
mControls = controls;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.input.ui;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class AdvancedMappingControlViewHolder extends RecyclerView.ViewHolder
|
||||
{
|
||||
private final ListItemAdvancedMappingControlBinding mBinding;
|
||||
|
||||
private String mName;
|
||||
|
||||
public AdvancedMappingControlViewHolder(@NonNull ListItemAdvancedMappingControlBinding binding,
|
||||
Consumer<String> onClickCallback)
|
||||
{
|
||||
super(binding.getRoot());
|
||||
|
||||
mBinding = binding;
|
||||
|
||||
binding.getRoot().setOnClickListener(view -> onClickCallback.accept(mName));
|
||||
}
|
||||
|
||||
public void bind(String name)
|
||||
{
|
||||
mName = name;
|
||||
|
||||
mBinding.textName.setText(name);
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
package org.dolphinemu.dolphinemu.features.input.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.google.android.material.divider.MaterialDividerItemDecoration;
|
||||
|
||||
import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.CoreDevice;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.MappingCommon;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class AdvancedMappingDialog extends AlertDialog
|
||||
implements AdapterView.OnItemClickListener
|
||||
{
|
||||
private final DialogAdvancedMappingBinding mBinding;
|
||||
private final ControlReference mControlReference;
|
||||
private final EmulatedController mController;
|
||||
private final String[] mDevices;
|
||||
private final AdvancedMappingControlAdapter mControlAdapter;
|
||||
|
||||
private String mSelectedDevice;
|
||||
|
||||
public AdvancedMappingDialog(Context context, DialogAdvancedMappingBinding binding,
|
||||
ControlReference controlReference, EmulatedController controller)
|
||||
{
|
||||
super(context);
|
||||
|
||||
mBinding = binding;
|
||||
mControlReference = controlReference;
|
||||
mController = controller;
|
||||
|
||||
mDevices = ControllerInterface.getAllDeviceStrings();
|
||||
|
||||
// TODO: Remove workaround for text filtering issue in material components when fixed
|
||||
// https://github.com/material-components/material-components-android/issues/1464
|
||||
mBinding.dropdownDevice.setSaveEnabled(false);
|
||||
|
||||
binding.dropdownDevice.setOnItemClickListener(this);
|
||||
|
||||
ArrayAdapter<String> deviceAdapter = new ArrayAdapter<>(
|
||||
context, android.R.layout.simple_spinner_dropdown_item, mDevices);
|
||||
binding.dropdownDevice.setAdapter(deviceAdapter);
|
||||
|
||||
mControlAdapter = new AdvancedMappingControlAdapter(this::onControlClicked);
|
||||
mBinding.listControl.setAdapter(mControlAdapter);
|
||||
mBinding.listControl.setLayoutManager(new LinearLayoutManager(context));
|
||||
|
||||
MaterialDividerItemDecoration divider =
|
||||
new MaterialDividerItemDecoration(context, LinearLayoutManager.VERTICAL);
|
||||
divider.setLastItemDecorated(false);
|
||||
mBinding.listControl.addItemDecoration(divider);
|
||||
|
||||
binding.editExpression.setText(controlReference.getExpression());
|
||||
|
||||
selectDefaultDevice();
|
||||
}
|
||||
|
||||
public String getExpression()
|
||||
{
|
||||
return mBinding.editExpression.getText().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id)
|
||||
{
|
||||
setSelectedDevice(mDevices[position]);
|
||||
}
|
||||
|
||||
private void setSelectedDevice(String deviceString)
|
||||
{
|
||||
mSelectedDevice = deviceString;
|
||||
|
||||
CoreDevice device = ControllerInterface.getDevice(deviceString);
|
||||
if (device == null)
|
||||
setControls(new CoreDevice.Control[0]);
|
||||
else if (mControlReference.isInput())
|
||||
setControls(device.getInputs());
|
||||
else
|
||||
setControls(device.getOutputs());
|
||||
}
|
||||
|
||||
private void setControls(CoreDevice.Control[] controls)
|
||||
{
|
||||
mControlAdapter.setControls(
|
||||
Arrays.stream(controls)
|
||||
.map(CoreDevice.Control::getName)
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
private void onControlClicked(String control)
|
||||
{
|
||||
String expression = MappingCommon.getExpressionForControl(control, mSelectedDevice,
|
||||
mController.getDefaultDevice());
|
||||
|
||||
int start = Math.max(mBinding.editExpression.getSelectionStart(), 0);
|
||||
int end = Math.max(mBinding.editExpression.getSelectionEnd(), 0);
|
||||
mBinding.editExpression.getText().replace(
|
||||
Math.min(start, end), Math.max(start, end), expression, 0, expression.length());
|
||||
}
|
||||
|
||||
private void selectDefaultDevice()
|
||||
{
|
||||
String defaultDevice = mController.getDefaultDevice();
|
||||
boolean isInput = mControlReference.isInput();
|
||||
|
||||
if (Arrays.asList(mDevices).contains(defaultDevice) &&
|
||||
(isInput || deviceHasOutputs(defaultDevice)))
|
||||
{
|
||||
// The default device is available, and it's an appropriate choice. Pick it
|
||||
setSelectedDevice(defaultDevice);
|
||||
mBinding.dropdownDevice.setText(defaultDevice, false);
|
||||
return;
|
||||
}
|
||||
else if (!isInput)
|
||||
{
|
||||
// Find the first device that has an output. (Most built-in devices don't have any)
|
||||
Optional<String> deviceWithOutputs = Arrays.stream(mDevices)
|
||||
.filter(AdvancedMappingDialog::deviceHasOutputs)
|
||||
.findFirst();
|
||||
|
||||
if (deviceWithOutputs.isPresent())
|
||||
{
|
||||
setSelectedDevice(deviceWithOutputs.get());
|
||||
mBinding.dropdownDevice.setText(deviceWithOutputs.get(), false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found
|
||||
setSelectedDevice("");
|
||||
}
|
||||
|
||||
private static boolean deviceHasOutputs(String deviceString)
|
||||
{
|
||||
CoreDevice device = ControllerInterface.getDevice(deviceString);
|
||||
return device != null && device.getOutputs().length > 0;
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
|
||||
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
|
||||
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
|
||||
@ -17,9 +17,9 @@ public final class InputMappingControlSettingViewHolder extends SettingViewHolde
|
||||
{
|
||||
private InputMappingControlSetting mItem;
|
||||
|
||||
private final ListItemSettingBinding mBinding;
|
||||
private final ListItemMappingBinding mBinding;
|
||||
|
||||
public InputMappingControlSettingViewHolder(@NonNull ListItemSettingBinding binding,
|
||||
public InputMappingControlSettingViewHolder(@NonNull ListItemMappingBinding binding,
|
||||
SettingsAdapter adapter)
|
||||
{
|
||||
super(binding.getRoot(), adapter);
|
||||
@ -33,6 +33,7 @@ public final class InputMappingControlSettingViewHolder extends SettingViewHolde
|
||||
|
||||
mBinding.textSettingName.setText(mItem.getName());
|
||||
mBinding.textSettingDescription.setText(mItem.getValue());
|
||||
mBinding.buttonAdvancedSettings.setOnClickListener(this::onLongClick);
|
||||
|
||||
setStyle(mBinding.textSettingName, mItem);
|
||||
}
|
||||
@ -46,11 +47,28 @@ public final class InputMappingControlSettingViewHolder extends SettingViewHolde
|
||||
return;
|
||||
}
|
||||
|
||||
getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition());
|
||||
if (mItem.isInput())
|
||||
getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition());
|
||||
else
|
||||
getAdapter().onAdvancedInputMappingClick(mItem, getBindingAdapterPosition());
|
||||
|
||||
setStyle(mBinding.textSettingName, mItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View clicked)
|
||||
{
|
||||
if (!mItem.isEditable())
|
||||
{
|
||||
showNotRuntimeEditableError();
|
||||
return true;
|
||||
}
|
||||
|
||||
getAdapter().onAdvancedInputMappingClick(mItem, getBindingAdapterPosition());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
protected SettingsItem getItem()
|
||||
{
|
||||
|
@ -31,12 +31,15 @@ import com.google.android.material.timepicker.MaterialTimePicker;
|
||||
import com.google.android.material.timepicker.TimeFormat;
|
||||
|
||||
import org.dolphinemu.dolphinemu.R;
|
||||
import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.DialogSliderBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemHeaderBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemSettingBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemSettingSwitchBinding;
|
||||
import org.dolphinemu.dolphinemu.databinding.ListItemSubmenuBinding;
|
||||
import org.dolphinemu.dolphinemu.features.input.ui.AdvancedMappingDialog;
|
||||
import org.dolphinemu.dolphinemu.features.input.ui.MotionAlertDialog;
|
||||
import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
|
||||
import org.dolphinemu.dolphinemu.features.input.ui.viewholder.InputMappingControlSettingViewHolder;
|
||||
@ -123,7 +126,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||
return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this);
|
||||
|
||||
case SettingsItem.TYPE_INPUT_MAPPING_CONTROL:
|
||||
return new InputMappingControlSettingViewHolder(ListItemSettingBinding.inflate(inflater),
|
||||
return new InputMappingControlSettingViewHolder(ListItemMappingBinding.inflate(inflater),
|
||||
this);
|
||||
|
||||
case SettingsItem.TYPE_FILE_PICKER:
|
||||
@ -359,6 +362,44 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void onAdvancedInputMappingClick(final InputMappingControlSetting item, final int position)
|
||||
{
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
|
||||
DialogAdvancedMappingBinding binding = DialogAdvancedMappingBinding.inflate(inflater);
|
||||
|
||||
final AdvancedMappingDialog dialog = new AdvancedMappingDialog(mContext, binding,
|
||||
item.getControlReference(), item.getController());
|
||||
|
||||
Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
|
||||
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
|
||||
MaterialColors.getColor(dialog.getWindow().getDecorView(), R.attr.colorSurface),
|
||||
dialog.getWindow().getDecorView().getElevation());
|
||||
background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
|
||||
dialog.getWindow().setBackgroundDrawable(background);
|
||||
|
||||
dialog.setTitle(item.isInput() ?
|
||||
R.string.input_configure_input : R.string.input_configure_output);
|
||||
dialog.setView(binding.getRoot());
|
||||
dialog.setButton(AlertDialog.BUTTON_POSITIVE, mContext.getString(R.string.ok),
|
||||
(dialogInterface, i) ->
|
||||
{
|
||||
item.setValue(dialog.getExpression());
|
||||
notifyItemChanged(position);
|
||||
mView.onSettingChanged();
|
||||
});
|
||||
dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(R.string.cancel), this);
|
||||
dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear),
|
||||
(dialogInterface, i) ->
|
||||
{
|
||||
item.clearValue();
|
||||
notifyItemChanged(position);
|
||||
mView.onSettingChanged();
|
||||
});
|
||||
dialog.setCanceledOnTouchOutside(false);
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public void onFilePickerDirectoryClick(SettingsItem item, int position)
|
||||
{
|
||||
mClickedItem = item;
|
||||
|
9
Source/Android/app/src/main/res/drawable/ic_more.xml
Normal file
9
Source/Android/app/src/main/res/drawable/ic_more.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24"
|
||||
android:viewportWidth="24">
|
||||
<path
|
||||
android:fillColor="?attr/colorControlNormal"
|
||||
android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z" />
|
||||
</vector>
|
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout android:id="@+id/root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@id/button_advanced_settings">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_setting_name"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:textSize="16sp"
|
||||
android:textAlignment="viewStart"
|
||||
android:layout_toStartOf="@+id/button_more_settings"
|
||||
tools:text="Setting Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_setting_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignStart="@+id/text_setting_name"
|
||||
android:layout_below="@+id/text_setting_name"
|
||||
android:layout_marginBottom="@dimen/spacing_large"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="@string/overclock_enable_description" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_advanced_settings"
|
||||
style="?attr/materialIconButtonStyle"
|
||||
android:contentDescription="@string/advanced_settings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/spacing_small"
|
||||
android:nextFocusRight="@id/root"
|
||||
app:icon="@drawable/ic_more"
|
||||
app:iconTint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingHorizontal="@dimen/spacing_large"
|
||||
android:paddingTop="@dimen/spacing_medlarge">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_control"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@+id/expression"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@+id/device" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/device"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:hint="@string/input_device">
|
||||
|
||||
<com.google.android.material.textfield.MaterialAutoCompleteTextView
|
||||
android:id="@+id/dropdown_device"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="none" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/expression"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:hint="@string/input_expression">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/edit_expression"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="start"
|
||||
android:importantForAutofill="no"
|
||||
android:typeface="monospace" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
</RelativeLayout>
|
@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="54dp"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||
tools:text="Button A"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:id="@+id/text_name"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="16sp" />
|
||||
|
||||
</RelativeLayout>
|
56
Source/Android/app/src/main/res/layout/list_item_mapping.xml
Normal file
56
Source/Android/app/src/main/res/layout/list_item_mapping.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout android:id="@+id/root"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:nextFocusRight="@id/button_advanced_settings">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_setting_name"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginTop="@dimen/spacing_large"
|
||||
android:textSize="16sp"
|
||||
android:textAlignment="viewStart"
|
||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
||||
tools:text="Setting Name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_setting_description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_alignStart="@+id/text_setting_name"
|
||||
android:layout_below="@+id/text_setting_name"
|
||||
android:layout_marginBottom="@dimen/spacing_large"
|
||||
android:layout_marginEnd="@dimen/spacing_large"
|
||||
android:layout_marginStart="@dimen/spacing_large"
|
||||
android:layout_marginTop="@dimen/spacing_small"
|
||||
android:layout_toStartOf="@+id/button_advanced_settings"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="@string/overclock_enable_description" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_advanced_settings"
|
||||
style="?attr/materialIconButtonStyle"
|
||||
android:contentDescription="@string/advanced_settings"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="@dimen/spacing_small"
|
||||
android:nextFocusLeft="@id/root"
|
||||
app:icon="@drawable/ic_more"
|
||||
app:iconTint="?attr/colorOnPrimaryContainer" />
|
||||
|
||||
</RelativeLayout>
|
@ -50,6 +50,9 @@
|
||||
<string name="input_binding">Input Binding</string>
|
||||
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
|
||||
<string name="input_binding_no_device">You need to select a device first!</string>
|
||||
<string name="input_configure_input">Configure Input</string>
|
||||
<string name="input_configure_output">Configure Output</string>
|
||||
<string name="input_expression">Expression</string>
|
||||
|
||||
<!-- Main Preference Fragment -->
|
||||
<string name="settings">Settings</string>
|
||||
@ -420,6 +423,7 @@
|
||||
<string name="other">Other</string>
|
||||
<string name="continue_anyway">Continue Anyway</string>
|
||||
<string name="more_settings">More Settings</string>
|
||||
<string name="advanced_settings">Advanced Settings</string>
|
||||
|
||||
<!-- Game Grid Screen-->
|
||||
<string name="platform_gamecube">GameCube Games</string>
|
||||
|
Reference in New Issue
Block a user