@@ -9,6 +9,7 @@
import android.provider.Settings;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.NonNull;
@@ -18,6 +19,7 @@
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.lifecycle.ViewModelProvider;

@@ -41,15 +43,18 @@ public final class SettingsActivity extends AppCompatActivity implements Setting
private static final String ARG_GAME_ID = "game_id";
private static final String ARG_REVISION = "revision";
private static final String ARG_IS_WII = "is_wii";
private static final String KEY_MAPPING_ALL_DEVICES = "all_devices";
private static final String FRAGMENT_TAG = "settings";
private SettingsActivityPresenter mPresenter;
private static final String FRAGMENT_DIALOG_TAG = "settings_dialog";

private SettingsActivityPresenter mPresenter;
private AlertDialog dialog;

private CollapsingToolbarLayout mToolbarLayout;

private ActivitySettingsBinding mBinding;

private boolean mMappingAllDevices = false;

public static void launch(Context context, MenuTag menuTag, String gameId, int revision,
boolean isWii)
{
@@ -82,6 +87,10 @@ protected void onCreate(Bundle savedInstanceState)
{
MainPresenter.skipRescanningLibrary();
}
else
{
mMappingAllDevices = savedInstanceState.getBoolean(KEY_MAPPING_ALL_DEVICES);
}

mBinding = ActivitySettingsBinding.inflate(getLayoutInflater());
setContentView(mBinding.getRoot());
@@ -125,7 +134,10 @@ protected void onSaveInstanceState(@NonNull Bundle outState)
{
// Critical: If super method is not called, rotations will be busted.
super.onSaveInstanceState(outState);

mPresenter.saveState(outState);

outState.putBoolean(KEY_MAPPING_ALL_DEVICES, mMappingAllDevices);
}

@Override
@@ -181,6 +193,12 @@ public void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToSt
transaction.commit();
}

@Override
public void showDialogFragment(DialogFragment fragment)
{
fragment.show(getSupportFragmentManager(), FRAGMENT_DIALOG_TAG);
}

private boolean areSystemAnimationsEnabled()
{
float duration = Settings.Global.getFloat(
@@ -304,6 +322,12 @@ public void onSettingChanged()
mPresenter.onSettingChanged();
}

@Override
public void onControllerSettingsChanged()
{
getFragment().onControllerSettingsChanged();
}

@Override
public void onMenuTagAction(@NonNull MenuTag menuTag, int value)
{
@@ -330,7 +354,27 @@ private SettingsFragment getFragment()

public void setToolbarTitle(String title)
{
mToolbarLayout.setTitle(title);
mBinding.toolbarSettingsLayout.setTitle(title);
}

@Override
public void setMappingAllDevices(boolean allDevices)
{
mMappingAllDevices = allDevices;
}

@Override
public boolean isMappingAllDevices()
{
return mMappingAllDevices;
}

@Override
public int setOldControllerSettingsWarningVisibility(boolean visible)
{
// We use INVISIBLE instead of GONE to avoid getting a stale height for the return value
mBinding.oldControllerSettingsWarning.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
return visible ? mBinding.oldControllerSettingsWarning.getHeight() : 0;
}

private void setInsets()
@@ -343,6 +387,10 @@ private void setInsets()

mBinding.frameContentSettings.setPadding(insets.left, 0, insets.right, 0);

int textPadding = getResources().getDimensionPixelSize(R.dimen.spacing_large);
mBinding.oldControllerSettingsWarning.setPadding(textPadding + insets.left, textPadding,
textPadding + insets.right, textPadding + insets.bottom);

InsetsHelper.applyNavbarWorkaround(insets.bottom, mBinding.workaroundView);
ThemeHelper.setNavigationBarColor(this,
MaterialColors.getColor(mBinding.appbarSettings, R.attr.colorSurface));
@@ -5,6 +5,7 @@
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;

import org.dolphinemu.dolphinemu.features.settings.model.Settings;

@@ -21,6 +22,13 @@
*/
void showSettingsFragment(MenuTag menuTag, Bundle extras, boolean addToStack, String gameId);

/**
* Shows a DialogFragment.
*
* Only one can be shown at a time.
*/
void showDialogFragment(DialogFragment fragment);

/**
* Called by a contained Fragment to get access to the Setting HashMap
* loaded from disk, so that each Fragment doesn't need to perform its own
@@ -60,6 +68,14 @@
*/
void onSettingChanged();

/**
* Refetches the values of all controller settings.
*
* To be used when loading an input profile or performing some other action that changes all
* controller settings at once.
*/
void onControllerSettingsChanged();

/**
* Called by a containing Fragment to tell the containing Activity that the user wants to open the
* MenuTag associated with a setting.
@@ -97,4 +113,25 @@
* Accesses the material toolbar layout and changes the title
*/
void setToolbarTitle(String title);

/**
* Sets whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
void setMappingAllDevices(boolean allDevices);

/**
* Returns whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
boolean isMappingAllDevices();

/**
* Shows or hides a warning telling the user that they're using incompatible controller settings.
* The warning is hidden by default.
*
* @param visible Whether the warning should be visible.
* @return The height of the warning view, or 0 if the view is now invisible.
*/
int setOldControllerSettingsWarningVisibility(boolean visible);
}
@@ -31,21 +31,24 @@
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.dialogs.MotionAlertDialog;
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;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.model.view.DateTimeChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SwitchSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.FilePicker;
import org.dolphinemu.dolphinemu.features.settings.model.view.FloatSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.InputBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.IntSliderSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.RumbleBindingSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.features.settings.model.view.SingleChoiceSettingDynamicDescriptions;
@@ -57,9 +60,7 @@
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.FilePickerViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderHyperLinkViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.HeaderViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputBindingSettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.InputStringSettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RumbleBindingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.RunRunnableViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder;
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SingleChoiceViewHolder;
@@ -124,13 +125,9 @@ public SettingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewT
case SettingsItem.TYPE_SUBMENU:
return new SubmenuViewHolder(ListItemSubmenuBinding.inflate(inflater), this);

case SettingsItem.TYPE_INPUT_BINDING:
return new InputBindingSettingViewHolder(ListItemSettingBinding.inflate(inflater), this,
mContext);

case SettingsItem.TYPE_RUMBLE_BINDING:
return new RumbleBindingViewHolder(ListItemSettingBinding.inflate(inflater), this,
mContext);
case SettingsItem.TYPE_INPUT_MAPPING_CONTROL:
return new InputMappingControlSettingViewHolder(ListItemMappingBinding.inflate(inflater),
this);

case SettingsItem.TYPE_FILE_PICKER:
return new FilePickerViewHolder(ListItemSettingBinding.inflate(inflater), this);
@@ -259,11 +256,26 @@ public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int positi
mClickedItem = item;
mClickedPosition = position;

mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
this)
.show();
item.refreshChoicesAndValues();

String[] choices = item.getChoices();
int noChoicesAvailableString = item.getNoChoicesAvailableString();
if (noChoicesAvailableString != 0 && choices.length == 0)
{
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setMessage(noChoicesAvailableString)
.setPositiveButton(R.string.ok, null)
.show();
}
else
{
mDialog = new MaterialAlertDialogBuilder(mView.getActivity())
.setTitle(item.getName())
.setSingleChoiceItems(item.getChoices(), item.getSelectedValueIndex(getSettings()),
this)
.show();
}
}

