Android: Allow editing settings during emulation

This commit is contained in:
JosJuice 2020-07-22 21:59:30 +02:00
parent 736505f020
commit 9c19309a03
21 changed files with 219 additions and 13 deletions

View File

@ -34,6 +34,8 @@ import androidx.fragment.app.FragmentManager;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivity;
import org.dolphinemu.dolphinemu.features.settings.utils.SettingsFile;
import org.dolphinemu.dolphinemu.fragments.EmulationFragment;
import org.dolphinemu.dolphinemu.fragments.MenuFragment;
@ -101,7 +103,8 @@ public final class EmulationActivity extends AppCompatActivity
MENU_ACTION_LOAD_SLOT6, MENU_ACTION_EXIT, MENU_ACTION_CHANGE_DISC,
MENU_ACTION_RESET_OVERLAY, MENU_SET_IR_SENSITIVITY, MENU_ACTION_CHOOSE_DOUBLETAP,
MENU_ACTION_SCREEN_ORIENTATION, MENU_ACTION_MOTION_CONTROLS, MENU_ACTION_PAUSE_EMULATION,
MENU_ACTION_UNPAUSE_EMULATION, MENU_ACTION_OVERLAY_CONTROLS})
MENU_ACTION_UNPAUSE_EMULATION, MENU_ACTION_OVERLAY_CONTROLS, MENU_ACTION_SETTINGS_CORE,
MENU_ACTION_SETTINGS_GRAPHICS})
public @interface MenuAction
{
}
@ -140,6 +143,8 @@ public final class EmulationActivity extends AppCompatActivity
public static final int MENU_ACTION_PAUSE_EMULATION = 31;
public static final int MENU_ACTION_UNPAUSE_EMULATION = 32;
public static final int MENU_ACTION_OVERLAY_CONTROLS = 33;
public static final int MENU_ACTION_SETTINGS_CORE = 34;
public static final int MENU_ACTION_SETTINGS_GRAPHICS = 35;
private static SparseIntArray buttonsActionsMap = new SparseIntArray();
@ -648,6 +653,14 @@ public final class EmulationActivity extends AppCompatActivity
showMotionControlsOptions();
return;
case MENU_ACTION_SETTINGS_CORE:
SettingsActivity.launch(this, MenuTag.CONFIG);
return;
case MENU_ACTION_SETTINGS_GRAPHICS:
SettingsActivity.launch(this, MenuTag.GRAPHICS);
return;
case MENU_ACTION_EXIT:
mEmulationFragment.stopEmulation();
finish();

View File