public void onSingleChoiceDynamicDescriptionsClick(SingleChoiceSettingDynamicDescriptions item,
@@ -314,9 +326,19 @@ public void onSubmenuClick(SubmenuSetting item)
mView.loadSubMenu(item.getMenuKey());
}

public void onInputBindingClick(final InputBindingSetting item, final int position)
public void onInputMappingClick(final InputMappingControlSetting item, final int position)
{
final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item, this);
if (item.getController().getDefaultDevice().isEmpty() && !mView.isMappingAllDevices())
{
new MaterialAlertDialogBuilder(mView.getActivity())
.setMessage(R.string.input_binding_no_device)
.setPositiveButton(R.string.ok, this)
.show();
return;
}

final MotionAlertDialog dialog = new MotionAlertDialog(mView.getActivity(), item,
mView.isMappingAllDevices());

Drawable background = ContextCompat.getDrawable(mContext, R.drawable.dialog_round);
@ColorInt int color = new ElevationOverlayProvider(dialog.getContext()).compositeOverlay(
@@ -326,13 +348,11 @@ public void onInputBindingClick(final InputBindingSetting item, final int positi
dialog.getWindow().setBackgroundDrawable(background);

dialog.setTitle(R.string.input_binding);
dialog.setMessage(String.format(mContext.getString(
item instanceof RumbleBindingSetting ?
R.string.input_rumble_description : R.string.input_binding_description),
dialog.setMessage(String.format(mContext.getString(R.string.input_binding_description),
item.getName()));
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(getSettings()));
(dialogInterface, i) -> item.clearValue());
dialog.setOnDismissListener(dialog1 ->
{
notifyItemChanged(position);
@@ -342,6 +362,44 @@ public void onInputBindingClick(final InputBindingSetting item, final int positi
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;
@@ -13,6 +13,7 @@
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
@@ -36,6 +37,8 @@ public final class SettingsFragment extends Fragment implements SettingsFragment

private SettingsAdapter mAdapter;

private int mOldControllerSettingsWarningHeight = 0;

private static final Map<MenuTag, Integer> titles = new HashMap<>();

static
@@ -65,14 +68,26 @@ public final class SettingsFragment extends Fragment implements SettingsFragment
titles.put(MenuTag.GCPAD_2, R.string.controller_1);
titles.put(MenuTag.GCPAD_3, R.string.controller_2);
titles.put(MenuTag.GCPAD_4, R.string.controller_3);
titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_4);
titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_5);
titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_6);
titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_7);
titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_4);
titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_5);
titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_6);
titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_7);
titles.put(MenuTag.WIIMOTE_1, R.string.wiimote_0);
titles.put(MenuTag.WIIMOTE_2, R.string.wiimote_1);
titles.put(MenuTag.WIIMOTE_3, R.string.wiimote_2);
titles.put(MenuTag.WIIMOTE_4, R.string.wiimote_3);
titles.put(MenuTag.WIIMOTE_EXTENSION_1, R.string.wiimote_extension_0);
titles.put(MenuTag.WIIMOTE_EXTENSION_2, R.string.wiimote_extension_1);
titles.put(MenuTag.WIIMOTE_EXTENSION_3, R.string.wiimote_extension_2);
titles.put(MenuTag.WIIMOTE_EXTENSION_4, R.string.wiimote_extension_3);
titles.put(MenuTag.WIIMOTE_GENERAL_1, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_GENERAL_2, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_GENERAL_3, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_GENERAL_4, R.string.wiimote_general);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_1, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_2, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_3, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_SIMULATION_4, R.string.wiimote_motion_simulation);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_1, R.string.wiimote_motion_input);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_2, R.string.wiimote_motion_input);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_3, R.string.wiimote_motion_input);
titles.put(MenuTag.WIIMOTE_MOTION_INPUT_4, R.string.wiimote_motion_input);
}

private FragmentSettingsBinding mBinding;
@@ -203,6 +218,12 @@ public void loadSubMenu(MenuTag menuKey)
mActivity.showSettingsFragment(menuKey, null, true, getArguments().getString(ARGUMENT_GAME_ID));
}

@Override
public void showDialogFragment(DialogFragment fragment)
{
mActivity.showDialogFragment(fragment);
}

@Override
public void showToastMessage(String message)
{
@@ -221,6 +242,13 @@ public void onSettingChanged()
mActivity.onSettingChanged();
}

@Override
public void onControllerSettingsChanged()
{
mAdapter.notifyAllSettingsChanged();
mPresenter.updateOldControllerSettingsWarningVisibility();
}

@Override
public void onMenuTagAction(@NonNull MenuTag menuTag, int value)
{
@@ -232,13 +260,35 @@ public boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value)
return mActivity.hasMenuTagActionForValue(menuTag, value);
}

@Override
public void setMappingAllDevices(boolean allDevices)
{
mActivity.setMappingAllDevices(allDevices);
}

@Override
public boolean isMappingAllDevices()
{
return mActivity.isMappingAllDevices();
}

@Override
public void setOldControllerSettingsWarningVisibility(boolean visible)
{
mOldControllerSettingsWarningHeight =
mActivity.setOldControllerSettingsWarningVisibility(visible);

// Trigger the insets listener we've registered
mBinding.listSettings.requestApplyInsets();
}

private void setInsets()
{
ViewCompat.setOnApplyWindowInsetsListener(mBinding.listSettings, (v, windowInsets) ->
{
Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(0, 0, 0,
insets.bottom + getResources().getDimensionPixelSize(R.dimen.spacing_list));
int listSpacing = getResources().getDimensionPixelSize(R.dimen.spacing_list);
v.setPadding(0, 0, 0, insets.bottom + listSpacing + mOldControllerSettingsWarningHeight);
return windowInsets;
});
}

Large diffs are not rendered by default.

@@ -3,6 +3,7 @@
package org.dolphinemu.dolphinemu.features.settings.ui;

import androidx.annotation.NonNull;
import androidx.fragment.app.DialogFragment;
import androidx.fragment.app.FragmentActivity;

import org.dolphinemu.dolphinemu.features.settings.model.Settings;
@@ -55,6 +56,8 @@
*/
void loadSubMenu(MenuTag menuKey);

void showDialogFragment(DialogFragment fragment);

/**
* Tell the Fragment to tell the containing activity to display a toast message.
*
@@ -72,6 +75,14 @@
*/
void onSettingChanged();

/**
* Refetches the values of all controller settings.
*
* To be used when loading an input profile or performing some other action that changes all
* controller settings at once.
*/
void onControllerSettingsChanged();

/**
* Have the fragment tell the containing Activity that the user wants to open the MenuTag
* associated with a setting.
@@ -89,4 +100,24 @@
* @param value The current value of the setting.
*/
boolean hasMenuTagActionForValue(@NonNull MenuTag menuTag, int value);

/**
* Sets whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
void setMappingAllDevices(boolean allDevices);

/**
* Returns whether the input mapping dialog should detect inputs from all devices,
* not just the device configured for the controller.
*/
boolean isMappingAllDevices();

/**
* Shows or hides a warning telling the user that they're using incompatible controller settings.
* The warning is hidden by default.
*
* @param visible Whether the warning should be visible.
*/
void setOldControllerSettingsWarningVisibility(boolean visible);
}

This file was deleted.

This file was deleted.

@@ -83,7 +83,7 @@ public boolean onLongClick(View clicked)
{
SettingsItem item = getItem();

if (item == null || !item.hasSetting())
if (item == null || !item.canClear())
return false;

if (!item.isEditable())
@@ -64,10 +64,8 @@ else if (item instanceof SingleChoiceSetting)
else if (item instanceof StringSingleChoiceSetting)
{
StringSingleChoiceSetting setting = (StringSingleChoiceSetting) item;
String[] choices = setting.getChoices();
int valueIndex = setting.getSelectedValueIndex(settings);
if (valueIndex != -1)
mBinding.textSettingDescription.setText(choices[valueIndex]);
String choice = setting.getSelectedChoice(settings);
mBinding.textSettingDescription.setText(choice);
}
else if (item instanceof SingleChoiceSettingDynamicDescriptions)
{
@@ -20,161 +20,6 @@
public static final String KEY_ISO_PATH_BASE = "ISOPath";
public static final String KEY_ISO_PATHS = "ISOPaths";

public static final String KEY_GCPAD_PLAYER_1 = "SIDevice0";

public static final String KEY_GCBIND_A = "InputA_";
public static final String KEY_GCBIND_B = "InputB_";
public static final String KEY_GCBIND_X = "InputX_";
public static final String KEY_GCBIND_Y = "InputY_";
public static final String KEY_GCBIND_Z = "InputZ_";
public static final String KEY_GCBIND_START = "InputStart_";
public static final String KEY_GCBIND_CONTROL_UP = "MainUp_";
public static final String KEY_GCBIND_CONTROL_DOWN = "MainDown_";
public static final String KEY_GCBIND_CONTROL_LEFT = "MainLeft_";
public static final String KEY_GCBIND_CONTROL_RIGHT = "MainRight_";
public static final String KEY_GCBIND_C_UP = "CStickUp_";
public static final String KEY_GCBIND_C_DOWN = "CStickDown_";
public static final String KEY_GCBIND_C_LEFT = "CStickLeft_";
public static final String KEY_GCBIND_C_RIGHT = "CStickRight_";
public static final String KEY_GCBIND_TRIGGER_L = "InputL_";
public static final String KEY_GCBIND_TRIGGER_R = "InputR_";
public static final String KEY_GCBIND_DPAD_UP = "DPadUp_";
public static final String KEY_GCBIND_DPAD_DOWN = "DPadDown_";
public static final String KEY_GCBIND_DPAD_LEFT = "DPadLeft_";
public static final String KEY_GCBIND_DPAD_RIGHT = "DPadRight_";

public static final String KEY_EMU_RUMBLE = "EmuRumble";

public static final String KEY_WIIMOTE_EXTENSION = "Extension";

// Controller keys for game specific settings
public static final String KEY_WIIMOTE_PROFILE = "WiimoteProfile";

public static final String KEY_WIIBIND_A = "WiimoteA_";
public static final String KEY_WIIBIND_B = "WiimoteB_";
public static final String KEY_WIIBIND_1 = "Wiimote1_";
public static final String KEY_WIIBIND_2 = "Wiimote2_";
public static final String KEY_WIIBIND_MINUS = "WiimoteMinus_";
public static final String KEY_WIIBIND_PLUS = "WiimotePlus_";
public static final String KEY_WIIBIND_HOME = "WiimoteHome_";
public static final String KEY_WIIBIND_IR_UP = "IRUp_";
public static final String KEY_WIIBIND_IR_DOWN = "IRDown_";
public static final String KEY_WIIBIND_IR_LEFT = "IRLeft_";
public static final String KEY_WIIBIND_IR_RIGHT = "IRRight_";
public static final String KEY_WIIBIND_IR_FORWARD = "IRForward_";
public static final String KEY_WIIBIND_IR_BACKWARD = "IRBackward_";
public static final String KEY_WIIBIND_IR_HIDE = "IRHide_";
public static final String KEY_WIIBIND_IR_PITCH = "IRTotalPitch";
public static final String KEY_WIIBIND_IR_YAW = "IRTotalYaw";
public static final String KEY_WIIBIND_IR_VERTICAL_OFFSET = "IRVerticalOffset";
public static final String KEY_WIIBIND_SWING_UP = "SwingUp_";
public static final String KEY_WIIBIND_SWING_DOWN = "SwingDown_";
public static final String KEY_WIIBIND_SWING_LEFT = "SwingLeft_";
public static final String KEY_WIIBIND_SWING_RIGHT = "SwingRight_";
public static final String KEY_WIIBIND_SWING_FORWARD = "SwingForward_";
public static final String KEY_WIIBIND_SWING_BACKWARD = "SwingBackward_";
public static final String KEY_WIIBIND_TILT_FORWARD = "TiltForward_";
public static final String KEY_WIIBIND_TILT_BACKWARD = "TiltBackward_";
public static final String KEY_WIIBIND_TILT_LEFT = "TiltLeft_";
public static final String KEY_WIIBIND_TILT_RIGHT = "TiltRight_";
public static final String KEY_WIIBIND_TILT_MODIFIER = "TiltModifier_";
public static final String KEY_WIIBIND_SHAKE_X = "ShakeX_";
public static final String KEY_WIIBIND_SHAKE_Y = "ShakeY_";
public static final String KEY_WIIBIND_SHAKE_Z = "ShakeZ_";
public static final String KEY_WIIBIND_DPAD_UP = "WiimoteUp_";
public static final String KEY_WIIBIND_DPAD_DOWN = "WiimoteDown_";
public static final String KEY_WIIBIND_DPAD_LEFT = "WiimoteLeft_";
public static final String KEY_WIIBIND_DPAD_RIGHT = "WiimoteRight_";
public static final String KEY_WIIBIND_NUNCHUK_C = "NunchukC_";
public static final String KEY_WIIBIND_NUNCHUK_Z = "NunchukZ_";
public static final String KEY_WIIBIND_NUNCHUK_UP = "NunchukUp_";
public static final String KEY_WIIBIND_NUNCHUK_DOWN = "NunchukDown_";
public static final String KEY_WIIBIND_NUNCHUK_LEFT = "NunchukLeft_";
public static final String KEY_WIIBIND_NUNCHUK_RIGHT = "NunchukRight_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_UP = "NunchukSwingUp_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_DOWN = "NunchukSwingDown_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_LEFT = "NunchukSwingLeft_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_RIGHT = "NunchukSwingRight_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_FORWARD = "NunchukSwingForward_";
public static final String KEY_WIIBIND_NUNCHUK_SWING_BACKWARD = "NunchukSwingBackward_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_FORWARD = "NunchukTiltForward_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_BACKWARD = "NunchukTiltBackward_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_LEFT = "NunchukTiltLeft_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_RIGHT = "NunchukTiltRight_";
public static final String KEY_WIIBIND_NUNCHUK_TILT_MODIFIER = "NunchukTiltModifier_";
public static final String KEY_WIIBIND_NUNCHUK_SHAKE_X = "NunchukShakeX_";
public static final String KEY_WIIBIND_NUNCHUK_SHAKE_Y = "NunchukShakeY_";
public static final String KEY_WIIBIND_NUNCHUK_SHAKE_Z = "NunchukShakeZ_";
public static final String KEY_WIIBIND_CLASSIC_A = "ClassicA_";
public static final String KEY_WIIBIND_CLASSIC_B = "ClassicB_";
public static final String KEY_WIIBIND_CLASSIC_X = "ClassicX_";
public static final String KEY_WIIBIND_CLASSIC_Y = "ClassicY_";
public static final String KEY_WIIBIND_CLASSIC_ZL = "ClassicZL_";
public static final String KEY_WIIBIND_CLASSIC_ZR = "ClassicZR_";
public static final String KEY_WIIBIND_CLASSIC_MINUS = "ClassicMinus_";
public static final String KEY_WIIBIND_CLASSIC_PLUS = "ClassicPlus_";
public static final String KEY_WIIBIND_CLASSIC_HOME = "ClassicHome_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_UP = "ClassicLeftStickUp_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_DOWN = "ClassicLeftStickDown_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_LEFT = "ClassicLeftStickLeft_";
public static final String KEY_WIIBIND_CLASSIC_LEFT_RIGHT = "ClassicLeftStickRight_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_UP = "ClassicRightStickUp_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_DOWN = "ClassicRightStickDown_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_LEFT = "ClassicRightStickLeft_";
public static final String KEY_WIIBIND_CLASSIC_RIGHT_RIGHT = "ClassicRightStickRight_";
public static final String KEY_WIIBIND_CLASSIC_TRIGGER_L = "ClassicTriggerL_";
public static final String KEY_WIIBIND_CLASSIC_TRIGGER_R = "ClassicTriggerR_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_UP = "ClassicUp_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_DOWN = "ClassicDown_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_LEFT = "ClassicLeft_";
public static final String KEY_WIIBIND_CLASSIC_DPAD_RIGHT = "ClassicRight_";
public static final String KEY_WIIBIND_GUITAR_FRET_GREEN = "GuitarGreen_";
public static final String KEY_WIIBIND_GUITAR_FRET_RED = "GuitarRed_";
public static final String KEY_WIIBIND_GUITAR_FRET_YELLOW = "GuitarYellow_";
public static final String KEY_WIIBIND_GUITAR_FRET_BLUE = "GuitarBlue_";
public static final String KEY_WIIBIND_GUITAR_FRET_ORANGE = "GuitarOrange_";
public static final String KEY_WIIBIND_GUITAR_STRUM_UP = "GuitarStrumUp_";
public static final String KEY_WIIBIND_GUITAR_STRUM_DOWN = "GuitarStrumDown_";
public static final String KEY_WIIBIND_GUITAR_MINUS = "GuitarMinus_";
public static final String KEY_WIIBIND_GUITAR_PLUS = "GuitarPlus_";
public static final String KEY_WIIBIND_GUITAR_STICK_UP = "GuitarUp_";
public static final String KEY_WIIBIND_GUITAR_STICK_DOWN = "GuitarDown_";
public static final String KEY_WIIBIND_GUITAR_STICK_LEFT = "GuitarLeft_";
public static final String KEY_WIIBIND_GUITAR_STICK_RIGHT = "GuitarRight_";
public static final String KEY_WIIBIND_GUITAR_WHAMMY_BAR = "GuitarWhammy_";
public static final String KEY_WIIBIND_DRUMS_PAD_RED = "DrumsRed_";
public static final String KEY_WIIBIND_DRUMS_PAD_YELLOW = "DrumsYellow_";
public static final String KEY_WIIBIND_DRUMS_PAD_BLUE = "DrumsBlue_";
public static final String KEY_WIIBIND_DRUMS_PAD_GREEN = "DrumsGreen_";
public static final String KEY_WIIBIND_DRUMS_PAD_ORANGE = "DrumsOrange_";
public static final String KEY_WIIBIND_DRUMS_PAD_BASS = "DrumsBass_";
public static final String KEY_WIIBIND_DRUMS_MINUS = "DrumsMinus_";
public static final String KEY_WIIBIND_DRUMS_PLUS = "DrumsPlus_";
public static final String KEY_WIIBIND_DRUMS_STICK_UP = "DrumsUp_";
public static final String KEY_WIIBIND_DRUMS_STICK_DOWN = "DrumsDown_";
public static final String KEY_WIIBIND_DRUMS_STICK_LEFT = "DrumsLeft_";
public static final String KEY_WIIBIND_DRUMS_STICK_RIGHT = "DrumsRight_";
public static final String KEY_WIIBIND_TURNTABLE_GREEN_LEFT = "TurntableGreenLeft_";
public static final String KEY_WIIBIND_TURNTABLE_RED_LEFT = "TurntableRedLeft_";
public static final String KEY_WIIBIND_TURNTABLE_BLUE_LEFT = "TurntableBlueLeft_";
public static final String KEY_WIIBIND_TURNTABLE_GREEN_RIGHT = "TurntableGreenRight_";
public static final String KEY_WIIBIND_TURNTABLE_RED_RIGHT = "TurntableRedRight_";
public static final String KEY_WIIBIND_TURNTABLE_BLUE_RIGHT = "TurntableBlueRight_";
public static final String KEY_WIIBIND_TURNTABLE_MINUS = "TurntableMinus_";
public static final String KEY_WIIBIND_TURNTABLE_PLUS = "TurntablePlus_";
public static final String KEY_WIIBIND_TURNTABLE_EUPHORIA = "TurntableEuphoria_";
public static final String KEY_WIIBIND_TURNTABLE_LEFT_LEFT = "TurntableLeftLeft_";
public static final String KEY_WIIBIND_TURNTABLE_LEFT_RIGHT = "TurntableLeftRight_";
public static final String KEY_WIIBIND_TURNTABLE_RIGHT_LEFT = "TurntableRightLeft_";
public static final String KEY_WIIBIND_TURNTABLE_RIGHT_RIGHT = "TurntableRightRight_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_UP = "TurntableUp_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_DOWN = "TurntableDown_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_LEFT = "TurntableLeft_";
public static final String KEY_WIIBIND_TURNTABLE_STICK_RIGHT = "TurntableRight_";
public static final String KEY_WIIBIND_TURNTABLE_EFFECT_DIAL = "TurntableEffDial_";
public static final String KEY_WIIBIND_TURNTABLE_CROSSFADE_LEFT = "TurntableCrossLeft_";
public static final String KEY_WIIBIND_TURNTABLE_CROSSFADE_RIGHT = "TurntableCrossRight_";

private static BiMap<String, String> sectionsMap = new BiMap<>();

static
@@ -283,13 +128,4 @@ public static File getCustomGameSettingsFile(String gameId)
return new File(
DirectoryInitialization.getUserDirectory() + "/GameSettings/" + gameId + ".ini");
}

public static File getWiiProfile(String profile)
{
String wiiConfigPath =
DirectoryInitialization.getUserDirectory() + "/Config/Profiles/Wiimote/" +
profile + ".ini";

return new File(wiiConfigPath);
}
}
@@ -115,7 +115,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
{
int overlayX = mInputOverlay.getLeft();
int overlayY = mInputOverlay.getTop();
mInputOverlay.setSurfacePosition(new Rect(
mInputOverlay.setSurfacePosition(activity.getSettings(), new Rect(
surfaceView.getLeft() - overlayX, surfaceView.getTop() - overlayY,
surfaceView.getRight() - overlayX, surfaceView.getBottom() - overlayY));
});
@@ -133,6 +133,10 @@ public void onDestroyView()
public void onResume()
{
super.onResume();

if (mInputOverlay != null && NativeLibrary.IsGameMetadataValid())
mInputOverlay.refreshControls(activity.getSettings());

run(activity.isActivityRecreated());
}

@@ -170,19 +174,19 @@ public void toggleInputOverlayVisibility(Settings settings)
.setBoolean(settings, !BooleanSetting.MAIN_SHOW_INPUT_OVERLAY.getBoolean(settings));

if (mInputOverlay != null)
mInputOverlay.refreshControls();
mInputOverlay.refreshControls(settings);
}