@ -19,6 +19,12 @@ public class AbstractLegacySetting implements AbstractSetting
return settings.isGameSpecific() && settings.getSection(mFile, mSection).exists(mKey);
}
@Override
public boolean isRuntimeEditable()
{
return false;
}
@Override
public boolean delete(Settings settings)
{

View File

@ -4,5 +4,7 @@ public interface AbstractSetting
{
boolean isOverridden(Settings settings);
boolean isRuntimeEditable();
boolean delete(Settings settings);
}

View File

@ -26,6 +26,12 @@ public class AdHocBooleanSetting implements AbstractBooleanSetting
return NativeConfig.isOverridden(mFile, mSection, mKey);
}
@Override
public boolean isRuntimeEditable()
{
return true;
}
@Override
public boolean delete(Settings settings)
{

View File

@ -1,5 +1,9 @@
package org.dolphinemu.dolphinemu.features.settings.model;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public enum BooleanSetting implements AbstractBooleanSetting
{
// These entries have the same names and order as in C++, just for consistency.
@ -94,6 +98,17 @@ public enum BooleanSetting implements AbstractBooleanSetting
MAIN_JIT_REGISTER_CACHE_OFF(Settings.FILE_DOLPHIN, Settings.SECTION_DEBUG, "JitRegisterCacheOff",
false);
private static final BooleanSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new BooleanSetting[]{
MAIN_DSP_HLE,
MAIN_CPU_THREAD,
MAIN_OVERRIDE_REGION_SETTINGS,
MAIN_WII_SD_CARD, // Can actually be changed, but specific code is required
MAIN_DSP_JIT
};
private static final Set<BooleanSetting> NOT_RUNTIME_EDITABLE =
new HashSet<>(Arrays.asList(NOT_RUNTIME_EDITABLE_ARRAY));
private final String mFile;
private final String mSection;
private final String mKey;
@ -116,6 +131,18 @@ public enum BooleanSetting implements AbstractBooleanSetting
return NativeConfig.isOverridden(mFile, mSection, mKey);
}
@Override
public boolean isRuntimeEditable()
{
for (BooleanSetting setting : NOT_RUNTIME_EDITABLE)
{
if (setting == this)
return false;
}
return NativeConfig.isSettingSaveable(mFile, mSection, mKey);
}
@Override
public boolean delete(Settings settings)
{

View File

@ -29,6 +29,12 @@ public enum FloatSetting implements AbstractFloatSetting
return NativeConfig.isOverridden(mFile, mSection, mKey);
}
@Override
public boolean isRuntimeEditable()
{
return NativeConfig.isSettingSaveable(mFile, mSection, mKey);
}
@Override
public boolean delete(Settings settings)
{

View File

@ -2,6 +2,10 @@ package org.dolphinemu.dolphinemu.features.settings.model;
import org.dolphinemu.dolphinemu.NativeLibrary;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public enum IntSetting implements AbstractIntSetting
{
// These entries have the same names and order as in C++, just for consistency.
@ -34,6 +38,16 @@ public enum IntSetting implements AbstractIntSetting
LOGGER_VERBOSITY(Settings.FILE_LOGGER, Settings.SECTION_LOGGER_OPTIONS, "Verbosity", 1);
private static final IntSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new IntSetting[]{
MAIN_CPU_CORE,
MAIN_GC_LANGUAGE,
MAIN_SLOT_A, // Can actually be changed, but specific code is required
MAIN_SLOT_B, // Can actually be changed, but specific code is required
};
private static final Set<IntSetting> NOT_RUNTIME_EDITABLE =
new HashSet<>(Arrays.asList(NOT_RUNTIME_EDITABLE_ARRAY));
private final String mFile;
private final String mSection;
private final String mKey;
@ -56,6 +70,18 @@ public enum IntSetting implements AbstractIntSetting
return NativeConfig.isOverridden(mFile, mSection, mKey);
}
@Override
public boolean isRuntimeEditable()
{
for (IntSetting setting : NOT_RUNTIME_EDITABLE)
{
if (setting == this)
return false;
}
return NativeConfig.isSettingSaveable(mFile, mSection, mKey);
}
@Override
public boolean delete(Settings settings)
{

View File

@ -105,6 +105,10 @@ public class Settings implements Closeable
}
else
{
// Loading game INIs while the core is running will mess with the game INIs loaded by the core
if (NativeLibrary.IsRunning())
throw new IllegalStateException("Attempted to load game INI while emulating");
NativeConfig.loadGameInis(mGameId, mRevision);
loadCustomGameSettings(mGameId, view);
}
@ -155,9 +159,14 @@ public class Settings implements Closeable
NativeConfig.save(NativeConfig.LAYER_BASE_OR_CURRENT);
// Notify the native code of the changes
NativeLibrary.ReloadConfig();
NativeLibrary.ReloadWiimoteConfig();
if (!NativeLibrary.IsRunning())
{
// Notify the native code of the changes to legacy settings
NativeLibrary.ReloadConfig();
NativeLibrary.ReloadWiimoteConfig();
}
// LogManager does use the new config system, but doesn't pick up on changes automatically
NativeLibrary.ReloadLoggerConfig();
NativeLibrary.UpdateGCAdapterScanThread();

View File

@ -2,6 +2,10 @@ package org.dolphinemu.dolphinemu.features.settings.model;
import org.dolphinemu.dolphinemu.NativeLibrary;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public enum StringSetting implements AbstractStringSetting
{
// These entries have the same names and order as in C++, just for consistency.
@ -20,6 +24,13 @@ public enum StringSetting implements AbstractStringSetting
GFX_ENHANCE_POST_SHADER(Settings.FILE_GFX, Settings.SECTION_GFX_ENHANCEMENTS,
"PostProcessingShader", "");
private static final StringSetting[] NOT_RUNTIME_EDITABLE_ARRAY = new StringSetting[]{
MAIN_GFX_BACKEND,
};
private static final Set<StringSetting> NOT_RUNTIME_EDITABLE =
new HashSet<>(Arrays.asList(NOT_RUNTIME_EDITABLE_ARRAY));
private final String mFile;
private final String mSection;
private final String mKey;
@ -42,6 +53,18 @@ public enum StringSetting implements AbstractStringSetting
return NativeConfig.isOverridden(mFile, mSection, mKey);
}
@Override
public boolean isRuntimeEditable()
{
for (StringSetting setting : NOT_RUNTIME_EDITABLE)
{
if (setting == this)
return false;
}
return NativeConfig.isSettingSaveable(mFile, mSection, mKey);
}
@Override
public boolean delete(Settings settings)
{

View File

@ -1,5 +1,6 @@
package org.dolphinemu.dolphinemu.features.settings.model.view;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting;
import org.dolphinemu.dolphinemu.features.settings.model.Settings;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
@ -67,4 +68,13 @@ public abstract class SettingsItem
AbstractSetting setting = getSetting();
return setting != null && setting.isOverridden(settings);
}
public boolean isEditable()
{
if (!NativeLibrary.IsRunning())
return true;
AbstractSetting setting = getSetting();
return setting != null && setting.isRuntimeEditable();
}
}

View File

@ -300,6 +300,13 @@ public final class SettingsFragmentPresenter
BooleanSetting.MAIN_DSP_JIT.isOverridden(settings);
}
@Override
public boolean isRuntimeEditable()
{
return BooleanSetting.MAIN_DSP_HLE.isRuntimeEditable() &&
BooleanSetting.MAIN_DSP_JIT.isRuntimeEditable();
}
@Override
public boolean delete(Settings settings)
{

View File

@ -66,6 +66,12 @@ public final class CheckBoxSettingViewHolder extends SettingViewHolder
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
mCheckbox.toggle();
getAdapter().onBooleanClick(mItem, getAdapterPosition(), mCheckbox.isChecked());

View File

@ -52,6 +52,12 @@ public final class FilePickerViewHolder extends SettingViewHolder
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
if (mFilePicker.getRequestType() == MainPresenter.REQUEST_DIRECTORY)
{
getAdapter().onFilePickerDirectoryClick(mItem);

View File

@ -51,6 +51,12 @@ public final class InputBindingSettingViewHolder extends SettingViewHolder
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
getAdapter().onInputBindingClick(mItem, getAdapterPosition());
setStyle(mTextSettingName, mItem);

View File

@ -51,6 +51,12 @@ public class RumbleBindingViewHolder extends SettingViewHolder
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
getAdapter().onInputBindingClick(mItem, getAdapterPosition());
setStyle(mTextSettingName, mItem);

View File

@ -1,11 +1,16 @@
package org.dolphinemu.dolphinemu.features.settings.ui.viewholder;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import androidx.recyclerview.widget.RecyclerView;
import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter;
@ -34,6 +39,15 @@ public abstract class SettingViewHolder extends RecyclerView.ViewHolder
{
boolean overridden = settingsItem.isOverridden(mAdapter.getSettings());
textView.setTypeface(null, overridden ? Typeface.BOLD : Typeface.NORMAL);
if (!settingsItem.isEditable())
textView.setPaintFlags(textView.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
protected static void showNotRuntimeEditableError()
{
Toast.makeText(DolphinApplication.getAppContext(), R.string.setting_not_runtime_editable,
Toast.LENGTH_SHORT).show();
}
/**

View File

@ -87,6 +87,12 @@ public final class SingleChoiceViewHolder extends SettingViewHolder
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
int position = getAdapterPosition();
if (mItem instanceof SingleChoiceSetting)
{

View File

@ -56,6 +56,12 @@ public final class SliderViewHolder extends SettingViewHolder
@Override
public void onClick(View clicked)
{
if (!mItem.isEditable())
{
showNotRuntimeEditableError();
return;
}
getAdapter().onSliderClick(mItem, getAdapterPosition());
setStyle(mTextSettingName, mItem);

View File

@ -50,6 +50,9 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
.append(R.id.menu_screen_orientation, EmulationActivity.MENU_ACTION_SCREEN_ORIENTATION);
buttonsActionsMap.append(R.id.menu_change_disc, EmulationActivity.MENU_ACTION_CHANGE_DISC);
buttonsActionsMap.append(R.id.menu_exit, EmulationActivity.MENU_ACTION_EXIT);
buttonsActionsMap.append(R.id.menu_settings_core, EmulationActivity.MENU_ACTION_SETTINGS_CORE);
buttonsActionsMap.append(R.id.menu_settings_graphics,
EmulationActivity.MENU_ACTION_SETTINGS_GRAPHICS);
}
public static MenuFragment newInstance(String title)
@ -83,15 +86,6 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
updatePauseUnpauseVisibility();
Settings settings = ((EmulationActivity) getActivity()).getSettings();
if (BooleanSetting.MAIN_ENABLE_SAVESTATES.getBoolean(settings))
{
options.findViewById(R.id.menu_quicksave).setVisibility(View.VISIBLE);
options.findViewById(R.id.menu_quickload).setVisibility(View.VISIBLE);
options.findViewById(R.id.menu_emulation_save_root).setVisibility(View.VISIBLE);
options.findViewById(R.id.menu_emulation_load_root).setVisibility(View.VISIBLE);
}
PackageManager packageManager = requireActivity().getPackageManager();
if (!packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN))
@ -146,6 +140,22 @@ public final class MenuFragment extends Fragment implements View.OnClickListener
return rootView;
}
@Override
public void onResume()
{
super.onResume();
LinearLayout options = requireView().findViewById(R.id.layout_options);
Settings settings = ((EmulationActivity) requireActivity()).getSettings();
boolean savestatesEnabled = BooleanSetting.MAIN_ENABLE_SAVESTATES.getBoolean(settings);
int savestateVisibility = savestatesEnabled ? View.VISIBLE : View.GONE;
options.findViewById(R.id.menu_quicksave).setVisibility(savestateVisibility);
options.findViewById(R.id.menu_quickload).setVisibility(savestateVisibility);
options.findViewById(R.id.menu_emulation_save_root).setVisibility(savestateVisibility);
options.findViewById(R.id.menu_emulation_load_root).setVisibility(savestateVisibility);
}
private void updatePauseUnpauseVisibility()
{
boolean paused = EmulationActivity.getHasUserPausedEmulation();

View File

@ -30,6 +30,16 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<Button
android:id="@+id/menu_settings_core"
android:text="@string/grid_menu_config"
style="@style/InGameMenuOption"/>
<Button
android:id="@+id/menu_settings_graphics"
android:text="@string/grid_menu_graphics_settings"
style="@style/InGameMenuOption"/>
<Button
android:id="@+id/menu_pause_emulation"
android:text="@string/pause_emulation"

View File

@ -378,6 +378,7 @@
<string name="write_permission_needed">You need to allow write access to external storage for the emulator to work</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>
<string name="emulation_change_disc">Change Disc</string>
<string name="external_storage_not_mounted">The external storage needs to be available in order to use Dolphin</string>