public void initInputPointer()
public void initInputPointer(Settings settings)
{
if (mInputOverlay != null)
mInputOverlay.initTouchPointer();
mInputOverlay.initTouchPointer(settings);
}

public void refreshInputOverlay()
public void refreshInputOverlay(Settings settings)
{
if (mInputOverlay != null)
mInputOverlay.refreshControls();
mInputOverlay.refreshControls(settings);
}

public void refreshOverlayPointer(Settings settings)
@@ -191,10 +195,10 @@ public void refreshOverlayPointer(Settings settings)
mInputOverlay.refreshOverlayPointer(settings);
}

public void resetInputOverlay()
public void resetInputOverlay(Settings settings)
{
if (mInputOverlay != null)
mInputOverlay.resetButtonPlacement();
mInputOverlay.resetButtonPlacement(settings);
}

@Override

Large diffs are not rendered by default.

@@ -31,6 +31,7 @@
private int mPreviousTouchX, mPreviousTouchY;
private final int mWidth;
private final int mHeight;
private final int mControllerIndex;
private Rect mVirtBounds;
private Rect mOrigBounds;
private int mOpacity;
@@ -55,7 +56,7 @@
*/
public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, Bitmap bitmapInnerDefault,
Bitmap bitmapInnerPressed, Rect rectOuter, Rect rectInner, int legacyId, int xControl,
int yControl)
int yControl, int controllerIndex)
{
mJoystickLegacyId = legacyId;
mJoystickXControl = xControl;
@@ -68,6 +69,10 @@ public InputOverlayDrawableJoystick(Resources res, Bitmap bitmapOuter, Bitmap bi
mWidth = bitmapOuter.getWidth();
mHeight = bitmapOuter.getHeight();

if (controllerIndex < 0 || controllerIndex >= 4)
throw new IllegalArgumentException("controllerIndex must be 0-3");
mControllerIndex = controllerIndex;

setBounds(rectOuter);
mDefaultStateInnerBitmap.setBounds(rectInner);
mPressedStateInnerBitmap.setBounds(rectInner);
@@ -221,7 +226,8 @@ private void SetInnerBounds()

double angle = Math.atan2(y, x) + Math.PI + Math.PI;
double radius = Math.hypot(y, x);
double maxRadius = InputOverrider.getGateRadiusAtAngle(0, mJoystickXControl, angle);
double maxRadius = InputOverrider.getGateRadiusAtAngle(mControllerIndex, mJoystickXControl,
angle);
if (radius > maxRadius)
{
y = maxRadius * Math.sin(angle);
@@ -32,6 +32,7 @@

private int mMode;
private boolean mRecenter;
private int mControllerIndex;

private boolean doubleTap = false;
private int mDoubleTapControl;
@@ -47,11 +48,13 @@
DOUBLE_TAP_OPTIONS.add(NativeLibrary.ButtonType.CLASSIC_BUTTON_A);
}

public InputOverlayPointer(Rect surfacePosition, int doubleTapControl, int mode, boolean recenter)
public InputOverlayPointer(Rect surfacePosition, int doubleTapControl, int mode, boolean recenter,
int controllerIndex)
{
mDoubleTapControl = doubleTapControl;
mMode = mode;
mRecenter = recenter;
mControllerIndex = controllerIndex;

mGameCenterX = (surfacePosition.left + surfacePosition.right) / 2.0f;
mGameCenterY = (surfacePosition.top + surfacePosition.bottom) / 2.0f;
@@ -128,8 +131,9 @@ private void touchPress()
{
if (doubleTap)
{
InputOverrider.setControlState(0, mDoubleTapControl, 1.0);
new Handler().postDelayed(() -> InputOverrider.setControlState(0, mDoubleTapControl, 0.0),
InputOverrider.setControlState(mControllerIndex, mDoubleTapControl, 1.0);
new Handler().postDelayed(() -> InputOverrider.setControlState(mControllerIndex,
mDoubleTapControl, 0.0),
50);
}
else

This file was deleted.

@@ -31,17 +31,21 @@
import java.io.OutputStream;

/**
* A service that spawns its own thread in order to copy several binary and shader files
* from the Dolphin APK to the external file system.
* A class that spawns its own thread in order perform initialization.
*
* The initialization steps include:
* - Extracting the Sys directory from the APK so it can be accessed using regular file APIs
* - Letting the native code know where on external storage it should place the User directory
* - Running the native code's init steps (which include things like populating the User directory)
*/
public final class DirectoryInitialization
{
public static final String EXTRA_STATE = "directoryState";
private static final int WiimoteNewVersion = 5; // Last changed in PR 8907
private static final MutableLiveData<DirectoryInitializationState> directoryState =
new MutableLiveData<>(DirectoryInitializationState.NOT_YET_INITIALIZED);
private static volatile boolean areDirectoriesAvailable = false;
private static String userPath;
private static String sysPath;
private static boolean isUsingLegacyUserDirectory = false;

public enum DirectoryInitializationState
@@ -73,22 +77,14 @@ private static void init(Context context)
System.exit(1);
}

initializeInternalStorage(context);
boolean wiimoteIniWritten = initializeExternalStorage(context);
extractSysDirectory(context);
NativeLibrary.Initialize();
NativeLibrary.ReportStartToAnalytics();

areDirectoriesAvailable = true;

checkThemeSettings(context);

if (wiimoteIniWritten)
{
// This has to be done after calling NativeLibrary.Initialize(),
// as it relies on the config system
EmulationActivity.updateWiimoteNewIniPreferences(context);
}

directoryState.postValue(DirectoryInitializationState.DOLPHIN_DIRECTORIES_INITIALIZED);
}

@@ -131,7 +127,7 @@ private static boolean setDolphinUserDirectory(Context context)
return true;
}

private static void initializeInternalStorage(Context context)
private static void extractSysDirectory(Context context)
{
File sysDirectory = new File(context.getFilesDir(), "Sys");

@@ -142,54 +138,16 @@ private static void initializeInternalStorage(Context context)
// There is no extracted Sys directory, or there is a Sys directory from another
// version of Dolphin that might contain outdated files. Let's (re-)extract Sys.
deleteDirectoryRecursively(sysDirectory);
copyAssetFolder("Sys", sysDirectory, true, context);
copyAssetFolder("Sys", sysDirectory, context);

SharedPreferences.Editor editor = preferences.edit();
editor.putString("sysDirectoryVersion", revision);
editor.apply();
}

// Let the native code know where the Sys directory is.
SetSysDirectory(sysDirectory.getPath());
}

// Returns whether the WiimoteNew.ini file was written to
private static boolean initializeExternalStorage(Context context)
{
// Create User directory structure and copy some NAND files from the extracted Sys directory.
CreateUserDirectories();

// GCPadNew.ini and WiimoteNew.ini must contain specific values in order for controller
// input to work as intended (they aren't user configurable), so we overwrite them just
// in case the user has tried to modify them manually.
//
// ...Except WiimoteNew.ini contains the user configurable settings for Wii Remote
// extensions in addition to all of its lines that aren't user configurable, so since we
// don't want to lose the selected extensions, we don't overwrite that file if it exists.
//
// TODO: Redo the Android controller system so that we don't have to extract these INIs.
String configDirectory = NativeLibrary.GetUserDirectory() + File.separator + "Config";
String profileDirectory =
NativeLibrary.GetUserDirectory() + File.separator + "Config/Profiles/Wiimote/";
createWiimoteProfileDirectory(profileDirectory);

copyAsset("GCPadNew.ini", new File(configDirectory, "GCPadNew.ini"), true, context);

SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
boolean overwriteWiimoteIni = prefs.getInt("WiimoteNewVersion", 0) != WiimoteNewVersion;
boolean wiimoteIniWritten = copyAsset("WiimoteNew.ini",
new File(configDirectory, "WiimoteNew.ini"), overwriteWiimoteIni, context);
if (overwriteWiimoteIni)
{
SharedPreferences.Editor sPrefsEditor = prefs.edit();
sPrefsEditor.putInt("WiimoteNewVersion", WiimoteNewVersion);
sPrefsEditor.apply();
}

copyAsset("WiimoteProfile.ini", new File(profileDirectory, "WiimoteProfile.ini"), true,
context);

return wiimoteIniWritten;
sysPath = sysDirectory.getPath();
SetSysDirectory(sysPath);
}

private static void deleteDirectoryRecursively(@NonNull final File file)
@@ -240,26 +198,33 @@ public static String getUserDirectory()
return userPath;
}

public static String getSysDirectory()
{
if (!areDirectoriesAvailable)
{
throw new IllegalStateException(
"DirectoryInitialization must run before accessing the Sys directory!");
}
return sysPath;
}

public static File getGameListCache(Context context)
{
return new File(context.getExternalCacheDir(), "gamelist.cache");
}

private static boolean copyAsset(String asset, File output, Boolean overwrite, Context context)
private static boolean copyAsset(String asset, File output, Context context)
{
Log.verbose("[DirectoryInitialization] Copying File " + asset + " to " + output);

try
{
if (!output.exists() || overwrite)
try (InputStream in = context.getAssets().open(asset))
{
try (InputStream in = context.getAssets().open(asset))
try (OutputStream out = new FileOutputStream(output))
{
try (OutputStream out = new FileOutputStream(output))
{
copyFile(in, out);
return true;
}
copyFile(in, out);
return true;
}
}
}
@@ -271,8 +236,7 @@ private static boolean copyAsset(String asset, File output, Boolean overwrite, C
return false;
}

private static void copyAssetFolder(String assetFolder, File outputFolder, Boolean overwrite,
Context context)
private static void copyAssetFolder(String assetFolder, File outputFolder, Context context)
{
Log.verbose("[DirectoryInitialization] Copying Folder " + assetFolder + " to " +
outputFolder);
@@ -299,9 +263,8 @@ private static void copyAssetFolder(String assetFolder, File outputFolder, Boole
createdFolder = true;
}
copyAssetFolder(assetFolder + File.separator + file, new File(outputFolder, file),
overwrite, context);
copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), overwrite,
context);
copyAsset(assetFolder + File.separator + file, new File(outputFolder, file), context);
}
}
catch (IOException e)
@@ -322,18 +285,6 @@ private static void copyFile(InputStream in, OutputStream out) throws IOExceptio
}
}

private static void createWiimoteProfileDirectory(String directory)
{
File wiiPath = new File(directory);
if (!wiiPath.isDirectory())
{
if (!wiiPath.mkdirs())
{
Log.error("[DirectoryInitialization] Failed to create folder " + wiiPath.getAbsolutePath());
}
}
}

public static boolean preferOldFolderPicker(Context context)
{
// As of January 2021, ACTION_OPEN_DOCUMENT_TREE seems to be broken on the Nvidia Shield TV
@@ -433,7 +384,5 @@ private static void checkThemeSettings(Context context)
}
}

private static native void CreateUserDirectories();

private static native void SetSysDirectory(String path);
}
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: GPL-2.0-or-later

package org.dolphinemu.dolphinemu.utils;

import android.os.Looper;

public class LooperThread extends Thread
{
private Looper mLooper;

public LooperThread()
{
super();
}

public LooperThread(String name)
{
super(name);
}

@Override
public void run()
{
Looper.prepare();

synchronized (this)
{
mLooper = Looper.myLooper();
notifyAll();
}

Looper.loop();
}

public Looper getLooper()
{
if (!isAlive())
{
throw new IllegalStateException();
}

synchronized (this)
{
while (mLooper == null)
{
try
{
wait();
}
catch (InterruptedException ignored)
{
}
}
}

return mLooper;
}
}

This file was deleted.

This file was deleted.

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/colorControlNormal"
android:pathData="M7,21Q6.175,21 5.588,20.413Q5,19.825 5,19V6H4V4H9V3H15V4H20V6H19V19Q19,19.825 18.413,20.413Q17.825,21 17,21ZM17,6H7V19Q7,19 7,19Q7,19 7,19H17Q17,19 17,19Q17,19 17,19ZM9,17H11V8H9ZM13,17H15V8H13ZM7,6V19Q7,19 7,19Q7,19 7,19Q7,19 7,19Q7,19 7,19Z"/>
</vector>
@@ -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,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="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z" />
</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>
@@ -39,6 +39,18 @@
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

<TextView
android:id="@+id/old_controller_settings_warning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="?attr/colorErrorContainer"
android:text="@string/old_controller_settings"
android:textColor="?attr/colorOnErrorContainer"
android:visibility="invisible"
android:clickable="true"
android:focusable="false" />

<View
android:id="@+id/workaround_view"
android:layout_width="match_parent"
@@ -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,25 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/profile_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/profile_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />

</androidx.appcompat.widget.LinearLayoutCompat>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

This file was deleted.

@@ -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>
@@ -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>
@@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:paddingHorizontal="@dimen/spacing_medlarge"
android:paddingVertical="@dimen/spacing_medlarge">

<com.google.android.material.textview.MaterialTextView
android:id="@+id/text_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginStart="@dimen/spacing_small"
android:textColor="?attr/colorOnSurface"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_delete"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Wii Remote with Motion Plus Pointing" />

<Button
android:id="@+id/button_delete"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:contentDescription="@string/input_profile_delete"
android:tooltipText="@string/input_profile_delete"
app:icon="@drawable/ic_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_load"
app:layout_constraintStart_toEndOf="@id/text_name"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button_load"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:contentDescription="@string/input_profile_load"
android:tooltipText="@string/input_profile_load"
app:icon="@drawable/ic_load"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/button_save"
app:layout_constraintStart_toEndOf="@id/button_delete"
app:layout_constraintTop_toTopOf="parent" />

<Button
android:id="@+id/button_save"
style="?attr/materialIconButtonFilledTonalStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/spacing_small"
android:contentDescription="@string/input_profile_save"
android:tooltipText="@string/input_profile_save"
app:icon="@drawable/ic_save"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/button_load"
app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
@@ -20,9 +20,8 @@
android:title="@string/emulation_control_joystick_rel_center"/>

<item
android:id="@+id/menu_emulation_rumble"
android:checkable="true"
android:title="@string/emulation_control_rumble"/>
android:id="@+id/menu_emulation_choose_controller"
android:title="@string/emulation_choose_controller"/>

<item
android:id="@+id/menu_emulation_reset_overlay"
@@ -19,21 +19,12 @@
android:id="@+id/menu_emulation_joystick_rel_center"
android:checkable="true"
android:title="@string/emulation_control_joystick_rel_center"/>

<item
android:id="@+id/menu_emulation_rumble"
android:checkable="true"
android:title="@string/emulation_control_rumble"/>
</group>

<item
android:id="@+id/menu_emulation_choose_controller"
android:title="@string/emulation_choose_controller"/>

<item
android:id="@+id/menu_emulation_motion_controls"
android:title="@string/emulation_motion_controls"/>

<item
android:id="@+id/menu_emulation_ir_group"
android:title="@string/emulation_ir_group">
@@ -45,9 +36,6 @@
<item
android:id="@+id/menu_emulation_set_ir_mode"
android:title="@string/emulation_ir_mode"/>
<item
android:id="@+id/menu_emulation_set_ir_sensitivity"
android:title="@string/emulation_ir_sensitivity"/>
<item
android:id="@+id/menu_emulation_choose_doubletap"
android:title="@string/emulation_choose_doubletap"/>
@@ -210,14 +210,14 @@
<item>@string/extension_drums</item>
<item>@string/extension_turntable</item>
</string-array>
<string-array name="wiimoteExtensionsValues">
<item>None</item>
<item>Nunchuk</item>
<item>Classic</item>
<item>Guitar</item>
<item>Drums</item>
<item>Turntable</item>
</string-array>
<integer-array name="wiimoteExtensionsValues">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
<item>5</item>
</integer-array>

<!-- Texture Cache Accuracy Preference -->
<string-array name="textureCacheAccuracyEntries">
@@ -386,23 +386,6 @@
<item>2</item>
</integer-array>

<string-array name="controllersEntries">
<item>@string/controller_gc</item>
<item>@string/controller_wiimote</item>
<item>@string/controller_horizontal_wiimote</item>
<item>@string/controller_wiimote_plus_nunchuk</item>
<item>@string/controller_classic</item>
<item>@string/controller_none</item>
</string-array>
<string-array name="controllersValues">
<item>None</item>
<item>None</item>
<item>None</item>
<item>Nunchuk</item>
<item>Classic</item>
<item>None</item>
</string-array>

<string-array name="gcpadButtons">
<item>A</item>
<item>B</item>
@@ -6,109 +6,55 @@
<string name="host">app</string>
<string name="scheme">dolphinemu</string>

<string name="none">None</string>

<string name="controller_0">GameCube Controller 1</string>
<string name="controller_1">GameCube Controller 2</string>
<string name="controller_2">GameCube Controller 3</string>
<string name="controller_3">GameCube Controller 4</string>

<string name="controller_control">Control Stick</string>
<string name="controller_c">C Stick</string>
<string name="controller_trig">Triggers</string>
<string name="controller_dpad">D-Pad</string>
<string name="modifier_range">Modifier Range</string>
<string name="analog_radius">Analog Radius (High value = High sensitivity)</string>
<string name="analog_threshold">Analog Threshold (Low value = High sensitivity)</string>

<string name="wiimote_4">Wii Remote 1</string>
<string name="wiimote_5">Wii Remote 2</string>
<string name="wiimote_6">Wii Remote 3</string>
<string name="wiimote_7">Wii Remote 4</string>
<string name="wiimote_0">Wii Remote 1</string>
<string name="wiimote_1">Wii Remote 2</string>
<string name="wiimote_2">Wii Remote 3</string>
<string name="wiimote_3">Wii Remote 4</string>

<string name="wiimote_extension_4">Wii Remote Extension 1</string>
<string name="wiimote_extension_5">Wii Remote Extension 2</string>
<string name="wiimote_extension_6">Wii Remote Extension 3</string>
<string name="wiimote_extension_7">Wii Remote Extension 4</string>
<string name="wiimote_extension_0">Wii Remote Extension 1</string>
<string name="wiimote_extension_1">Wii Remote Extension 2</string>
<string name="wiimote_extension_2">Wii Remote Extension 3</string>
<string name="wiimote_extension_3">Wii Remote Extension 4</string>

<string name="wiimote">Wii Remote</string>
<string name="wiimote_general">Buttons</string>
<string name="wiimote_motion_simulation">Motion Simulation</string>
<string name="wiimote_motion_input">Motion Input</string>
<string name="wiimote_extensions">Extension</string>
<string name="wiimote_extensions_description">Choose and bind the Wii Remote extension.</string>
<string name="wiimote_ir">IR</string>
<string name="wiimote_swing">Swing</string>
<string name="wiimote_tilt">Tilt</string>
<string name="wiimote_shake">Shake</string>

<string name="input_device">Device</string>
<string name="input_device_all_devices">Create Mappings for Other Devices</string>
<string name="input_device_all_devices_description">Detects inputs from all devices, not just the selected device.</string>
<string name="input_profile">Profile</string>
<string name="input_profiles_empty">You haven\'t created any profiles yet.</string>
<string name="input_profiles">Profiles</string>
<string name="input_profile_new">(New Profile)</string>
<string name="input_profile_load">Load</string>
<string name="input_profile_save">Save</string>
<string name="input_profile_delete">Delete</string>
<string name="input_profile_confirm_load">Do you want to discard your current controller settings and load the profile \"%1$s\"?</string>
<string name="input_profile_confirm_save">Do you want to overwrite the profile \"%1$s\"?</string>
<string name="input_profile_confirm_delete">Do you want to delete the profile \"%1$s\"?</string>
<string name="input_reset_to_default">Default</string>
<string name="input_reset_to_default_description">Reset settings for this controller to the default.</string>
<string name="input_clear">Clear</string>
<string name="input_clear_description">Clear settings for this controller.</string>
<string name="input_reset_warning">Are you sure? Your current controller settings will be deleted.</string>
<string name="old_controller_settings">Your controller settings are from an old version of Dolphin and won\'t work in this version. Press \"Default\" to start over with new settings.</string>

<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_rumble_description">Press or move any input to set rumble.</string>

<!-- Generic buttons (Shared with lots of stuff) -->
<string name="generic_buttons">Buttons</string>
<string name="generic_up">Up</string>
<string name="generic_down">Down</string>
<string name="generic_left">Left</string>
<string name="generic_right">Right</string>
<string name="generic_forward">Forward</string>
<string name="generic_backward">Backward</string>
<string name="generic_stick">Stick</string>
<string name="generic_green">Green</string>
<string name="generic_red">Red</string>
<string name="generic_yellow">Yellow</string>
<string name="generic_blue">Blue</string>
<string name="generic_orange">Orange</string>

<!-- GameCube buttons (May be shared with other stuff too) -->
<string name="button_a">A</string>
<string name="button_b">B</string>
<string name="button_start">START</string>
<string name="button_x">X</string>
<string name="button_y">Y</string>
<string name="button_z">Z</string>
<string name="trigger_left">L</string>
<string name="trigger_right">R</string>

<!-- Wii Remote buttons (May be shared with other stuff too) -->
<string name="button_one">1</string>
<string name="button_two">2</string>
<string name="button_minus">-</string>
<string name="button_plus">+</string>
<string name="button_home">HOME</string>
<string name="ir_hide">Hide</string>
<string name="tilt_modifier">Modifier</string>
<string name="shake_x">X</string>
<string name="shake_y">Y</string>
<string name="shake_z">Z</string>

<!-- Nunchuk only buttons -->
<string name="nunchuk_button_c">C</string>

<!-- Classic only buttons -->
<string name="classic_button_zl">ZL</string>
<string name="classic_button_zr">ZR</string>
<string name="classic_leftstick">Left Stick</string>
<string name="classic_rightstick">Right Stick</string>

<!-- Guitar only buttons -->
<string name="guitar_frets">Frets</string>
<string name="guitar_strum">Strum</string>
<string name="guitar_whammy">Whammy</string>
<string name="guitar_whammy_bar">Bar</string>

<!-- Drums only buttons -->
<string name="drums_pads">Pads</string>
<string name="drums_pad_bass">Bass</string>

<!-- Turntable only buttons -->
<string name="turntable_button_green_left">Green Left</string>
<string name="turntable_button_red_left">Red Left</string>
<string name="turntable_button_blue_left">Blue Left</string>
<string name="turntable_button_green_right">Green Right</string>
<string name="turntable_button_red_right">Red Right</string>
<string name="turntable_button_blue_right">Blue Right</string>
<string name="turntable_button_euphoria">Euphoria</string>
<string name="turntable_table_left">Table Left</string>
<string name="turntable_table_right">Table Right</string>
<string name="turntable_effect">Effect</string>
<string name="turntable_effect_dial">Dial</string>
<string name="turntable_crossfade">Crossfade</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>
@@ -479,6 +425,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>
@@ -528,7 +475,6 @@
<string name="preferences_save_exit">Save and Exit</string>
<string name="preferences_game_properties">Game Properties</string>
<string name="preferences_game_properties_with_game_id">Game Properties: %1$s</string>
<string name="preferences_extensions">Extension Bindings</string>
<string name="game_ini_junk_title">Junk Data Found</string>
<string name="game_ini_junk_question">The settings file for this game contains extraneous data added by an old version of Dolphin. This will likely prevent global settings from working as intended.\n\nWould you like to fix this by deleting the settings file for this game? All game-specific settings and cheats that you have added will be removed. This cannot be undone.</string>

@@ -626,30 +572,22 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="emulation_control_opacity">Opacity</string>
<string name="emulation_control_adjustments">Adjust Controls</string>
<string name="emulation_control_joystick_rel_center">Relative Stick Center</string>
<string name="emulation_control_rumble">Rumble</string>
<string name="emulation_choose_controller">Choose Controller</string>
<string name="emulation_more_controller_settings">More Settings</string>
<string name="emulation_menu_help">Press Back to access the menu.\nLong press Back to exit emulation.</string>
<string name="emulation_touch_overlay_reset">Reset Overlay</string>
<string name="emulation_ir_group">Touch IR Pointer</string>
<string name="emulation_ir_recenter">Always recenter</string>
<string name="emulation_ir_mode">IR Mode</string>
<string name="emulation_ir_sensitivity">IR Sensitivity</string>
<string name="emulation_choose_doubletap">Double tap button</string>
<string name="emulation_motion_controls">Motion Controls</string>

<!-- GC Adapter Menu-->
<string name="gc_adapter_rumble">Enable Vibration</string>
<string name="gc_adapter_rumble_description">Enable the vibration function for this GameCube controller.</string>
<string name="gc_adapter_bongos">Bongo Controller</string>
<string name="gc_adapter_bongos_description">Enable this if you are using bongos on this port.</string>

<!-- Wii Remote Input Menu-->
<string name="header_wiimote_general">General</string>
<string name="header_controllers">Controllers</string>

<!-- Rumble -->
<string name="rumble_not_found">Device rumble not found</string>

<string name="path_not_changeable_scoped_storage">Due to the Scoped Storage policy in Android 11 and newer, you can\'t change this path.</string>
<string name="load_settings">Loading Settings…</string>
<string name="setting_not_runtime_editable">This setting can\'t be changed while a game is running.</string>
@@ -674,14 +612,12 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="unavailable_paths">Dolphin does not have permission to access one or more configured paths. Would you like to fix this before starting?</string>

<!-- Misc -->
<string name="pitch">Total Pitch</string>
<string name="yaw">Total Yaw</string>
<string name="vertical_offset">Vertical Offset</string>
<string name="enabled">Enabled</string>
<string name="default_values">Default Values</string>
<string name="slider_setting_value">%1$d%2$s</string>
<string name="disc_number">Disc %1$d</string>
<string name="replug_gc_adapter">GameCube Adapter couldn\'t be opened. Please re-plug the device.</string>
<string name="disabled_gc_overlay_notice">GameCube Controller 1 is set to \"None\"</string>
<string name="disabled_gc_overlay_notice">The selected GameCube controller is set to \"None\"</string>
<string name="ignore_warning_alert_messages">Ignore for this session</string>

<!-- UI CPU Core selection -->
@@ -825,14 +761,6 @@ It can efficiently compress both junk data and encrypted Wii data.
<string name="wiimote_emulated">Emulated</string>
<string name="wiimote_real">Real Wii Remote (DolphinBar required)</string>

<!-- Controller Types -->
<string name="controller_gc">GameCube Controller</string>
<string name="controller_wiimote">Wii Remote</string>
<string name="controller_horizontal_wiimote">Horizontal Wii Remote</string>
<string name="controller_wiimote_plus_nunchuk">Wii Remote + Nunchuk</string>
<string name="controller_classic">Classic Controller</string>
<string name="controller_none">None</string>

<!-- Gamepad Controls -->
<string name="gamepad_start">Start</string>
<string name="gamepad_d_pad">D-Pad</string>