Merge pull request #3547 from sigmabeta/android-config-rewrite

[Android] Rewrite settings UI.
This commit is contained in:
Ryan Houdek 2016-01-23 12:40:05 -05:00
commit df5bdbe279
67 changed files with 2649 additions and 5858 deletions

View File

@ -9,10 +9,10 @@
<item>@string/cached_interpreter</item>
<item>@string/jit_arm64_recompiler</item>
</string-array>
<string-array name="int_emu_cores" translatable="false">
<integer-array name="int_emu_cores" translatable="false">
<item>0</item>
<item>5</item>
<item>4</item>
</string-array>
</integer-array>
</resources>

View File

@ -56,9 +56,9 @@
android:label="@string/add_directory_title"/>
<activity
android:name=".activities.SettingsActivity"
android:name=".ui.settings.SettingsActivity"
android:theme="@style/DolphinSettingsGamecube"
android:label="@string/grid_menu_settings"/>
android:label="@string/grid_menu_core_settings"/>
<activity
android:name=".activities.EmulationActivity"
@ -71,8 +71,6 @@
<service android:name=".services.AssetCopyService"/>
<service android:name=".services.SettingsSaveService"/>
<provider
android:name=".model.GameProvider"
android:authorities="${applicationId}.provider"

View File

@ -1,48 +0,0 @@
package org.dolphinemu.dolphinemu.activities;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import org.dolphinemu.dolphinemu.fragments.SettingsFragment;
import org.dolphinemu.dolphinemu.services.SettingsSaveService;
import org.dolphinemu.dolphinemu.utils.Log;
public final class SettingsActivity extends AppCompatActivity
{
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Display the fragment as the main content.
getFragmentManager().beginTransaction()
.replace(android.R.id.content, new SettingsFragment(), "settings_fragment")
.commit();
}
/**
* If this is called, the user has left the settings screen (potentially through the
* home button) and will expect their changes to be persisted. So we kick off an
* IntentService which will do so on a background thread.
*/
@Override
protected void onStop()
{
super.onStop();
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...");
// Copy assets into appropriate locations.
Intent settingsSaver = new Intent(this, SettingsSaveService.class);
startService(settingsSaver);
}
public static void launch(Context context)
{
Intent settings = new Intent(context, SettingsActivity.class);
context.startActivity(settings);
}
}

View File

@ -1,155 +0,0 @@
package org.dolphinemu.dolphinemu.fragments;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Environment;
import android.preference.ListPreference;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import javax.microedition.khronos.opengles.GL10;
public final class SettingsFragment extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener
{
private SharedPreferences mPreferences;
private ListPreference mVideoBackendPreference;
private final EGLHelper mEglHelper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
private final String mVendor = mEglHelper.getGL().glGetString(GL10.GL_VENDOR);
private final String mVersion = mEglHelper.getGL().glGetString(GL10.GL_VERSION);
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Load the preferences from an XML resource
addPreferencesFromResource(R.xml.preferences);
// TODO Below here is effectively ported from the old VideoSettingsFragment. There is
// TODO probably a simpler way to do this, but potentially could require UI discussion/feedback.
// Setting valid video backends.
mVideoBackendPreference = (ListPreference) findPreference("gpuPref");
final boolean deviceSupportsGL = mEglHelper.supportsOpenGL();
final boolean deviceSupportsGLES3 = mEglHelper.supportsGLES3();
if (deviceSupportsGL)
{
mVideoBackendPreference.setEntries(R.array.videoBackendEntriesGL);
mVideoBackendPreference.setEntryValues(R.array.videoBackendValuesGL);
}
else if (deviceSupportsGLES3)
{
mVideoBackendPreference.setEntries(R.array.videoBackendEntriesGLES3);
mVideoBackendPreference.setEntryValues(R.array.videoBackendValuesGLES3);
}
else
{
mVideoBackendPreference.setEntries(R.array.videoBackendEntriesNoGLES3);
mVideoBackendPreference.setEntryValues(R.array.videoBackendValuesNoGLES3);
}
//
// Set available post processing shaders
//
List<CharSequence> shader_names = new ArrayList<CharSequence>();
List<CharSequence> shader_values = new ArrayList<CharSequence>();
// Disabled option
shader_names.add("Disabled");
shader_values.add("");
// TODO Since shaders are included with the APK, we know what they are at build-time. We should
// TODO be able to run this logic somehow at build-time and not rely on the device doing it.
File shaders_folder = new File(Environment.getExternalStorageDirectory() + File.separator + "dolphin-emu" + File.separator + "Shaders");
if (shaders_folder.exists())
{
File[] shaders = shaders_folder.listFiles();
for (File file : shaders)
{
if (file.isFile())
{
String filename = file.getName();
if (filename.endsWith(".glsl"))
{
// Strip the extension and put it in to the list
shader_names.add(filename.substring(0, filename.lastIndexOf('.')));
shader_values.add(filename.substring(0, filename.lastIndexOf('.')));
}
}
}
}
final ListPreference shader_preference = (ListPreference) findPreference("postProcessingShader");
shader_preference.setEntries(shader_names.toArray(new CharSequence[shader_names.size()]));
shader_preference.setEntryValues(shader_values.toArray(new CharSequence[shader_values.size()]));
//
// Disable all options if Software Rendering is used.
//
// Note that the numeric value in 'getPreference()'
// denotes the placement on the UI. So if more elements are
// added to the video settings, these may need to change.
//
mPreferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
if (mVideoBackendPreference.getValue().equals("Software Renderer"))
{
findPreference("enhancements").setEnabled(false);
findPreference("hacks").setEnabled(false);
findPreference("showFPS").setEnabled(false);
}
else if (mVideoBackendPreference.getValue().equals("OGL"))
{
findPreference("enhancements").setEnabled(true);
findPreference("hacks").setEnabled(true);
findPreference("showFPS").setEnabled(true);
// Check if we support stereo
// If we support desktop GL then we must support at least OpenGL 3.2
// If we only support OpenGLES then we need both OpenGLES 3.1 and AEP
if ((mEglHelper.supportsOpenGL() && mEglHelper.GetVersion() >= 320) ||
(mEglHelper.supportsGLES3() && mEglHelper.GetVersion() >= 310 && mEglHelper.SupportsExtension("GL_ANDROID_extension_pack_es31a")))
findPreference("StereoscopyScreen").setEnabled(true);
else
findPreference("StereoscopyScreen").setEnabled(false);
}
// Also set a listener, so that if someone changes the video backend, it will disable
// the video settings, upon the user choosing "Software Rendering".
mPreferences.registerOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key)
{
if (key.equals("gpuPref"))
{
if (preferences.getString(key, "Software Renderer").equals("Software Renderer"))
{
findPreference("enhancements").setEnabled(false);
findPreference("hacks").setEnabled(false);
findPreference("showFPS").setEnabled(false);
}
else if (preferences.getString(key, "Software Renderer").equals("OGL"))
{
findPreference("enhancements").setEnabled(true);
findPreference("hacks").setEnabled(true);
findPreference("showFPS").setEnabled(true);
}
}
}
}

View File

@ -0,0 +1,28 @@
package org.dolphinemu.dolphinemu.model.settings;
public final class BooleanSetting extends Setting
{
private boolean mValue;
public BooleanSetting(String key, String section, boolean value)
{
super(key, section);
mValue = value;
}
public boolean getValue()
{
return mValue;
}
public void setValue(boolean value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return mValue ? "True" : "False";
}
}

View File

@ -0,0 +1,28 @@
package org.dolphinemu.dolphinemu.model.settings;
public final class FloatSetting extends Setting
{
private float mValue;
public FloatSetting(String key, String section, float value)
{
super(key, section);
mValue = value;
}
public float getValue()
{
return mValue;
}
public void setValue(float value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return Float.toString(mValue);
}
}

View File

@ -0,0 +1,28 @@
package org.dolphinemu.dolphinemu.model.settings;
public final class IntSetting extends Setting
{
private int mValue;
public IntSetting(String key, String section, int value)
{
super(key, section);
mValue = value;
}
public int getValue()
{
return mValue;
}
public void setValue(int value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return Integer.toString(mValue);
}
}

View File

@ -0,0 +1,48 @@
package org.dolphinemu.dolphinemu.model.settings;
/**
* Abstraction for a setting item as read from / written to Dolphin's configuration ini files.
* These files generally consist of a key/value pair, though the type of value is ambiguous and
* must be inferred at read-time. The type of value determines which child of this class is used
* to represent the Setting.
*/
public abstract class Setting
{
private String mKey;
private String mSection;
/**
* Base constructor.
*
* @param key Everything to the left of the = in a line from the ini file.
* @param section The corresponding recent section header; e.g. [Core] or [Enhancements] without the brackets.
*/
public Setting(String key, String section)
{
mKey = key;
mSection = section;
}
/**
*
* @return The identifier used to write this setting to the ini file.
*/
public String getKey()
{
return mKey;
}
/**
*
* @return The name of the header under which this Setting should be written in the ini file.
*/
public String getSection()
{
return mSection;
}
/**
* @return A representation of this Setting's backing value converted to a String (e.g. for serialization).
*/
public abstract String getValueAsString();
}

View File

@ -0,0 +1,56 @@
package org.dolphinemu.dolphinemu.model.settings;
import java.util.HashMap;
/**
* A semantically-related group of Settings objects. These Settings are
* internally stored as a Hashmap.
*/
public final class SettingSection
{
private String mName;
private HashMap<String, Setting> mSettings = new HashMap<>();
/**
* Create a new SettingSection with no Settings in it.
*
* @param name The header of this section; e.g. [Core] or [Enhancements] without the brackets.
*/
public SettingSection(String name)
{
mName = name;
}
public String getName()
{
return mName;
}
/**
* Convenience method; inserts a value directly into the backing Hashmap.
*
* @param key The key where the Setting will be inserted.
* @param setting The Setting to be inserted.
*/
public void putSetting(String key, Setting setting)
{
mSettings.put(key, setting);
}
/**
* Convenience method; gets a value directly from the backing Hashmap.
*
* @param key Used to retrieve the Setting.
* @return A Setting object (you should probably cast this before using)
*/
public Setting getSetting(String key)
{
return mSettings.get(key);
}
public HashMap<String, Setting> getSettings()
{
return mSettings;
}
}

View File

@ -0,0 +1,28 @@
package org.dolphinemu.dolphinemu.model.settings;
public final class StringSetting extends Setting
{
private String mValue;
public StringSetting(String key, String section, String value)
{
super(key, section);
mValue = value;
}
public String getValue()
{
return mValue;
}
public void setValue(String value)
{
mValue = value;
}
@Override
public String getValueAsString()
{
return mValue;
}
}

View File

@ -0,0 +1,56 @@
package org.dolphinemu.dolphinemu.model.settings.view;
import org.dolphinemu.dolphinemu.model.settings.BooleanSetting;
import org.dolphinemu.dolphinemu.model.settings.Setting;
public final class CheckBoxSetting extends SettingsItem
{
private boolean mDefaultValue;
public CheckBoxSetting(String key, String section, int titleId, int descriptionId, boolean defaultValue, Setting setting)
{
super(key, section, setting, titleId, descriptionId);
mDefaultValue = defaultValue;
}
public boolean isChecked()
{
if (getSetting() == null)
{
return mDefaultValue;
}
BooleanSetting setting = (BooleanSetting) getSetting();
return setting.getValue();
}
/**
* Write a value to the backing boolean. If that boolean was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param checked Pretty self explanatory.
* @return null if overwritten successfully; otherwise, a newly created BooleanSetting.
*/
public BooleanSetting setChecked(boolean checked)
{
if (getSetting() == null)
{
BooleanSetting setting = new BooleanSetting(getKey(), getSection(), checked);
setSetting(setting);
return setting;
}
else
{
BooleanSetting setting = (BooleanSetting) getSetting();
setting.setValue(checked);
return null;
}
}
@Override
public int getType()
{
return TYPE_CHECKBOX;
}
}

View File

@ -0,0 +1,18 @@
package org.dolphinemu.dolphinemu.model.settings.view;
import org.dolphinemu.dolphinemu.model.settings.Setting;
public final class HeaderSetting extends SettingsItem
{
public HeaderSetting(String key, Setting setting, int titleId, int descriptionId)
{
super(key, null, setting, titleId, descriptionId);
}
@Override
public int getType()
{
return SettingsItem.TYPE_HEADER;
}
}

View File

@ -0,0 +1,106 @@
package org.dolphinemu.dolphinemu.model.settings.view;
import org.dolphinemu.dolphinemu.model.settings.Setting;
/**
* ViewModel abstraction for an Item in the RecyclerView powering SettingsFragments.
* Each one corresponds to a {@link Setting} object, so this class's subclasses
* should vaguely correspond to those subclasses. There are a few with multiple analogues
* and a few with none (Headers, for example, do not correspond to anything in the ini
* file.)
*/
public abstract class SettingsItem
{
public static final int TYPE_HEADER = 0;
public static final int TYPE_CHECKBOX = 1;
public static final int TYPE_SINGLE_CHOICE = 2;
public static final int TYPE_SLIDER = 3;
public static final int TYPE_SUBMENU = 4;
private String mKey;
private String mSection;
private Setting mSetting;
private int mNameId;
private int mDescriptionId;
/**
* Base constructor. Takes a key / section name in case the third parameter, the Setting,
* is null; in which case, one can be constructed and saved using the key / section.
*
* @param key Identifier for the Setting represented by this Item.
* @param section Section to which the Setting belongs.
* @param setting A possibly-null backing Setting, to be modified on UI events.
* @param nameId Resource ID for a text string to be displayed as this setting's name.
* @param descriptionId Resource ID for a text string to be displayed as this setting's description.
*/
public SettingsItem(String key, String section, Setting setting, int nameId, int descriptionId)
{
mKey = key;
mSection = section;
mSetting = setting;
mNameId = nameId;
mDescriptionId = descriptionId;
}
/**
*
* @return The identifier for the backing Setting.
*/
public String getKey()
{
return mKey;
}
/**
*
* @return The header under which the backing Setting belongs.
*/
public String getSection()
{
return mSection;
}
/**
*
* @return The backing Setting, possibly null.
*/
public Setting getSetting()
{
return mSetting;
}
/**
* Replace the backing setting with a new one. Generally used in cases where
* the backing setting is null.
*
* @param setting A non-null Setting.
*/
public void setSetting(Setting setting)
{
mSetting = setting;
}
/**
*
* @return A resource ID for a text string representing this Setting's name.
*/
public int getNameId()
{
return mNameId;
}
public int getDescriptionId()
{
return mDescriptionId;
}
/**
* Used by {@link org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter}'s onCreateViewHolder()
* method to determine which type of ViewHolder should be created.
*
* @return An integer (ideally, one of the constants defined in this file)
*/
public abstract int getType();
}

View File

@ -0,0 +1,73 @@
package org.dolphinemu.dolphinemu.model.settings.view;
import org.dolphinemu.dolphinemu.model.settings.IntSetting;
import org.dolphinemu.dolphinemu.model.settings.Setting;
public final class SingleChoiceSetting extends SettingsItem
{
private int mDefaultValue;
private int mChoicesId;
private int mValuesId;
public SingleChoiceSetting(String key, String section, int titleId, int descriptionId, int choicesId, int valuesId, int defaultValue, Setting setting)
{
super(key, section, setting, titleId, descriptionId);
mValuesId = valuesId;
mChoicesId = choicesId;
mDefaultValue = defaultValue;
}
public int getChoicesId()
{
return mChoicesId;
}
public int getValuesId()
{
return mValuesId;
}
public int getSelectedValue()
{
if (getSetting() != null)
{
IntSetting setting = (IntSetting) getSetting();
return setting.getValue();
}
else
{
return mDefaultValue;
}
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
*/
public IntSetting setSelectedValue(int selection)
{
if (getSetting() == null)
{
IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection);
return null;
}
}
@Override
public int getType()
{
return TYPE_SINGLE_CHOICE;
}
}

View File

@ -0,0 +1,118 @@
package org.dolphinemu.dolphinemu.model.settings.view;
import org.dolphinemu.dolphinemu.model.settings.FloatSetting;
import org.dolphinemu.dolphinemu.model.settings.IntSetting;
import org.dolphinemu.dolphinemu.model.settings.Setting;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
public final class SliderSetting extends SettingsItem
{
private int mMax;
private int mDefaultValue;
private String mUnits;
public SliderSetting(String key, String section, int titleId, int descriptionId, int max, String units, int defaultValue, Setting setting)
{
super(key, section, setting, titleId, descriptionId);
mMax = max;
mUnits = units;
mDefaultValue = defaultValue;
}
public int getMax()
{
return mMax;
}
public int getSelectedValue()
{
Setting setting = getSetting();
if (setting == null)
{
return mDefaultValue;
}
if (setting instanceof IntSetting)
{
IntSetting intSetting = (IntSetting) setting;
return intSetting.getValue();
}
else if (setting instanceof FloatSetting)
{
FloatSetting floatSetting = (FloatSetting) setting;
if (floatSetting.getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT))
{
return Math.round(floatSetting.getValue() * 100);
}
else
{
return Math.round(floatSetting.getValue());
}
}
else
{
Log.error("[SliderSetting] Error casting setting type.");
return -1;
}
}
/**
* Write a value to the backing int. If that int was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the int.
* @return null if overwritten successfully otherwise; a newly created IntSetting.
*/
public IntSetting setSelectedValue(int selection)
{
if (getSetting() == null)
{
IntSetting setting = new IntSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
IntSetting setting = (IntSetting) getSetting();
setting.setValue(selection);
return null;
}
}
/**
* Write a value to the backing float. If that float was previously null,
* initializes a new one and returns it, so it can be added to the Hashmap.
*
* @param selection New value of the float.
* @return null if overwritten successfully otherwise; a newly created FloatSetting.
*/
public FloatSetting setSelectedValue(float selection)
{
if (getSetting() == null)
{
FloatSetting setting = new FloatSetting(getKey(), getSection(), selection);
setSetting(setting);
return setting;
}
else
{
FloatSetting setting = (FloatSetting) getSetting();
setting.setValue(selection);
return null;
}
}
public String getUnits()
{
return mUnits;
}
@Override
public int getType()
{
return TYPE_SLIDER;
}
}

View File

@ -0,0 +1,25 @@
package org.dolphinemu.dolphinemu.model.settings.view;
import org.dolphinemu.dolphinemu.model.settings.Setting;
public final class SubmenuSetting extends SettingsItem
{
private String mMenuKey;
public SubmenuSetting(String key, Setting setting, int titleId, int descriptionId, String menuKey)
{
super(key, null, setting, titleId, descriptionId);
mMenuKey = menuKey;
}
public String getMenuKey()
{
return mMenuKey;
}
@Override
public int getType()
{
return TYPE_SUBMENU;
}
}

View File

@ -13,7 +13,6 @@ import android.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.UserPreferences;
import java.io.File;
import java.io.FileOutputStream;
@ -61,10 +60,6 @@ public final class AssetCopyService extends IntentService
copyAsset("GCPadNew.ini", ConfigDir + File.separator + "GCPadNew.ini");
copyAsset("WiimoteNew.ini", ConfigDir + File.separator + "WiimoteNew.ini");
// Load the configuration keys set in the Dolphin ini and gfx ini files
// into the application's shared preferences.
UserPreferences.LoadIniToPrefs(this);
// Record the fact that we've done this before, so we don't do it on every launch.
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
SharedPreferences.Editor editor = preferences.edit();

View File

@ -1,28 +0,0 @@
package org.dolphinemu.dolphinemu.services;
import android.app.IntentService;
import android.content.Intent;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.UserPreferences;
/**
* IntentServices, unlike regular services, inherently run on a background thread.
* This IntentService saves all the options the user set in the Java-based UI into
* INI files the native code can read.
*/
public final class SettingsSaveService extends IntentService
{
public SettingsSaveService()
{
super("SettingsSaveService");
}
@Override
protected void onHandleIntent(Intent intent)
{
Log.verbose("[SettingsSaveService] Saving settings to INI files...");
UserPreferences.SavePrefsToIni(this);
Log.verbose("[SettingsSaveService] Save successful.");
}
}

View File

@ -0,0 +1,157 @@
package org.dolphinemu.dolphinemu.ui;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
/**
* Implementation from:
* https://gist.github.com/lapastillaroja/858caf1a82791b6c1a36
*/
public final class DividerItemDecoration extends RecyclerView.ItemDecoration
{
private Drawable mDivider;
private boolean mShowFirstDivider = false;
private boolean mShowLastDivider = false;
public DividerItemDecoration(Context context, AttributeSet attrs)
{
final TypedArray a = context
.obtainStyledAttributes(attrs, new int[]{android.R.attr.listDivider});
mDivider = a.getDrawable(0);
a.recycle();
}
public DividerItemDecoration(Context context, AttributeSet attrs, boolean showFirstDivider,
boolean showLastDivider)
{
this(context, attrs);
mShowFirstDivider = showFirstDivider;
mShowLastDivider = showLastDivider;
}
public DividerItemDecoration(Drawable divider)
{
mDivider = divider;
}
public DividerItemDecoration(Drawable divider, boolean showFirstDivider,
boolean showLastDivider)
{
this(divider);
mShowFirstDivider = showFirstDivider;
mShowLastDivider = showLastDivider;
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state)
{
super.getItemOffsets(outRect, view, parent, state);
if (mDivider == null)
{
return;
}
if (parent.getChildPosition(view) < 1)
{
return;
}
if (getOrientation(parent) == LinearLayoutManager.VERTICAL)
{
outRect.top = mDivider.getIntrinsicHeight();
}
else
{
outRect.left = mDivider.getIntrinsicWidth();
}
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state)
{
if (mDivider == null)
{
super.onDrawOver(c, parent, state);
return;
}
// Initialization needed to avoid compiler warning
int left = 0, right = 0, top = 0, bottom = 0, size;
int orientation = getOrientation(parent);
int childCount = parent.getChildCount();
if (orientation == LinearLayoutManager.VERTICAL)
{
size = mDivider.getIntrinsicHeight();
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
}
else
{ //horizontal
size = mDivider.getIntrinsicWidth();
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
}
for (int i = mShowFirstDivider ? 0 : 1; i < childCount; i++)
{
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
if (orientation == LinearLayoutManager.VERTICAL)
{
top = child.getTop() - params.topMargin;
bottom = top + size;
}
else
{ //horizontal
left = child.getLeft() - params.leftMargin;
right = left + size;
}
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
// show last divider
if (mShowLastDivider && childCount > 0)
{
View child = parent.getChildAt(childCount - 1);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
if (orientation == LinearLayoutManager.VERTICAL)
{
top = child.getBottom() + params.bottomMargin;
bottom = top + size;
}
else
{ // horizontal
left = child.getRight() + params.rightMargin;
right = left + size;
}
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private int getOrientation(RecyclerView parent)
{
if (parent.getLayoutManager() instanceof LinearLayoutManager)
{
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
return layoutManager.getOrientation();
}
else
{
throw new IllegalStateException(
"DividerItemDecoration can only be used with a LinearLayoutManager.");
}
}
}

View File

@ -16,10 +16,10 @@ import android.view.View;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
import org.dolphinemu.dolphinemu.activities.SettingsActivity;
import org.dolphinemu.dolphinemu.adapters.PlatformPagerAdapter;
import org.dolphinemu.dolphinemu.model.GameProvider;
import org.dolphinemu.dolphinemu.ui.platform.PlatformGamesView;
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.StartupHandler;
/**
@ -115,9 +115,9 @@ public final class MainActivity extends AppCompatActivity implements MainView
}
@Override
public void launchSettingsActivity()
public void launchSettingsActivity(String menuTag)
{
SettingsActivity.launch(this);
SettingsActivity.launch(this, menuTag);
}
@Override

View File

@ -7,6 +7,7 @@ import org.dolphinemu.dolphinemu.DolphinApplication;
import org.dolphinemu.dolphinemu.NativeLibrary;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.GameDatabase;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
@ -41,8 +42,12 @@ public final class MainPresenter
{
switch (itemId)
{
case R.id.menu_settings:
mView.launchSettingsActivity();
case R.id.menu_settings_core:
mView.launchSettingsActivity(SettingsFile.FILE_NAME_DOLPHIN);
return true;
case R.id.menu_settings_video:
mView.launchSettingsActivity(SettingsFile.FILE_NAME_GFX);
return true;
case R.id.menu_refresh:

View File

@ -32,7 +32,7 @@ public interface MainView
void refreshFragmentScreenshot(int fragmentPosition);
void launchSettingsActivity();
void launchSettingsActivity(String menuTag);
void launchFileListActivity();

View File

@ -20,11 +20,12 @@ import android.support.v17.leanback.widget.RowPresenter;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.activities.AddDirectoryActivity;
import org.dolphinemu.dolphinemu.activities.EmulationActivity;
import org.dolphinemu.dolphinemu.activities.SettingsActivity;
import org.dolphinemu.dolphinemu.adapters.GameRowPresenter;
import org.dolphinemu.dolphinemu.adapters.SettingsRowPresenter;
import org.dolphinemu.dolphinemu.model.Game;
import org.dolphinemu.dolphinemu.model.TvSettingsItem;
import org.dolphinemu.dolphinemu.ui.settings.SettingsActivity;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
import org.dolphinemu.dolphinemu.utils.StartupHandler;
import org.dolphinemu.dolphinemu.viewholders.TvGameViewHolder;
@ -112,9 +113,9 @@ public final class TvMainActivity extends Activity implements MainView
}
@Override
public void launchSettingsActivity()
public void launchSettingsActivity(String menuTag)
{
SettingsActivity.launch(this);
SettingsActivity.launch(this, SettingsFile.FILE_NAME_DOLPHIN);
}
@Override
@ -229,9 +230,13 @@ public final class TvMainActivity extends Activity implements MainView
R.drawable.ic_refresh_tv,
R.string.grid_menu_refresh));
rowItems.add(new TvSettingsItem(R.id.menu_settings,
R.drawable.ic_settings_tv,
R.string.grid_menu_settings));
rowItems.add(new TvSettingsItem(R.id.menu_settings_core,
R.drawable.ic_settings_core_tv,
R.string.grid_menu_core_settings));
rowItems.add(new TvSettingsItem(R.id.menu_settings_video,
R.drawable.ic_settings_graphics_tv,
R.string.grid_menu_core_settings));
rowItems.add(new TvSettingsItem(R.id.button_add_directory,
R.drawable.ic_add_tv,

View File

@ -0,0 +1,106 @@
package org.dolphinemu.dolphinemu.ui.settings;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import java.util.HashMap;
public final class SettingsActivity extends AppCompatActivity implements SettingsActivityView
{
private SettingsActivityPresenter mPresenter = new SettingsActivityPresenter(this);
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_settings);
Intent launcher = getIntent();
String filename = launcher.getStringExtra(ARGUMENT_FILE_NAME);
mPresenter.onCreate(savedInstanceState, filename);
}
/**
* If this is called, the user has left the settings screen (potentially through the
* home button) and will expect their changes to be persisted. So we kick off an
* IntentService which will do so on a background thread.
*/
@Override
protected void onStop()
{
super.onStop();
mPresenter.onStop(isFinishing());
}
@Override
public void showSettingsFragment(String menuTag, boolean addToStack)
{
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_content, SettingsFragment.newInstance(menuTag), SettingsFragment.FRAGMENT_TAG);
if (addToStack)
{
transaction.addToBackStack(null);
}
transaction.commit();
}
@Override
public HashMap<String, SettingSection> getSettings()
{
return mPresenter.getSettings();
}
@Override
public void setSettings(HashMap<String, SettingSection> settings)
{
mPresenter.setSettings(settings);
}
@Override
public void onSettingsFileLoaded(HashMap<String, SettingSection> settings)
{
SettingsFragmentView fragment = getFragment();
if (fragment != null)
{
fragment.onSettingsFileLoaded(settings);
}
}
@Override
public void showToastMessage(String message)
{
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
private SettingsFragment getFragment()
{
return (SettingsFragment) getSupportFragmentManager().findFragmentByTag(SettingsFragment.FRAGMENT_TAG);
}
public static final String ARGUMENT_FILE_NAME = BuildConfig.APPLICATION_ID + ".file_name";
public static void launch(Context context, String menuTag)
{
Intent settings = new Intent(context, SettingsActivity.class);
settings.putExtra(ARGUMENT_FILE_NAME, menuTag);
context.startActivity(settings);
}
}

View File

@ -0,0 +1,97 @@
package org.dolphinemu.dolphinemu.ui.settings;
import android.os.Bundle;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
import java.util.HashMap;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.schedulers.Schedulers;
public final class SettingsActivityPresenter
{
private SettingsActivityView mView;
private String mFileName;
private HashMap<String, SettingSection> mSettingsBySection;
public SettingsActivityPresenter(SettingsActivityView view)
{
mView = view;
}
public void onCreate(Bundle savedInstanceState, final String filename)
{
mFileName = filename;
if (savedInstanceState == null)
{
SettingsFile.readFile(mFileName)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<HashMap<String, SettingSection>>()
{
@Override
public void call(HashMap<String, SettingSection> settingsBySection)
{
mSettingsBySection = settingsBySection;
mView.onSettingsFileLoaded(settingsBySection);
}
},
new Action1<Throwable>()
{
@Override
public void call(Throwable throwable)
{
Log.error("[SettingsActivityPresenter] Error reading file " + filename + ".ini: "+ throwable.getMessage());
mView.onSettingsFileLoaded(null);
}
});
mView.showSettingsFragment(mFileName, false);
}
}
public void setSettings(HashMap<String, SettingSection> settings)
{
mSettingsBySection = settings;
}
public HashMap<String, SettingSection> getSettings()
{
return mSettingsBySection;
}
public void onStop(boolean finishing)
{
if (mSettingsBySection != null && finishing)
{
Log.debug("[SettingsActivity] Settings activity stopping. Saving settings to INI...");
SettingsFile.saveFile(mFileName, mSettingsBySection)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1<Boolean>()
{
@Override
public void call(Boolean aBoolean)
{
mView.showToastMessage("Saved successfully to " + mFileName + ".ini");
}
},
new Action1<Throwable>()
{
@Override
public void call(Throwable throwable)
{
mView.showToastMessage("Error saving " + mFileName + ".ini: " + throwable.getMessage());
}
});
}
}
}

View File

@ -0,0 +1,51 @@
package org.dolphinemu.dolphinemu.ui.settings;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import java.util.HashMap;
/**
* Abstraction for the Activity that manages SettingsFragments.
*/
public interface SettingsActivityView
{
/**
* Show a new SettingsFragment.
*
* @param menuTag Identifier for the settings group that should be displayed.
* @param addToStack Whether or not this fragment should replace a previous one.
*/
void showSettingsFragment(String menuTag, boolean addToStack);
/**
* 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
* read operation.
*
* @return A possibly null Hashmap of Settings.
*/
HashMap<String, SettingSection> getSettings();
/**
* Used to provide the Activity with a Settings Hashmap if a Fragment already
* has one; for example, if a rotation occurs, the Fragment will not be killed,
* but the Activity will, so the Activity needs to have its Hashmap resupplied.
*
* @param settings The Fragment's Settings hashmap.
*/
void setSettings(HashMap<String, SettingSection> settings);
/**
* Called when an asynchronous load operation completes.
*
* @param settings The (possibly null) result of the load operation.
*/
void onSettingsFileLoaded(HashMap<String, SettingSection> settings);
/**
* Display a popup text message on screen.
*
* @param message The contents of the onscreen message.
*/
void showToastMessage(String message);
}

View File

@ -0,0 +1,334 @@
package org.dolphinemu.dolphinemu.ui.settings;
import android.content.Context;
import android.content.DialogInterface;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.SeekBar;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.BooleanSetting;
import org.dolphinemu.dolphinemu.model.settings.FloatSetting;
import org.dolphinemu.dolphinemu.model.settings.IntSetting;
import org.dolphinemu.dolphinemu.model.settings.view.CheckBoxSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.model.settings.view.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SliderSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SubmenuSetting;
import org.dolphinemu.dolphinemu.ui.settings.viewholder.CheckBoxSettingViewHolder;
import org.dolphinemu.dolphinemu.ui.settings.viewholder.HeaderViewHolder;
import org.dolphinemu.dolphinemu.ui.settings.viewholder.SettingViewHolder;
import org.dolphinemu.dolphinemu.ui.settings.viewholder.SingleChoiceViewHolder;
import org.dolphinemu.dolphinemu.ui.settings.viewholder.SliderViewHolder;
import org.dolphinemu.dolphinemu.ui.settings.viewholder.SubmenuViewHolder;
import org.dolphinemu.dolphinemu.utils.Log;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
import java.util.ArrayList;
public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolder>
implements DialogInterface.OnClickListener, SeekBar.OnSeekBarChangeListener
{
private SettingsFragmentView mView;
private Context mContext;
private ArrayList<SettingsItem> mSettings;
private SettingsItem mClickedItem;
private int mSeekbarProgress;
private AlertDialog mDialog;
private TextView mTextSliderValue;
public SettingsAdapter(SettingsFragmentView view, Context context)
{
mView = view;
mContext = context;
}
@Override
public SettingViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View view;
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
switch (viewType)
{
case SettingsItem.TYPE_HEADER:
view = inflater.inflate(R.layout.list_item_settings_header, parent, false);
return new HeaderViewHolder(view, this);
case SettingsItem.TYPE_CHECKBOX:
view = inflater.inflate(R.layout.list_item_setting_checkbox, parent, false);
return new CheckBoxSettingViewHolder(view, this);
case SettingsItem.TYPE_SINGLE_CHOICE:
view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new SingleChoiceViewHolder(view, this);
case SettingsItem.TYPE_SLIDER:
view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new SliderViewHolder(view, this);
case SettingsItem.TYPE_SUBMENU:
view = inflater.inflate(R.layout.list_item_setting, parent, false);
return new SubmenuViewHolder(view, this);
default:
Log.error("[SettingsAdapter] Invalid view type: " + viewType);
return null;
}
}
@Override
public void onBindViewHolder(SettingViewHolder holder, int position)
{
holder.bind(getItem(position));
}
private SettingsItem getItem(int position)
{
return mSettings.get(position);
}
@Override
public int getItemCount()
{
if (mSettings != null)
{
return mSettings.size();
}
else
{
return 0;
}
}
@Override
public int getItemViewType(int position)
{
return getItem(position).getType();
}
public void setSettings(ArrayList<SettingsItem> settings)
{
mSettings = settings;
notifyDataSetChanged();
}
public void onBooleanClick(CheckBoxSetting item, int position, boolean checked)
{
BooleanSetting setting = item.setChecked(checked);
notifyItemChanged(position);
if (setting != null)
{
mView.putSetting(setting);
}
}
public void onSingleChoiceClick(SingleChoiceSetting item)
{
mClickedItem = item;
int value = getSelectionForSingleChoiceValue(item);
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
builder.setTitle(item.getNameId());
builder.setSingleChoiceItems(item.getChoicesId(), value, this);
mDialog = builder.show();
}
public void onSliderClick(SliderSetting item)
{
mClickedItem = item;
mSeekbarProgress = item.getSelectedValue();
AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
LayoutInflater inflater = LayoutInflater.from(mView.getActivity());
View view = inflater.inflate(R.layout.dialog_seekbar, null);
builder.setTitle(item.getNameId());
builder.setView(view);
builder.setPositiveButton(R.string.dialog_seekbar_pos, this);
builder.setNegativeButton(R.string.dialog_seekbar_neg, this);
mDialog = builder.show();
mTextSliderValue = (TextView) view.findViewById(R.id.text_value);
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
TextView units = (TextView) view.findViewById(R.id.text_units);
units.setText(item.getUnits());
SeekBar seekbar = (SeekBar) view.findViewById(R.id.seekbar);
seekbar.setMax(item.getMax());
seekbar.setProgress(mSeekbarProgress);
seekbar.setOnSeekBarChangeListener(this);
}
public void onSubmenuClick(SubmenuSetting item)
{
mView.loadSubMenu(item.getMenuKey());
}
@Override
public void onClick(DialogInterface dialog, int which)
{
if (mClickedItem instanceof SingleChoiceSetting)
{
SingleChoiceSetting scSetting = (SingleChoiceSetting) mClickedItem;
int value = getValueForSingleChoiceSelection(scSetting, which);
// Get the backing Setting, which may be null (if for example it was missing from the file)
IntSetting setting = scSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
else
{
if (scSetting.getKey().equals(SettingsFile.KEY_XFB_METHOD))
{
putXfbSetting(which);
}
}
closeDialog();
}
else if (mClickedItem instanceof SliderSetting)
{
SliderSetting sliderSetting = (SliderSetting) mClickedItem;
if (sliderSetting.getSetting() instanceof FloatSetting)
{
float value;
if (sliderSetting.getKey().equals(SettingsFile.KEY_OVERCLOCK_PERCENT))
{
value = mSeekbarProgress / 100.0f;
}
else
{
value = (float) mSeekbarProgress;
}
FloatSetting setting = sliderSetting.setSelectedValue(value);
if (setting != null)
{
mView.putSetting(setting);
}
}
else
{
IntSetting setting = sliderSetting.setSelectedValue(mSeekbarProgress);
if (setting != null)
{
mView.putSetting(setting);
}
}
}
mClickedItem = null;
mSeekbarProgress = -1;
}
public void closeDialog()
{
if (mDialog != null)
{
mDialog.dismiss();
mDialog = null;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser)
{
mSeekbarProgress = progress;
mTextSliderValue.setText(String.valueOf(mSeekbarProgress));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar)
{
}
@Override
public void onStopTrackingTouch(SeekBar seekBar)
{
}
private int getValueForSingleChoiceSelection(SingleChoiceSetting item, int which)
{
int valuesId = item.getValuesId();
if (valuesId > 0)
{
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
return valuesArray[which];
}
else
{
return which;
}
}
private int getSelectionForSingleChoiceValue(SingleChoiceSetting item)
{
int value = item.getSelectedValue();
int valuesId = item.getValuesId();
if (valuesId > 0)
{
int[] valuesArray = mContext.getResources().getIntArray(valuesId);
for (int index = 0; index < valuesArray.length; index++)
{
int current = valuesArray[index];
if (current == value)
{
return index;
}
}
}
else
{
return value;
}
return -1;
}
public void putXfbSetting(int which)
{
BooleanSetting xfbEnable = null;
BooleanSetting xfbReal = null;
switch (which)
{
case 0:
xfbEnable = new BooleanSetting(SettingsFile.KEY_XFB, SettingsFile.SECTION_GFX_SETTINGS, false);
xfbReal = new BooleanSetting(SettingsFile.KEY_XFB_REAL, SettingsFile.SECTION_GFX_SETTINGS, false);
break;
case 1:
xfbEnable = new BooleanSetting(SettingsFile.KEY_XFB, SettingsFile.SECTION_GFX_SETTINGS, true);
xfbReal = new BooleanSetting(SettingsFile.KEY_XFB_REAL, SettingsFile.SECTION_GFX_SETTINGS, false);
break;
case 2:
xfbEnable = new BooleanSetting(SettingsFile.KEY_XFB, SettingsFile.SECTION_GFX_SETTINGS, true);
xfbReal = new BooleanSetting(SettingsFile.KEY_XFB_REAL, SettingsFile.SECTION_GFX_SETTINGS, true);
break;
}
mView.putSetting(xfbEnable);
mView.putSetting(xfbReal);
}
}

View File

@ -0,0 +1,141 @@
package org.dolphinemu.dolphinemu.ui.settings;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.dolphinemu.dolphinemu.BuildConfig;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.Setting;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.ui.DividerItemDecoration;
import java.util.ArrayList;
import java.util.HashMap;
public final class SettingsFragment extends Fragment implements SettingsFragmentView
{
private SettingsFragmentPresenter mPresenter = new SettingsFragmentPresenter(this);
private SettingsActivityView mActivity;
private SettingsAdapter mAdapter;
@Override
public void onAttach(Context context)
{
super.onAttach(context);
mActivity = (SettingsActivityView) context;
mPresenter.onAttach();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setRetainInstance(true);
String menuTag = getArguments().getString(ARGUMENT_MENU_TAG);
mAdapter = new SettingsAdapter(this, getActivity());
mPresenter.onCreate(menuTag);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_settings, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState)
{
LinearLayoutManager manager = new LinearLayoutManager(getActivity());
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.list_settings);
recyclerView.setAdapter(mAdapter);
recyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), null));
SettingsActivityView activity = (SettingsActivityView) getActivity();
HashMap<String, SettingSection> settings = activity.getSettings();
mPresenter.onViewCreated(settings);
}
@Override
public void onDetach()
{
super.onDetach();
mActivity = null;
if (mAdapter != null)
{
mAdapter.closeDialog();
}
}
@Override
public void onSettingsFileLoaded(HashMap<String, SettingSection> settings)
{
mPresenter.setSettings(settings);
}
@Override
public void passSettingsToActivity(HashMap<String, SettingSection> settings)
{
if (mActivity != null)
{
mActivity.setSettings(settings);
}
}
@Override
public void showSettingsList(ArrayList<SettingsItem> settingsList)
{
mAdapter.setSettings(settingsList);
}
@Override
public void loadSubMenu(String menuKey)
{
mActivity.showSettingsFragment(menuKey, true);
}
@Override
public void showToastMessage(String message)
{
mActivity.showToastMessage(message);
}
@Override
public void putSetting(Setting setting)
{
mPresenter.putSetting(setting);
}
public static final String FRAGMENT_TAG = BuildConfig.APPLICATION_ID + ".fragment.settings";
public static final String ARGUMENT_MENU_TAG = FRAGMENT_TAG + ".menu_tag";
public static Fragment newInstance(String menuTag)
{
SettingsFragment fragment = new SettingsFragment();
Bundle arguments = new Bundle();
arguments.putString(ARGUMENT_MENU_TAG, menuTag);
fragment.setArguments(arguments);
return fragment;
}
}

View File

@ -0,0 +1,257 @@
package org.dolphinemu.dolphinemu.ui.settings;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.BooleanSetting;
import org.dolphinemu.dolphinemu.model.settings.IntSetting;
import org.dolphinemu.dolphinemu.model.settings.Setting;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import org.dolphinemu.dolphinemu.model.settings.view.CheckBoxSetting;
import org.dolphinemu.dolphinemu.model.settings.view.HeaderSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.model.settings.view.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SliderSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SubmenuSetting;
import org.dolphinemu.dolphinemu.utils.EGLHelper;
import org.dolphinemu.dolphinemu.utils.SettingsFile;
import java.util.ArrayList;
import java.util.HashMap;
public final class SettingsFragmentPresenter
{
private SettingsFragmentView mView;
private String mMenuTag;
private HashMap<String, SettingSection> mSettings;
private ArrayList<SettingsItem> mSettingsList;
public SettingsFragmentPresenter(SettingsFragmentView view)
{
mView = view;
}
public void onCreate(String menuTag)
{
mMenuTag = menuTag;
}
public void onViewCreated(HashMap<String, SettingSection> settings)
{
setSettings(settings);
}
/**
* If the screen is rotated, the Activity will forget the settings map. This fragment
* won't, though; so rather than have the Activity reload from disk, have the fragment pass
* the settings map back to the Activity.
*/
public void onAttach()
{
if (mSettings != null)
{
mView.passSettingsToActivity(mSettings);
}
}
public void putSetting(Setting setting)
{
mSettings.get(setting.getSection()).putSetting(setting.getKey(), setting);
}
public void setSettings(HashMap<String, SettingSection> settings)
{
if (mSettingsList == null)
{
mSettings = settings;
loadSettingsList();
}
else
{
mView.showSettingsList(mSettingsList);
}
}
private void loadSettingsList()
{
ArrayList<SettingsItem> sl = new ArrayList<>();
switch (mMenuTag)
{
case SettingsFile.FILE_NAME_DOLPHIN:
addCoreSettings(sl);
break;
case SettingsFile.FILE_NAME_GFX:
addGraphicsSettings(sl);
break;
case SettingsFile.SECTION_GFX_ENHANCEMENTS:
addEnhanceSettings(sl);
break;
case SettingsFile.SECTION_GFX_HACKS:
addHackSettings(sl);
break;
default:
mView.showToastMessage("Unimplemented menu.");
return;
}
mSettingsList = sl;
mView.showSettingsList(mSettingsList);
}
private void addCoreSettings(ArrayList<SettingsItem> sl)
{
Setting cpuCore = null;
Setting dualCore = null;
Setting overclockEnable = null;
Setting overclock = null;
if (mSettings != null)
{
cpuCore = mSettings.get(SettingsFile.SECTION_CORE).getSetting(SettingsFile.KEY_CPU_CORE);
dualCore = mSettings.get(SettingsFile.SECTION_CORE).getSetting(SettingsFile.KEY_DUAL_CORE);
overclockEnable = mSettings.get(SettingsFile.SECTION_CORE).getSetting(SettingsFile.KEY_OVERCLOCK_ENABLE);
overclock = mSettings.get(SettingsFile.SECTION_CORE).getSetting(SettingsFile.KEY_OVERCLOCK_PERCENT);
}
else
{
mSettings = new HashMap<>();
mSettings.put(SettingsFile.SECTION_CORE, new SettingSection(SettingsFile.SECTION_CORE));
mView.passSettingsToActivity(mSettings);
}
// TODO Set default value for cpuCore based on arch.
sl.add(new SingleChoiceSetting(SettingsFile.KEY_CPU_CORE, SettingsFile.SECTION_CORE, R.string.cpu_core, 0, R.array.string_emu_cores, R.array.int_emu_cores, 4, cpuCore));
sl.add(new CheckBoxSetting(SettingsFile.KEY_DUAL_CORE, SettingsFile.SECTION_CORE, R.string.dual_core, R.string.dual_core_descrip, true, dualCore));
sl.add(new CheckBoxSetting(SettingsFile.KEY_OVERCLOCK_ENABLE, SettingsFile.SECTION_CORE, R.string.overclock_enable, R.string.overclock_enable_description, false, overclockEnable));
sl.add(new SliderSetting(SettingsFile.KEY_OVERCLOCK_PERCENT, SettingsFile.SECTION_CORE, R.string.overclock_title, 0, 400, "%", 100, overclock));
}
private void addGraphicsSettings(ArrayList<SettingsItem> sl)
{
Setting showFps = null;
if (mSettings != null)
{
showFps = mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_SHOW_FPS);
}
else
{
mSettings = new HashMap<>();
mSettings.put(SettingsFile.SECTION_GFX_SETTINGS, new SettingSection(SettingsFile.SECTION_GFX_SETTINGS));
mSettings.put(SettingsFile.SECTION_GFX_ENHANCEMENTS, new SettingSection(SettingsFile.SECTION_GFX_ENHANCEMENTS));
mSettings.put(SettingsFile.SECTION_GFX_HACKS, new SettingSection(SettingsFile.SECTION_GFX_HACKS));
mView.passSettingsToActivity(mSettings);
}
sl.add(new CheckBoxSetting(SettingsFile.KEY_SHOW_FPS, SettingsFile.SECTION_GFX_SETTINGS, R.string.show_fps, 0, true, showFps));
sl.add(new SubmenuSetting(null, null, R.string.enhancements, 0, SettingsFile.SECTION_GFX_ENHANCEMENTS));
sl.add(new SubmenuSetting(null, null, R.string.hacks, 0, SettingsFile.SECTION_GFX_HACKS));
}
private void addEnhanceSettings(ArrayList<SettingsItem> sl)
{
Setting resolution = mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_INTERNAL_RES);
Setting fsaa = mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_FSAA);
Setting anisotropic = mSettings.get(SettingsFile.SECTION_GFX_ENHANCEMENTS).getSetting(SettingsFile.KEY_ANISOTROPY);
Setting efbScaledCopy = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_SCALED_EFB);
Setting perPixel = mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_PER_PIXEL);
Setting forceFilter = mSettings.get(SettingsFile.SECTION_GFX_ENHANCEMENTS).getSetting(SettingsFile.KEY_FORCE_FILTERING);
Setting disableFog = mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_DISABLE_FOG);
sl.add(new SingleChoiceSetting(SettingsFile.KEY_INTERNAL_RES, SettingsFile.SECTION_GFX_SETTINGS, R.string.internal_resolution, R.string.internal_resolution_descrip, R.array.internalResolutionEntries, R.array.internalResolutionValues, 0, resolution));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_FSAA, SettingsFile.SECTION_GFX_SETTINGS, R.string.FSAA, R.string.FSAA_descrip, R.array.FSAAEntries, R.array.FSAAValues, 0, fsaa));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_ANISOTROPY, SettingsFile.SECTION_GFX_ENHANCEMENTS, R.string.anisotropic_filtering, R.string.anisotropic_filtering_descrip, R.array.anisotropicFilteringEntries, R.array.anisotropicFilteringValues, 0, anisotropic));
// TODO
// Setting shader = mSettings.get(SettingsFile.SECTION_GFX_ENHANCEMENTS).getSetting(SettingsFile.KEY_POST_SHADER)
// sl.add(new SingleChoiceSetting(.getKey(), , R.string., R.string._descrip, R.array., R.array.));
sl.add(new CheckBoxSetting(SettingsFile.KEY_SCALED_EFB, SettingsFile.SECTION_GFX_HACKS, R.string.scaled_efb_copy, R.string.scaled_efb_copy_descrip, true, efbScaledCopy));
sl.add(new CheckBoxSetting(SettingsFile.KEY_PER_PIXEL, SettingsFile.SECTION_GFX_SETTINGS, R.string.per_pixel_lighting, R.string.per_pixel_lighting_descrip, false, perPixel));
sl.add(new CheckBoxSetting(SettingsFile.KEY_FORCE_FILTERING, SettingsFile.SECTION_GFX_ENHANCEMENTS, R.string.force_texture_filtering, R.string.force_texture_filtering_descrip, false, forceFilter));
sl.add(new CheckBoxSetting(SettingsFile.KEY_DISABLE_FOG, SettingsFile.SECTION_GFX_SETTINGS, R.string.disable_fog, R.string.disable_fog_descrip, false, disableFog));
/*
Check if we support stereo
If we support desktop GL then we must support at least OpenGL 3.2
If we only support OpenGLES then we need both OpenGLES 3.1 and AEP
*/
EGLHelper helper = new EGLHelper(EGLHelper.EGL_OPENGL_ES2_BIT);
if ((helper.supportsOpenGL() && helper.GetVersion() >= 320) ||
(helper.supportsGLES3() && helper.GetVersion() >= 310 && helper.SupportsExtension("GL_ANDROID_extension_pack_es31a")))
{
sl.add(new SubmenuSetting(null, null, R.string.stereoscopy, 0, SettingsFile.SECTION_STEREOSCOPY));
}
}
private void addHackSettings(ArrayList<SettingsItem> sl)
{
int xfbValue = getXfbValue();
Setting skipEFB = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_SKIP_EFB);
Setting ignoreFormat = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_IGNORE_FORMAT);
Setting efbToTexture = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_EFB_TEXTURE);
Setting texCacheAccuracy = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_TEXCACHE_ACCURACY);
IntSetting xfb = new IntSetting(SettingsFile.KEY_XFB, SettingsFile.SECTION_GFX_HACKS, xfbValue);
Setting fastDepth = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_FAST_DEPTH);
Setting aspectRatio = mSettings.get(SettingsFile.SECTION_GFX_HACKS).getSetting(SettingsFile.KEY_ASPECT_RATIO);
sl.add(new HeaderSetting(null, null, R.string.embedded_frame_buffer, 0));
sl.add(new CheckBoxSetting(SettingsFile.KEY_SKIP_EFB, SettingsFile.SECTION_GFX_HACKS, R.string.skip_efb_access, R.string.skip_efb_access_descrip, false, skipEFB));
sl.add(new CheckBoxSetting(SettingsFile.KEY_IGNORE_FORMAT, SettingsFile.SECTION_GFX_HACKS, R.string.ignore_format_changes, R.string.ignore_format_changes_descrip, false, ignoreFormat));
sl.add(new CheckBoxSetting(SettingsFile.KEY_EFB_TEXTURE, SettingsFile.SECTION_GFX_HACKS, R.string.efb_copy_method, R.string.efb_copy_method_descrip, true, efbToTexture));
sl.add(new HeaderSetting(null, null, R.string.texture_cache, 0));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_TEXCACHE_ACCURACY, SettingsFile.SECTION_GFX_HACKS, R.string.texture_cache_accuracy, R.string.texture_cache_accuracy_descrip, R.array.textureCacheAccuracyEntries, R.array.textureCacheAccuracyValues, 128, texCacheAccuracy));
sl.add(new HeaderSetting(null, null, R.string.external_frame_buffer, 0));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_XFB_METHOD, SettingsFile.SECTION_GFX_HACKS, R.string.external_frame_buffer, R.string.external_frame_buffer_descrip, R.array.externalFrameBufferEntries, R.array.externalFrameBufferValues, 0, xfb));
sl.add(new HeaderSetting(null, null, R.string.other, 0));
sl.add(new CheckBoxSetting(SettingsFile.KEY_FAST_DEPTH, SettingsFile.SECTION_GFX_HACKS, R.string.fast_depth_calculation, R.string.fast_depth_calculation_descrip, true, fastDepth));
sl.add(new SingleChoiceSetting(SettingsFile.KEY_ASPECT_RATIO, SettingsFile.SECTION_GFX_HACKS, R.string.aspect_ratio, R.string.aspect_ratio_descrip, R.array.aspectRatioEntries, R.array.aspectRatioValues, 0, aspectRatio));
}
private int getXfbValue()
{
int xfbValue;
try
{
boolean usingXFB = ((BooleanSetting) mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_XFB)).getValue();
boolean usingRealXFB = ((BooleanSetting) mSettings.get(SettingsFile.SECTION_GFX_SETTINGS).getSetting(SettingsFile.KEY_XFB_REAL)).getValue();
if (!usingXFB)
{
xfbValue = 0;
}
else if (!usingRealXFB)
{
xfbValue = 1;
}
else
{
xfbValue = 2;
}
}
catch (NullPointerException ex)
{
xfbValue = 0;
}
return xfbValue;
}
}

View File

@ -0,0 +1,68 @@
package org.dolphinemu.dolphinemu.ui.settings;
import android.app.Activity;
import org.dolphinemu.dolphinemu.model.settings.Setting;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Abstraction for a screen showing a list of settings. Instances of
* this type of view will each display a layer of the setting hierarchy.
*/
public interface SettingsFragmentView
{
/**
* Called by the containing Activity to notify the Fragment that an
* asynchronous load operation completed.
*
* @param settings The potentially-null result of the load operation.
*/
void onSettingsFileLoaded(HashMap<String, SettingSection> settings);
/**
* Pass a settings Hashmap to the containing activity, so that it can
* share the Hashmap with other SettingsFragments; useful so that rotations
* do not require an additional load operation.
*
* @param settings A Hashmap containing all the settings
*/
void passSettingsToActivity(HashMap<String, SettingSection> settings);
/**
* Pass an ArrayList to the View so that it can be displayed on screen.
*
* @param settingsList The result of converting the Hashmap to an ArrayList
*/
void showSettingsList(ArrayList<SettingsItem> settingsList);
/**
* @return The Fragment's containing activity.
*/
Activity getActivity();
/**
* Tell the Fragment to tell the containing Activity to show a new
* Fragment containing a submenu of settings.
*
* @param menuKey Identifier for the settings group that should be shown.
*/
void loadSubMenu(String menuKey);
/**
* Tell the Fragment to tell the containing activity to display a toast message.
*
* @param message Text to be shown in the Toast
*/
void showToastMessage(String message);
/**
* Have the fragment add a setting to the Hashmap.
*
* @param setting The (possibly previously missing) new setting.
*/
void putSetting(Setting setting);
}

View File

@ -0,0 +1,57 @@
package org.dolphinemu.dolphinemu.ui.settings.viewholder;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.view.CheckBoxSetting;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter;
public final class CheckBoxSettingViewHolder extends SettingViewHolder
{
private CheckBoxSetting mItem;
private TextView mTextSettingName;
private TextView mTextSettingDescription;
private CheckBox mCheckbox;
public CheckBoxSettingViewHolder(View itemView, SettingsAdapter adapter)
{
super(itemView, adapter);
}
@Override
protected void findViews(View root)
{
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
mCheckbox = (CheckBox) root.findViewById(R.id.checkbox);
}
@Override
public void bind(SettingsItem item)
{
mItem = (CheckBoxSetting) item;
mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0)
{
mTextSettingDescription.setText(item.getDescriptionId());
}
mCheckbox.setChecked(mItem.isChecked());
}
@Override
public void onClick(View clicked)
{
mCheckbox.toggle();
getAdapter().onBooleanClick(mItem, getAdapterPosition(), mCheckbox.isChecked());
}
}

View File

@ -0,0 +1,37 @@
package org.dolphinemu.dolphinemu.ui.settings.viewholder;
import android.view.View;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter;
public final class HeaderViewHolder extends SettingViewHolder
{
private TextView mHeaderName;
public HeaderViewHolder(View itemView, SettingsAdapter adapter)
{
super(itemView, adapter);
itemView.setOnClickListener(null);
}
@Override
protected void findViews(View root)
{
mHeaderName = (TextView) root.findViewById(R.id.text_header_name);
}
@Override
public void bind(SettingsItem item)
{
mHeaderName.setText(item.getNameId());
}
@Override
public void onClick(View clicked)
{
// no-op
}
}

View File

@ -0,0 +1,51 @@
package org.dolphinemu.dolphinemu.ui.settings.viewholder;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter;
public abstract class SettingViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener
{
private SettingsAdapter mAdapter;
public SettingViewHolder(View itemView, SettingsAdapter adapter)
{
super(itemView);
mAdapter = adapter;
itemView.setOnClickListener(this);
findViews(itemView);
}
protected SettingsAdapter getAdapter()
{
return mAdapter;
}
/**
* Gets handles to all this ViewHolder's child views using their XML-defined identifiers.
*
* @param root The newly inflated top-level view.
*/
protected abstract void findViews(View root);
/**
* Called by the adapter to set this ViewHolder's child views to display the list item
* it must now represent.
*
* @param item The list item that should be represented by this ViewHolder.
*/
public abstract void bind(SettingsItem item);
/**
* Called when this ViewHolder's view is clicked on. Implementations should usually pass
* this event up to the adapter.
*
* @param clicked The view that was clicked on.
*/
public abstract void onClick(View clicked);
}

View File

@ -0,0 +1,48 @@
package org.dolphinemu.dolphinemu.ui.settings.viewholder;
import android.view.View;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.model.settings.view.SingleChoiceSetting;
import org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter;
public final class SingleChoiceViewHolder extends SettingViewHolder
{
private SingleChoiceSetting mItem;
private TextView mTextSettingName;
private TextView mTextSettingDescription;
public SingleChoiceViewHolder(View itemView, SettingsAdapter adapter)
{
super(itemView, adapter);
}
@Override
protected void findViews(View root)
{
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
}
@Override
public void bind(SettingsItem item)
{
mItem = (SingleChoiceSetting) item;
mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0)
{
mTextSettingDescription.setText(item.getDescriptionId());
}
}
@Override
public void onClick(View clicked)
{
getAdapter().onSingleChoiceClick(mItem);
}
}

View File

@ -0,0 +1,50 @@
package org.dolphinemu.dolphinemu.ui.settings.viewholder;
import android.view.View;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.model.settings.view.SliderSetting;
import org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter;
public final class SliderViewHolder extends SettingViewHolder
{
private SliderSetting mItem;
private TextView mTextSettingName;
private TextView mTextSettingDescription;
public SliderViewHolder(View itemView, SettingsAdapter adapter)
{
super(itemView, adapter);
}
@Override
protected void findViews(View root)
{
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
}
@Override
public void bind(SettingsItem item)
{
mItem = (SliderSetting) item;
mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0)
{
mTextSettingDescription.setText(item.getDescriptionId());
}
}
@Override
public void onClick(View clicked)
{
getAdapter().onSliderClick(mItem);
}
}

View File

@ -0,0 +1,48 @@
package org.dolphinemu.dolphinemu.ui.settings.viewholder;
import android.view.View;
import android.widget.TextView;
import org.dolphinemu.dolphinemu.R;
import org.dolphinemu.dolphinemu.model.settings.view.SettingsItem;
import org.dolphinemu.dolphinemu.model.settings.view.SubmenuSetting;
import org.dolphinemu.dolphinemu.ui.settings.SettingsAdapter;
public final class SubmenuViewHolder extends SettingViewHolder
{
private SubmenuSetting mItem;
private TextView mTextSettingName;
private TextView mTextSettingDescription;
public SubmenuViewHolder(View itemView, SettingsAdapter adapter)
{
super(itemView, adapter);
}
@Override
protected void findViews(View root)
{
mTextSettingName = (TextView) root.findViewById(R.id.text_setting_name);
mTextSettingDescription = (TextView) root.findViewById(R.id.text_setting_description);
}
@Override
public void bind(SettingsItem item)
{
mItem = (SubmenuSetting) item;
mTextSettingName.setText(item.getNameId());
if (item.getDescriptionId() > 0)
{
mTextSettingDescription.setText(item.getDescriptionId());
}
}
@Override
public void onClick(View clicked)
{
getAdapter().onSubmenuClick(mItem);
}
}

View File

@ -0,0 +1,302 @@
package org.dolphinemu.dolphinemu.utils;
import android.os.Environment;
import android.support.annotation.NonNull;
import org.dolphinemu.dolphinemu.model.settings.BooleanSetting;
import org.dolphinemu.dolphinemu.model.settings.FloatSetting;
import org.dolphinemu.dolphinemu.model.settings.IntSetting;
import org.dolphinemu.dolphinemu.model.settings.Setting;
import org.dolphinemu.dolphinemu.model.settings.SettingSection;
import org.dolphinemu.dolphinemu.model.settings.StringSetting;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Set;
import rx.Observable;
import rx.Subscriber;
/**
* Contains static methods for interacting with .ini files in which settings are stored.
*/
public final class SettingsFile
{
public static final String FILE_NAME_DOLPHIN = "Dolphin";
public static final String FILE_NAME_GFX = "GFX";
public static final String FILE_NAME_GCPAD = "GCPadNew";
public static final String FILE_NAME_WIIMOTE = "WiimoteNew";
public static final String SECTION_CORE = "Core";
public static final String SECTION_GFX_SETTINGS = "Settings";
public static final String SECTION_GFX_ENHANCEMENTS = "Enhancements";
public static final String SECTION_GFX_HACKS = "Hacks";
public static final String SECTION_STEREOSCOPY = "Stereoscopy";
public static final String KEY_CPU_CORE = "CPUCore";
public static final String KEY_DUAL_CORE = "CPUThread";
public static final String KEY_OVERCLOCK_ENABLE = "OverclockEnable";
public static final String KEY_OVERCLOCK_PERCENT = "Overclock";
public static final String KEY_VIDEO_BACKEND = "GFXBackend";
public static final String KEY_SHOW_FPS = "ShowFPS";
public static final String KEY_INTERNAL_RES = "EFBScale";
public static final String KEY_FSAA = "MSAA";
public static final String KEY_ANISOTROPY = "MaxAnisotropy";
public static final String KEY_POST_SHADER = "PostProcessingShader";
public static final String KEY_SCALED_EFB = "EFBScaledCopy";
public static final String KEY_PER_PIXEL = "EnablePixelLighting";
public static final String KEY_FORCE_FILTERING = "ForceFiltering";
public static final String KEY_DISABLE_FOG = "DisableFog";
public static final String KEY_STEREO_MODE = "StereoMode";
public static final String KEY_STEREO_DEPTH = "StereoDepth";
public static final String KEY_STEREO_CONV = "StereoConvergencePercentage";
public static final String KEY_STEREO_SWAP = "StereoSwapEyes";
public static final String KEY_SKIP_EFB = "EFBAccessEnable";
public static final String KEY_IGNORE_FORMAT = "EFBEmulateFormatChanges";
public static final String KEY_EFB_COPY = "EFBCopyEnable";
public static final String KEY_EFB_TEXTURE = "EFBToTextureEnable";
public static final String KEY_EFB_CACHE = "EFBCopyCacheEnable";
public static final String KEY_TEXCACHE_ACCURACY = "SafeTextureCacheColorSamples";
public static final String KEY_XFB = "UseXFB";
public static final String KEY_XFB_REAL = "UseRealXFB";
public static final String KEY_FAST_DEPTH= "FastDepthCalc";
public static final String KEY_ASPECT_RATIO= "AspectRatio";
// Internal only, not actually found in settings file.
public static final String KEY_XFB_METHOD = "XFBMethod";
private SettingsFile()
{
}
/**
* Reads a given .ini file from disk and returns it as an Rx Observable. If successful,
* this Observable emits a Hashmap of SettingSections, themselves effectively a Hashmap of
* key/value settings. If unsuccessful, the observer notifies the subscriber of why it failed.
*
* @param fileName The name of the settings file without a path or extension.
* @return An Observable that emits a Hashmap of the file's contents, then completes.
*/
public static Observable<HashMap<String, SettingSection>> readFile(final String fileName)
{
return Observable.create(new Observable.OnSubscribe<HashMap<String, SettingSection>>()
{
@Override
public void call(Subscriber<? super HashMap<String, SettingSection>> subscriber)
{
HashMap<String, SettingSection> sections = new HashMap<>();
File ini = getSettingsFile(fileName);
BufferedReader reader = null;
try
{
reader = new BufferedReader(new FileReader(ini));
SettingSection current = null;
for (String line; (line = reader.readLine()) != null; )
{
if (line.startsWith("[") && line.endsWith("]"))
{
current = sectionFromLine(line);
sections.put(current.getName(), current);
}
else if ((current != null) && line.contains("="))
{
Setting setting = settingFromLine(current, line);
current.putSetting(setting.getKey(), setting);
}
}
}
catch (FileNotFoundException e)
{
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage());
subscriber.onError(e);
}
catch (IOException e)
{
Log.error("[SettingsFile] Error reading from: " + fileName + ".ini: " + e.getMessage());
subscriber.onError(e);
} finally
{
if (reader != null)
{
try
{
reader.close();
}
catch (IOException e)
{
Log.error("[SettingsFile] Error closing: " + fileName + ".ini: " + e.getMessage());
subscriber.onError(e);
}
}
}
subscriber.onNext(sections);
subscriber.onCompleted();
}
});
}
/**
* Saves a Settings Hashmap to a given .ini file on disk, returning the operation
* as an Rx Observable. If successful, this Observable emits an event to notify subscribers;
* if unsuccessful, the observer notifies the subscriber of why it failed.
*
* @param fileName The target filename without a path or extension.
* @param sections The hashmap containing the Settings we want to serialize.
* @return An Observable representing the operation.
*/
public static Observable<Boolean> saveFile(final String fileName, final HashMap<String, SettingSection> sections)
{
return Observable.create(new Observable.OnSubscribe<Boolean>()
{
@Override
public void call(Subscriber<? super Boolean> subscriber)
{
File ini = getSettingsFile(fileName);
PrintWriter writer = null;
try
{
writer = new PrintWriter(ini, "UTF-8");
Set<String> keySet = sections.keySet();
for (String key : keySet)
{
SettingSection section = sections.get(key);
writeSection(writer, section);
}
}
catch (FileNotFoundException e)
{
Log.error("[SettingsFile] File not found: " + fileName + ".ini: " + e.getMessage());
subscriber.onError(e);
}
catch (UnsupportedEncodingException e)
{
Log.error("[SettingsFile] Bad encoding; please file a bug report: " + fileName + ".ini: " + e.getMessage());
subscriber.onError(e);
} finally
{
if (writer != null)
{
writer.close();
}
}
subscriber.onNext(true);
subscriber.onCompleted();
}
});
}
@NonNull
private static File getSettingsFile(String fileName)
{
String storagePath = Environment.getExternalStorageDirectory().getAbsolutePath();
return new File(storagePath + "/dolphin-emu/Config/" + fileName + ".ini");
}
private static SettingSection sectionFromLine(String line)
{
String sectionName = line.substring(1, line.length() - 1);
return new SettingSection(sectionName);
}
/**
* For a line of text, determines what type of data is being represented, and returns
* a Setting object containing this data.
*
* @param current The section currently being parsed by the consuming method.
* @param line The line of text being parsed.
* @return A typed Setting containing the key/value contained in the line.
*/
private static Setting settingFromLine(SettingSection current, String line)
{
String[] splitLine = line.split("=");
String key = splitLine[0].trim();
String value = splitLine[1].trim();
try
{
int valueAsInt = Integer.valueOf(value);
return new IntSetting(key, current.getName(), valueAsInt);
}
catch (NumberFormatException ex)
{
}
try
{
float valueAsFloat = Float.valueOf(value);
return new FloatSetting(key, current.getName(), valueAsFloat);
}
catch (NumberFormatException ex)
{
}
switch (value)
{
case "True":
return new BooleanSetting(key, current.getName(), true);
case "False":
return new BooleanSetting(key, current.getName(), false);
default:
return new StringSetting(key, current.getName(), value);
}
}
/**
* Writes the contents of a Section hashmap to disk.
*
* @param writer A PrintWriter pointed at a file on disk.
* @param section A section containing settings to be written to the file.
*/
private static void writeSection(PrintWriter writer, SettingSection section)
{
// Write the section header.
String header = sectionAsString(section);
writer.println(header);
// Write this section's values.
HashMap<String, Setting> settings = section.getSettings();
Set<String> keySet = settings.keySet();
for (String key : keySet)
{
Setting setting = settings.get(key);
String settingString = settingAsString(setting);
writer.println(settingString);
}
}
private static String sectionAsString(SettingSection section)
{
return "[" + section.getName() + "]";
}
private static String settingAsString(Setting setting)
{
return setting.getKey() + " = " + setting.getValueAsString();
}
}

View File

@ -1,128 +0,0 @@
package org.dolphinemu.dolphinemu.utils;
import android.app.AlertDialog;
import android.content.Context;
import android.os.Bundle;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import org.dolphinemu.dolphinemu.R;
public class SliderPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener, View.OnClickListener
{
private static final String androidns = "http://schemas.android.com/apk/res/android";
// SeekBar
private int m_max, m_value;
private String m_key;
private SeekBar m_seekbar;
// TextView
private TextView m_textview;
public SliderPreference(Context context, AttributeSet attrs)
{
super(context, attrs);
// Seekbar values
m_value = attrs.getAttributeIntValue(androidns, "defaultValue", 0);
m_max = attrs.getAttributeIntValue(androidns, "max", 100);
m_key = attrs.getAttributeValue(androidns, "key");
}
@Override
protected View onCreateDialogView()
{
LayoutInflater inflater = LayoutInflater.from(getContext());
LinearLayout layout = (LinearLayout) inflater.inflate(R.layout.slider_layout, null, false);
m_seekbar = (SeekBar) layout.findViewById(R.id.sliderSeekBar);
m_textview = (TextView) layout.findViewById(R.id.sliderTextView);
if (shouldPersist())
{
if (m_key != null && m_key.equals("Overclock"))
{
Toast.makeText(getContext(), getContext().getString(R.string.overclock_warning),
Toast.LENGTH_LONG).show();
float valueAsFloat = Float.valueOf(getPersistedString(Integer.toString(m_value)));
float valueAsPercent = valueAsFloat * 100;
m_value = Math.round(valueAsPercent);
}
else
{
m_value = Integer.valueOf(getPersistedString(Integer.toString(m_value)));
}
}
m_seekbar.setMax(m_max);
m_seekbar.setProgress(m_value);
setProgressText(m_value);
m_seekbar.setOnSeekBarChangeListener(this);
return layout;
}
// SeekBar overrides
@Override
public void onProgressChanged(SeekBar seek, int value, boolean fromTouch)
{
m_value = value;
setProgressText(value);
}
@Override
public void onStartTrackingTouch(SeekBar seek)
{
}
@Override
public void onStopTrackingTouch(SeekBar seek)
{
}
void setProgressText(int value)
{
m_textview.setText(String.valueOf(value));
}
@Override
public void showDialog(Bundle state)
{
super.showDialog(state);
Button positiveButton = ((AlertDialog) getDialog()).getButton(AlertDialog.BUTTON_POSITIVE);
positiveButton.setOnClickListener(this);
}
@Override
public void onClick(View v)
{
if (shouldPersist())
{
String valueToSave;
if (m_key != null && m_key.equals("Overclock"))
{
float valueAsFloat = m_value / 100.0f;
valueToSave = Float.toString(valueAsFloat);
}
else
{
valueToSave = Integer.toString(m_seekbar.getProgress());
}
persistString(valueToSave);
callChangeListener(m_seekbar.getProgress());
}
((AlertDialog) getDialog()).dismiss();
}
}

View File

@ -1,495 +0,0 @@
/**
* Copyright 2013 Dolphin Emulator Project
* Licensed under GPLv2+
* Refer to the license.txt file included.
*/
package org.dolphinemu.dolphinemu.utils;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
import org.dolphinemu.dolphinemu.NativeLibrary;
/**
* A class that retrieves all of the set user preferences in Android, in a safe way.
* <p>
* If any preferences are added to this emulator, an accessor for that preference
* should be added here. This way lengthy calls to getters from SharedPreferences
* aren't made necessary.
*/
public final class UserPreferences
{
private UserPreferences()
{
// Disallows instantiation.
}
/**
* Loads the settings stored in the Dolphin ini config files to the shared preferences of this front-end.
*
* @param ctx The context used to retrieve the SharedPreferences instance.
*/
public static void LoadIniToPrefs(Context ctx)
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
// Get an editor.
SharedPreferences.Editor editor = prefs.edit();
// Add the settings.
if (Build.CPU_ABI.contains("arm64"))
editor.putString("cpuCorePref", getConfig("Dolphin.ini", "Core", "CPUCore", "4"));
else
editor.putString("cpuCorePref", getConfig("Dolphin.ini", "Core", "CPUCore", "3"));
editor.putBoolean("dualCorePref", getConfig("Dolphin.ini", "Core", "CPUThread", "True").equals("True"));
editor.putBoolean("OverclockEnable", getConfig("Dolphin.ini", "Core", "OverclockEnable", "False").equals("True"));
editor.putString("Overclock", getConfig("Dolphin.ini", "Core", "Overclock", "100"));
// Load analog ranges from GCPadNew.ini and WiimoteNew.ini
editor.putString("mainRadius0", getConfig("GCPadNew.ini", "GCPad1", "Main Stick/Radius", "100,000000"));
editor.putString("cStickRadius0", getConfig("GCPadNew.ini", "GCPad1", "C-Stick/Radius", "100,000000"));
editor.putString("inputThres0", getConfig("GCPadNew.ini", "GCPad1", "Triggers/Threshold", "90,000000"));
editor.putString("mainRadius1", getConfig("GCPadNew.ini", "GCPad2", "Main Stick/Radius", "100,000000"));
editor.putString("cStickRadius1", getConfig("GCPadNew.ini", "GCPad2", "C-Stick/Radius", "100,000000"));
editor.putString("inputThres1", getConfig("GCPadNew.ini", "GCPad2", "Triggers/Threshold", "90,000000"));
editor.putString("mainRadius2", getConfig("GCPadNew.ini", "GCPad3", "Main Stick/Radius", "100,000000"));
editor.putString("cStickRadius2", getConfig("GCPadNew.ini", "GCPad3", "C-Stick/Radius", "100,000000"));
editor.putString("inputThres2", getConfig("GCPadNew.ini", "GCPad3", "Triggers/Threshold", "90,000000"));
editor.putString("mainRadius3", getConfig("GCPadNew.ini", "GCPad4", "Main Stick/Radius", "100,000000"));
editor.putString("cStickRadius3", getConfig("GCPadNew.ini", "GCPad4", "C-Stick/Radius", "100,000000"));
editor.putString("inputThres3", getConfig("GCPadNew.ini", "GCPad4", "Triggers/Threshold", "90,000000"));
editor.putString("tiltRange4", getConfig("WiimoteNew.ini", "Wiimote1", "Tilt/Modifier/Range", "50,00000"));
editor.putString("tiltRange5", getConfig("WiimoteNew.ini", "Wiimote2", "Tilt/Modifier/Range", "50,00000"));
editor.putString("tiltRange6", getConfig("WiimoteNew.ini", "Wiimote3", "Tilt/Modifier/Range", "50,00000"));
editor.putString("tiltRange7", getConfig("WiimoteNew.ini", "Wiimote4", "Tilt/Modifier/Range", "50,00000"));
editor.putString("nunchukRadius4", getConfig("WiimoteNew.ini", "Wiimote1", "Nunchuk/Stick/Radius", "100,000000"));
editor.putString("nunchukRange4", getConfig("WiimoteNew.ini", "Wiimote1", "Nunchuk/Tilt/Modifier/Range", "50,00000"));
editor.putString("nunchukRadius5", getConfig("WiimoteNew.ini", "Wiimote2", "Nunchuk/Stick/Radius", "100,000000"));
editor.putString("nunchukRange5", getConfig("WiimoteNew.ini", "Wiimote2", "Nunchuk/Tilt/Modifier/Range", "50,00000"));
editor.putString("nunchukRadius6", getConfig("WiimoteNew.ini", "Wiimote3", "Nunchuk/Stick/Radius", "100,000000"));
editor.putString("nunchukRange6", getConfig("WiimoteNew.ini", "Wiimote3", "Nunchuk/Tilt/Modifier/Range", "50,00000"));
editor.putString("nunchukRadius7", getConfig("WiimoteNew.ini", "Wiimote4", "Nunchuk/Stick/Radius", "100,000000"));
editor.putString("nunchukRange7", getConfig("WiimoteNew.ini", "Wiimote4", "Nunchuk/Tilt/Modifier/Range", "50,00000"));
editor.putString("classicLRadius4", getConfig("WiimoteNew.ini", "Wiimote1", "Classic/Left Stick/Radius", "100,000000"));
editor.putString("classicRRadius4", getConfig("WiimoteNew.ini", "Wiimote1", "Classic/Right Stick/Radius", "100,000000"));
editor.putString("classicThres4", getConfig("WiimoteNew.ini", "Wiimote1", "Classic/Triggers/Threshold", "90,000000"));
editor.putString("classicLRadius5", getConfig("WiimoteNew.ini", "Wiimote2", "Classic/Left Stick/Radius", "100,000000"));
editor.putString("classicRRadius5", getConfig("WiimoteNew.ini", "Wiimote2", "Classic/Right Stick/Radius", "100,000000"));
editor.putString("classicThres5", getConfig("WiimoteNew.ini", "Wiimote2", "Classic/Triggers/Threshold", "90,000000"));
editor.putString("classicLRadius6", getConfig("WiimoteNew.ini", "Wiimote3", "Classic/Left Stick/Radius", "100,000000"));
editor.putString("classicRRadius6", getConfig("WiimoteNew.ini", "Wiimote3", "Classic/Right Stick/Radius", "100,000000"));
editor.putString("classicThres6", getConfig("WiimoteNew.ini", "Wiimote3", "Classic/Triggers/Threshold", "90,000000"));
editor.putString("classicLRadius7", getConfig("WiimoteNew.ini", "Wiimote4", "Classic/Left Stick/Radius", "100,000000"));
editor.putString("classicRRadius7", getConfig("WiimoteNew.ini", "Wiimote4", "Classic/Right Stick/Radius", "100,000000"));
editor.putString("classicThres7", getConfig("WiimoteNew.ini", "Wiimote4", "Classic/Triggers/Threshold", "90,000000"));
editor.putString("guitarRadius4", getConfig("WiimoteNew.ini", "Wiimote1", "Guitar/Stick/Radius", "100,000000"));
editor.putString("guitarRadius5", getConfig("WiimoteNew.ini", "Wiimote2", "Guitar/Stick/Radius", "100,000000"));
editor.putString("guitarRadius6", getConfig("WiimoteNew.ini", "Wiimote3", "Guitar/Stick/Radius", "100,000000"));
editor.putString("guitarRadius7", getConfig("WiimoteNew.ini", "Wiimote4", "Guitar/Stick/Radius", "100,000000"));
editor.putString("drumsRadius4", getConfig("WiimoteNew.ini", "Wiimote1", "Drums/Stick/Radius", "100,000000"));
editor.putString("drumsRadius5", getConfig("WiimoteNew.ini", "Wiimote2", "Drums/Stick/Radius", "100,000000"));
editor.putString("drumsRadius6", getConfig("WiimoteNew.ini", "Wiimote3", "Drums/Stick/Radius", "100,000000"));
editor.putString("drumsRadius7", getConfig("WiimoteNew.ini", "Wiimote4", "Drums/Stick/Radius", "100,000000"));
editor.putString("turntableRadius4", getConfig("WiimoteNew.ini", "Wiimote1", "Turntable/Stick/Radius", "100,000000"));
editor.putString("turntableRadius5", getConfig("WiimoteNew.ini", "Wiimote2", "Turntable/Stick/Radius", "100,000000"));
editor.putString("turntableRadius6", getConfig("WiimoteNew.ini", "Wiimote3", "Turntable/Stick/Radius", "100,000000"));
editor.putString("turntableRadius7", getConfig("WiimoteNew.ini", "Wiimote4", "Turntable/Stick/Radius", "100,000000"));
// Load Wiimote Extension settings from WiimoteNew.ini
editor.putString("wiimoteExtension4", getConfig("WiimoteNew.ini", "Wiimote1", "Extension", "None"));
editor.putString("wiimoteExtension5", getConfig("WiimoteNew.ini", "Wiimote2", "Extension", "None"));
editor.putString("wiimoteExtension6", getConfig("WiimoteNew.ini", "Wiimote3", "Extension", "None"));
editor.putString("wiimoteExtension7", getConfig("WiimoteNew.ini", "Wiimote4", "Extension", "None"));
editor.putString("gpuPref", getConfig("Dolphin.ini", "Core", "GFXBackend", "OGL"));
editor.putBoolean("showFPS", getConfig("GFX.ini", "Settings", "ShowFPS", "False").equals("True"));
editor.putBoolean("drawOnscreenControls", getConfig("Dolphin.ini", "Android", "ScreenControls", "True").equals("True"));
editor.putString("internalResolution", getConfig("GFX.ini", "Settings", "EFBScale", "2"));
editor.putString("FSAA", getConfig("GFX.ini", "Settings", "MSAA", "0"));
editor.putString("anisotropicFiltering", getConfig("GFX.ini", "Enhancements", "MaxAnisotropy", "0"));
editor.putString("postProcessingShader", getConfig("GFX.ini", "Enhancements", "PostProcessingShader", ""));
editor.putBoolean("scaledEFBCopy", getConfig("GFX.ini", "Hacks", "EFBScaledCopy", "True").equals("True"));
editor.putBoolean("perPixelLighting", getConfig("GFX.ini", "Settings", "EnablePixelLighting", "False").equals("True"));
editor.putBoolean("forceTextureFiltering", getConfig("GFX.ini", "Enhancements", "ForceFiltering", "False").equals("True"));
editor.putBoolean("disableFog", getConfig("GFX.ini", "Settings", "DisableFog", "False").equals("True"));
editor.putBoolean("skipEFBAccess", getConfig("GFX.ini", "Hacks", "EFBAccessEnable", "False").equals("True"));
editor.putBoolean("ignoreFormatChanges", getConfig("GFX.ini", "Hacks", "EFBEmulateFormatChanges", "False").equals("True"));
editor.putString("stereoscopyMode", getConfig("GFX.ini", "Stereoscopy", "StereoMode", "0"));
editor.putBoolean("stereoSwapEyes", getConfig("GFX.ini", "Stereoscopy", "StereoSwapEyes", "False").equals("True"));
editor.putString("stereoDepth", getConfig("GFX.ini", "Stereoscopy", "StereoDepth", "20"));
editor.putString("stereoConvergencePercentage", getConfig("GFX.ini", "Stereoscopy", "StereoConvergencePercentage", "100"));
editor.putBoolean("enableController1", getConfig("Dolphin.ini", "Core", "SIDevice0", "6") == "6");
editor.putBoolean("enableController2", getConfig("Dolphin.ini", "Core", "SIDevice1", "0") == "6");
editor.putBoolean("enableController3", getConfig("Dolphin.ini", "Core", "SIDevice2", "0") == "6");
editor.putBoolean("enableController4", getConfig("Dolphin.ini", "Core", "SIDevice3", "0") == "6");
String efbCopyOn = getConfig("GFX.ini", "Hacks", "EFBCopyEnable", "True");
String efbToTexture = getConfig("GFX.ini", "Hacks", "EFBToTextureEnable", "True");
String efbCopyCache = getConfig("GFX.ini", "Hacks", "EFBCopyCacheEnable", "False");
if (efbCopyOn.equals("False"))
{
editor.putString("efbCopyMethod", "Off");
}
else if (efbCopyOn.equals("True") && efbToTexture.equals("True"))
{
editor.putString("efbCopyMethod", "Texture");
}
else if(efbCopyOn.equals("True") && efbToTexture.equals("False") && efbCopyCache.equals("False"))
{
editor.putString("efbCopyMethod", "RAM (uncached)");
}
else if(efbCopyOn.equals("True") && efbToTexture.equals("False") && efbCopyCache.equals("True"))
{
editor.putString("efbCopyMethod", "RAM (cached)");
}
editor.putString("textureCacheAccuracy", getConfig("GFX.ini", "Settings", "SafeTextureCacheColorSamples", "128"));
String usingXFB = getConfig("GFX.ini", "Settings", "UseXFB", "False");
String usingRealXFB = getConfig("GFX.ini", "Settings", "UseRealXFB", "False");
if (usingXFB.equals("False"))
{
editor.putString("externalFrameBuffer", "Disabled");
}
else if (usingXFB.equals("True") && usingRealXFB.equals("False"))
{
editor.putString("externalFrameBuffer", "Virtual");
}
else if (usingXFB.equals("True") && usingRealXFB.equals("True"))
{
editor.putString("externalFrameBuffer", "Real");
}
editor.putBoolean("fastDepthCalculation", getConfig("GFX.ini", "Settings", "FastDepthCalc", "True").equals("True"));
editor.putString("aspectRatio", getConfig("GFX.ini", "Settings", "AspectRatio", "0"));
// Apply the changes.
editor.apply();
}
// Small utility method that shortens calls to NativeLibrary.GetConfig.
private static String getConfig(String ini, String section, String key, String defaultValue)
{
return NativeLibrary.GetConfig(ini, section, key, defaultValue);
}
/**
* Writes the preferences set in the front-end to the Dolphin ini files.
*
* @param ctx The context used to retrieve the user settings.
* */
public static void SavePrefsToIni(Context ctx)
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
// Whether or not the user is using dual core.
boolean isUsingDualCore = prefs.getBoolean("dualCorePref", true);
// Current CPU core being used. Falls back to interpreter upon error.
String currentEmuCore = prefs.getString("cpuCorePref", "0");
boolean overclockEnabled = prefs.getBoolean("OverclockEnable", false);
String overclockSetting = prefs.getString("Overclock", "100");
// Current GC analog range setup. Falls back to default upon error.
String currentMainRadius0 = prefs.getString("mainRadius0", "100,000000");
String currentCStickRadius0 = prefs.getString("cStickRadius0", "100,000000");
String currentInputThres0 = prefs.getString("inputThres0", "90,000000");
String currentMainRadius1 = prefs.getString("mainRadius1", "100,000000");
String currentCStickRadius1 = prefs.getString("cStickRadius1", "100,000000");
String currentInputThres1 = prefs.getString("inputThres1", "90,000000");
String currentMainRadius2 = prefs.getString("mainRadius2", "100,000000");
String currentCStickRadius2 = prefs.getString("cStickRadius2", "100,000000");
String currentInputThres2 = prefs.getString("inputThres2", "90,000000");
String currentMainRadius3 = prefs.getString("mainRadius3", "100,000000");
String currentCStickRadius3 = prefs.getString("cStickRadius3", "100,000000");
String currentInputThres3 = prefs.getString("inputThres3", "90,000000");
// Current Wii analog range setup. Falls back to default on error.
String currentTiltRange4 = prefs.getString("tiltRange4", "50,000000");
String currentTiltRange5 = prefs.getString("tiltRange5", "50,000000");
String currentTiltRange6 = prefs.getString("tiltRange6", "50,000000");
String currentTiltRange7 = prefs.getString("tiltRange7", "50,000000");
// Current Nunchuk analog range setup. Falls back to default upon error.
String currentNunchukRadius4 = prefs.getString("nunchukRadius4", "100,000000");
String currentNunchukRange4 = prefs.getString("nunchukRange4", "50,000000");
String currentNunchukRadius5 = prefs.getString("nunchukRadius5", "100,000000");
String currentNunchukRange5 = prefs.getString("nunchukRange5", "50,000000");
String currentNunchukRadius6 = prefs.getString("nunchukRadius6", "100,000000");
String currentNunchukRange6 = prefs.getString("nunchukRange6", "50,000000");
String currentNunchukRadius7 = prefs.getString("nunchukRadius7", "100,000000");
String currentNunchukRange7 = prefs.getString("nunchukRange7", "50,000000");
// Current Classic analog range setup. Falls back to 100,000000 upon error.
String currentClassicLRadius4 = prefs.getString("classicLRadius4", "100,000000");
String currentClassicRRadius4 = prefs.getString("classicRRadius4", "100,000000");
String currentClassicThres4 = prefs.getString("classicThres4", "90,000000");
String currentClassicLRadius5 = prefs.getString("classicLRadius5", "100,000000");
String currentClassicRRadius5 = prefs.getString("classicRRadius5", "100,000000");
String currentClassicThres5 = prefs.getString("classicThres5", "90,000000");
String currentClassicLRadius6 = prefs.getString("classicLRadius6", "100,000000");
String currentClassicRRadius6 = prefs.getString("classicRRadius6", "100,000000");
String currentClassicThres6 = prefs.getString("classicThres6", "90,000000");
String currentClassicLRadius7 = prefs.getString("classicLRadius7", "100,000000");
String currentClassicRRadius7 = prefs.getString("classicRRadius7", "100,000000");
String currentClassicThres7 = prefs.getString("classicThres7", "90,000000");
// Current Guitar analog range setup. Falls back to default upon error.
String currentGuitarRadius4 = prefs.getString("guitarRadius4", "100,000000");
String currentGuitarRadius5 = prefs.getString("guitarRadius5", "100,000000");
String currentGuitarRadius6 = prefs.getString("guitarRadius6", "100,000000");
String currentGuitarRadius7 = prefs.getString("guitarRadius7", "100,000000");
// Current Drums modifier Radius setup. Falls back to default upon error.
String currentDrumsRadius4 = prefs.getString("drumsRadius4", "100,000000");
String currentDrumsRadius5 = prefs.getString("drumsRadius5", "100,000000");
String currentDrumsRadius6 = prefs.getString("drumsRadius6", "100,000000");
String currentDrumsRadius7 = prefs.getString("drumsRadius7", "100,000000");
// Current Turntable analog range setup. Falls back to default upon error.
String currentTurntableRadius4 = prefs.getString("turntableRadius4", "100,000000");
String currentTurntableRadius5 = prefs.getString("turntableRadius5", "100,000000");
String currentTurntableRadius6 = prefs.getString("turntableRadius6", "100,000000");
String currentTurntableRadius7 = prefs.getString("turntableRadius7", "100,000000");
// Current wiimote extension setup. Falls back to no extension upon error.
String currentWiimoteExtension4 = prefs.getString("wiimoteExtension4", "None");
String currentWiimoteExtension5 = prefs.getString("wiimoteExtension5", "None");
String currentWiimoteExtension6 = prefs.getString("wiimoteExtension6", "None");
String currentWiimoteExtension7 = prefs.getString("wiimoteExtension7", "None");
// Current video backend being used. Falls back to software rendering upon error.
String currentVideoBackend = prefs.getString("gpuPref", "Software Rendering");
// Whether or not FPS will be displayed on-screen.
boolean showingFPS = prefs.getBoolean("showFPS", false);
// Whether or not to draw on-screen controls.
boolean drawingOnscreenControls = prefs.getBoolean("drawOnscreenControls", true);
// Whether or not to ignore all EFB access requests from the CPU.
boolean skipEFBAccess = prefs.getBoolean("skipEFBAccess", false);
// Whether or not to ignore changes to the EFB format.
boolean ignoreFormatChanges = prefs.getBoolean("ignoreFormatChanges", false);
// EFB copy method to use.
String efbCopyMethod = prefs.getString("efbCopyMethod", "Texture");
// Texture cache accuracy. Falls back to "Fast" up error.
String textureCacheAccuracy = prefs.getString("textureCacheAccuracy", "128");
// External frame buffer emulation. Falls back to disabled upon error.
String externalFrameBuffer = prefs.getString("externalFrameBuffer", "Disabled");
// Whether or not to use fast depth calculation.
boolean useFastDepthCalc = prefs.getBoolean("fastDepthCalculation", true);
// Aspect ratio selection
String aspectRatio = prefs.getString("aspectRatio", "0");
// Internal resolution. Falls back to 1x Native upon error.
String internalResolution = prefs.getString("internalResolution", "2");
// FSAA Level. Falls back to 1x upon error.
String FSAALevel = prefs.getString("FSAA", "0");
// Anisotropic Filtering Level. Falls back to 1x upon error.
String anisotropicFiltLevel = prefs.getString("anisotropicFiltering", "0");
// Post processing shader setting
String postProcessing = prefs.getString("postProcessingShader", "");
// Whether or not Scaled EFB copies are used.
boolean usingScaledEFBCopy = prefs.getBoolean("scaledEFBCopy", true);
// Whether or not per-pixel lighting is used.
boolean usingPerPixelLighting = prefs.getBoolean("perPixelLighting", false);
// Whether or not texture filtering is being forced.
boolean isForcingTextureFiltering = prefs.getBoolean("forceTextureFiltering", false);
// Whether or not fog is disabled.
boolean fogIsDisabled = prefs.getBoolean("disableFog", false);
// Stereoscopy setting
String stereoscopyMode = prefs.getString("stereoscopyMode", "0");
// Stereoscopy swap eyes
boolean stereoscopyEyeSwap = prefs.getBoolean("stereoSwapEyes", false);
// Stereoscopy separation
String stereoscopySeparation = prefs.getString("stereoDepth", "20");
// Stereoscopy convergence
String stereoConvergencePercentage = prefs.getString("stereoConvergencePercentage", "100");
// Controllers
// Controller 1 never gets disconnected due to touch screen
//boolean enableController1 = prefs.getBoolean("enableController1", true);
boolean enableController2 = prefs.getBoolean("enableController2", false);
boolean enableController3 = prefs.getBoolean("enableController3", false);
boolean enableController4 = prefs.getBoolean("enableController4", false);
// CPU related Settings
NativeLibrary.SetConfig("Dolphin.ini", "Core", "CPUCore", currentEmuCore);
NativeLibrary.SetConfig("Dolphin.ini", "Core", "CPUThread", isUsingDualCore ? "True" : "False");
NativeLibrary.SetConfig("Dolphin.ini", "Core", "OverclockEnable", overclockEnabled ? "True" : "False");
NativeLibrary.SetConfig("Dolphin.ini", "Core", "Overclock", overclockSetting);
// GameCube analog ranges Setup
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad1", "Main Stick/Radius", currentMainRadius0);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad1", "C-Stick/Radius", currentCStickRadius0);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad1", "Triggers/Threshold", currentInputThres0);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad2", "Main Stick/Radius", currentMainRadius1);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad2", "C-Stick/Radius", currentCStickRadius1);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad2", "Triggers/Threshold", currentInputThres1);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad3", "Main Stick/Radius", currentMainRadius2);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad3", "C-Stick/Radius", currentCStickRadius2);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad3", "Triggers/Threshold", currentInputThres2);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad4", "Main Stick/Radius", currentMainRadius3);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad4", "C-Stick/Radius", currentCStickRadius3);
NativeLibrary.SetConfig("GCPadNew.ini", "GCPad4", "Triggers/Threshold", currentInputThres3);
// Wiimote analog ranges Setup
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Tilt/Modifier/Range", currentTiltRange4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Tilt/Modifier/Range", currentTiltRange5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Tilt/Modifier/Range", currentTiltRange6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Tilt/Modifier/Range", currentTiltRange7);
// Nunchuk analog ranges Setup
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Nunchuk/Stick/Radius", currentNunchukRadius4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Nunchuk/Stick/Radius", currentNunchukRange4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Nunchuk/Stick/Radius", currentNunchukRadius5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Nunchuk/Stick/Radius", currentNunchukRange5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Nunchuk/Stick/Radius", currentNunchukRadius6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Nunchuk/Stick/Radius", currentNunchukRange6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Nunchuk/Stick/Radius", currentNunchukRadius7);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Nunchuk/Stick/Radius", currentNunchukRange7);
// Classic analog ranges Setup
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Classic/Left Stick/Radius", currentClassicLRadius4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Classic/Right Stick/Radius", currentClassicRRadius4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Classic/Triggers/Threshold", currentClassicThres4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Classic/Left Stick/Radius", currentClassicLRadius5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Classic/Right Stick/Radius", currentClassicRRadius5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Classic/Triggers/Threshold", currentClassicThres5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Classic/Left Stick/Radius", currentClassicLRadius6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Classic/Right Stick/Radius", currentClassicRRadius6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Classic/Triggers/Threshold", currentClassicThres6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Classic/Left Stick/Radius", currentClassicLRadius7);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Classic/Right Stick/Radius", currentClassicRRadius7);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Classic/Triggers/Threshold", currentClassicThres7);
// Guitar analog ranges Setup
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Guitar/Stick/Radius", currentGuitarRadius4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Guitar/Stick/Radius", currentGuitarRadius5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Guitar/Stick/Radius", currentGuitarRadius6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Guitar/Stick/Radius", currentGuitarRadius7);
// Drums analog ranges Setup
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Drums/Stick/Radius", currentDrumsRadius4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Drums/Stick/Radius", currentDrumsRadius5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Drums/Stick/Radius", currentDrumsRadius6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Drums/Stick/Radius", currentDrumsRadius7);
// Turntable analog ranges Setup
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Turntable/Stick/Radius", currentTurntableRadius4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Turntable/Stick/Radius", currentTurntableRadius5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Turntable/Stick/Radius", currentTurntableRadius6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Turntable/Stick/Radius", currentTurntableRadius7);
// Wiimote Extension Settings
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote1", "Extension", currentWiimoteExtension4);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote2", "Extension", currentWiimoteExtension5);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote3", "Extension", currentWiimoteExtension6);
NativeLibrary.SetConfig("WiimoteNew.ini", "Wiimote4", "Extension", currentWiimoteExtension7);
// General Video Settings
NativeLibrary.SetConfig("Dolphin.ini", "Core", "GFXBackend", currentVideoBackend);
NativeLibrary.SetConfig("GFX.ini", "Settings", "ShowFPS", showingFPS ? "True" : "False");
NativeLibrary.SetConfig("Dolphin.ini", "Android", "ScreenControls", drawingOnscreenControls ? "True" : "False");
// Video Hack Settings
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBAccessEnable", skipEFBAccess ? "False" : "True");
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBEmulateFormatChanges", ignoreFormatChanges ? "True" : "False");
NativeLibrary.SetConfig("GFX.ini", "Settings", "AspectRatio", aspectRatio);
// Set EFB Copy Method
if (efbCopyMethod.equals("Off"))
{
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBCopyEnable", "False");
}
else if (efbCopyMethod.equals("Texture"))
{
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBCopyEnable", "True");
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBToTextureEnable", "True");
}
else if (efbCopyMethod.equals("RAM (uncached)"))
{
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBCopyEnable", "True");
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBToTextureEnable", "False");
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBCopyCacheEnable", "False");
}
else if (efbCopyMethod.equals("RAM (cached)"))
{
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBCopyEnable", "True");
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBToTextureEnable", "False");
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBCopyCacheEnable", "True");
}
// Set texture cache accuracy
NativeLibrary.SetConfig("GFX.ini", "Settings", "SafeTextureCacheColorSamples", textureCacheAccuracy);
// Set external frame buffer.
if (externalFrameBuffer.equals("Disabled"))
{
NativeLibrary.SetConfig("GFX.ini", "Settings", "UseXFB", "False");
}
else if (externalFrameBuffer.equals("Virtual"))
{
NativeLibrary.SetConfig("GFX.ini", "Settings", "UseXFB", "True");
NativeLibrary.SetConfig("GFX.ini", "Settings", "UseRealXFB", "False");
}
else if (externalFrameBuffer.equals("Real"))
{
NativeLibrary.SetConfig("GFX.ini", "Settings", "UseXFB", "True");
NativeLibrary.SetConfig("GFX.ini", "Settings", "UseRealXFB", "True");
}
NativeLibrary.SetConfig("GFX.ini", "Settings", "FastDepthCalc", useFastDepthCalc ? "True" : "False");
//-- Enhancement Settings --//
NativeLibrary.SetConfig("GFX.ini", "Settings", "EFBScale", internalResolution);
NativeLibrary.SetConfig("GFX.ini", "Settings", "MSAA", FSAALevel);
NativeLibrary.SetConfig("GFX.ini", "Enhancements", "MaxAnisotropy", anisotropicFiltLevel);
NativeLibrary.SetConfig("GFX.ini", "Enhancements", "PostProcessingShader", postProcessing);
NativeLibrary.SetConfig("GFX.ini", "Hacks", "EFBScaledCopy", usingScaledEFBCopy ? "True" : "False");
NativeLibrary.SetConfig("GFX.ini", "Settings", "EnablePixelLighting", usingPerPixelLighting ? "True" : "False");
NativeLibrary.SetConfig("GFX.ini", "Enhancements", "ForceFiltering", isForcingTextureFiltering ? "True" : "False");
NativeLibrary.SetConfig("GFX.ini", "Settings", "DisableFog", fogIsDisabled ? "True" : "False");
NativeLibrary.SetConfig("GFX.ini", "Stereoscopy", "StereoMode", stereoscopyMode);
NativeLibrary.SetConfig("GFX.ini", "Stereoscopy", "StereoSwapEyes", stereoscopyEyeSwap ? "True" : "False");
NativeLibrary.SetConfig("GFX.ini", "Stereoscopy", "StereoDepth", stereoscopySeparation);
NativeLibrary.SetConfig("GFX.ini", "Stereoscopy", "StereoConvergence", stereoConvergencePercentage);
NativeLibrary.SetConfig("Dolphin.ini", "Core", "SIDevice0", "6");
NativeLibrary.SetConfig("Dolphin.ini", "Core", "SIDevice1", enableController2 ? "6" : "0");
NativeLibrary.SetConfig("Dolphin.ini", "Core", "SIDevice2", enableController3 ? "6" : "0");
NativeLibrary.SetConfig("Dolphin.ini", "Core", "SIDevice3", enableController4 ? "6" : "0");
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 304 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 737 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 759 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,20 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/frame_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/activity_horizontal_margin"
android:layout_marginRight="@dimen/activity_horizontal_margin"
tools:listitem="@layout/list_item_file"
android:elevation="4dp"
android:background="@android:color/white"/>
</LinearLayout>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frame_content"/>

View File

@ -0,0 +1,37 @@
<?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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<SeekBar
android:id="@+id/seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/spacing_xlarge"
android:layout_marginRight="@dimen/spacing_xlarge"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/text_value"
android:layout_marginBottom="@dimen/spacing_xlarge"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="75"
android:id="@+id/text_value"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/spacing_xlarge"
android:layout_marginBottom="@dimen/spacing_large"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="%"
android:id="@+id/text_units"
android:layout_alignTop="@+id/text_value"
android:layout_toEndOf="@+id/text_value"/>
</RelativeLayout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/list_settings"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

View File

@ -0,0 +1,39 @@
<?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="72dp"
android:background="?android:attr/selectableItemBackground"
android:focusable="true"
android:clickable="true">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Headline"
tools:text="Setting Name"
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_setting_name"
android:textSize="16sp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="@string/overclock_enable_description"
android:layout_alignParentEnd="true"
android:layout_alignParentStart="true"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginEnd="@dimen/spacing_large"
android:layout_marginBottom="@dimen/spacing_large"
android:layout_marginTop="@dimen/spacing_small"
android:id="@+id/text_setting_description"
android:layout_below="@+id/text_setting_name"
android:layout_alignStart="@+id/text_setting_name"/>
</RelativeLayout>

View File

@ -0,0 +1,49 @@
<?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="72dp"
android:background="?android:attr/selectableItemBackground"
android:focusable="true"
android:clickable="true">
<TextView
android:id="@+id/text_setting_name"
style="@style/TextAppearance.AppCompat.Headline"
android:layout_width="0dp"
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:layout_toStartOf="@+id/checkbox"
android:textSize="16sp"
tools:text="@string/overclock_enable"/>
<TextView
android:id="@+id/text_setting_description"
android:layout_width="wrap_content"
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/checkbox"
tools:text="@string/overclock_enable_description"/>
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/spacing_large"
android:focusable="false"
android:clickable="false"/>
</RelativeLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="48dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_header_name"
tools:text="CPU Settings"
android:layout_marginStart="@dimen/spacing_large"
android:layout_marginBottom="@dimen/spacing_small"
android:textColor="?android:colorAccent"
android:textStyle="bold"
android:layout_gravity="left|bottom"/>
</FrameLayout>

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="1"
android:gravity="center_horizontal">
<SeekBar
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/sliderSeekBar"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:text="Sample Text"
android:id="@+id/sliderTextView"
android:textStyle="bold"/>
</LinearLayout>

View File

@ -7,9 +7,14 @@
android:icon="@drawable/ic_refresh"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_settings"
android:title="@string/grid_menu_settings"
android:icon="@drawable/ic_settings"
android:id="@+id/menu_settings_core"
android:title="@string/grid_menu_core_settings"
android:icon="@drawable/ic_settings_core"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_settings_video"
android:title="@string/grid_menu_video_settings"
android:icon="@drawable/ic_settings_graphics"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -9,77 +9,49 @@
<item>@string/jit64_recompiler</item>
<item>@string/jitil_recompiler</item>
</string-array>
<string-array name="emuCoreValuesX86_64" translatable="false">
<integer-array name="emuCoreValuesX86_64" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
</integer-array>
<!-- CPU core selection - ARM -->
<string-array name="emuCoreEntriesARM" translatable="false">
<item>@string/interpreter</item>
<item>@string/jit_arm_recompiler</item>
</string-array>
<string-array name="emuCoreValuesARM" translatable="false">
<integer-array name="emuCoreValuesARM" translatable="false">
<item>0</item>
<item>3</item>
</string-array>
</integer-array>
<!-- CPU core selection - ARM64 -->
<string-array name="emuCoreEntriesARM64" translatable="false">
<item>@string/interpreter</item>
<item>@string/jit_arm64_recompiler</item>
</string-array>
<string-array name="emuCoreValuesARM64" translatable="false">
<integer-array name="emuCoreValuesARM64" translatable="false">
<item>0</item>
<item>4</item>
</string-array>
</integer-array>
<!-- CPU core selection - Other -->
<string-array name="emuCoreEntriesOther" translatable="false">
<item>@string/interpreter</item>
</string-array>
<string-array name="emuCoreValuesOther" translatable="false">
<integer-array name="emuCoreValuesOther" translatable="false">
<item>0</item>
</string-array>
</integer-array>
<!-- New UI CPU Core selection - Default -->
<string-array name="string_emu_cores" translatable="false">
<item>@string/interpreter</item>
<item>@string/cached_interpreter</item>
</string-array>
<string-array name="int_emu_cores" translatable="false">
<integer-array name="int_emu_cores" translatable="false">
<item>0</item>
<item>5</item>
</string-array>
<!-- Video Backend Selection - Supports OpenGL ES 3 -->
<string-array name="videoBackendEntriesGLES3" translatable="false">
<item>@string/software_renderer</item>
<item>@string/opengl_es3</item>
</string-array>
<string-array name="videoBackendValuesGLES3" translatable="false">
<item>Software Renderer</item>
<item>OGL</item>
</string-array>
<!-- Video Backend Selection - Supports desktop OpenGL -->
<string-array name="videoBackendEntriesGL" translatable="false">
<item>@string/software_renderer</item>
<item>@string/opengl</item>
</string-array>
<string-array name="videoBackendValuesGL" translatable="false">
<item>Software Renderer</item>
<item>OGL</item>
</string-array>
<!-- Video Backend Selection - No OpenGL ES 3 support -->
<string-array name="videoBackendEntriesNoGLES3" translatable="false">
<item>@string/software_renderer</item>
</string-array>
<string-array name="videoBackendValuesNoGLES3" translatable="false">
<item>Software Renderer</item>
</string-array>
</integer-array>
<!-- Wiimote extensions -->
<string-array name="wiimoteExtEntries" translatable="false">
@ -99,57 +71,17 @@
<item>Turntable</item>
</string-array>
<!-- EFB Copy Method Preference -->
<string-array name="efbCopyMethodEntries" translatable="false">
<item>@string/disabled</item>
<item>@string/efb_copy_texture</item>
<item>@string/efb_copy_ram_uncached</item>
<item>@string/efb_copy_ram_cached</item>
</string-array>
<string-array name="efbCopyMethodValues" translatable="false">
<item>Off</item>
<item>Texture</item>
<item>RAM (cached)</item>
<item>RAM (uncached)</item>
</string-array>
<!-- Texture Cache Accuracy Preference -->
<string-array name="textureCacheAccuracyEntries" translatable="false">
<item>@string/texture_cache_accuracy_low</item>
<item>@string/texture_cache_accuracy_medium</item>
<item>@string/texture_cache_accuracy_high</item>
</string-array>
<string-array name="textureCacheAccuracyValues" translatable="false">
<integer-array name="textureCacheAccuracyValues" translatable="false">
<item>128</item>
<item>512</item>
<item>0</item>
</string-array>
<!-- Analog Modifier ranges -->
<string-array name="analogRangesEntries" translatable="false">
<item>100</item>
<item>90</item>
<item>80</item>
<item>70</item>
<item>60</item>
<item>50</item>
<item>40</item>
<item>30</item>
<item>20</item>
<item>10</item>
</string-array>
<string-array name="analogRangesValues" translatable="false">
<item>100,000000</item>
<item>90,000000</item>
<item>80,000000</item>
<item>70,000000</item>
<item>60,000000</item>
<item>50,000000</item>
<item>40,000000</item>
<item>30,000000</item>
<item>20,000000</item>
<item>10,000000</item>
</string-array>
</integer-array>
<!-- External Frame Buffer Preference -->
<string-array name="externalFrameBufferEntries" translatable="false">
@ -157,11 +89,11 @@
<item>@string/external_frame_buffer_virtual</item>
<item>@string/external_frame_buffer_real</item>
</string-array>
<string-array name="externalFrameBufferValues" translatable="false">
<item>Disabled</item>
<item>Virtual</item>
<item>Real</item>
</string-array>
<integer-array name="externalFrameBufferValues" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
</integer-array>
<!-- Internal Resolution Preference -->
<string-array name="internalResolutionEntries" translatable="false">
@ -174,7 +106,7 @@
<item>5x Native (3200x2640)</item>
<item>6x Native (3840x3168) for 4K</item>
</string-array>
<string-array name="internalResolutionValues" translatable="false">
<integer-array name="internalResolutionValues" translatable="false">
<item>2</item>
<item>3</item>
<item>4</item>
@ -183,7 +115,7 @@
<item>7</item>
<item>8</item>
<item>9</item>
</string-array>
</integer-array>
<!-- FSAA Preference -->
<string-array name="FSAAEntries" translatable="false">
@ -191,11 +123,11 @@
<item>2x</item>
<item>4x</item>
</string-array>
<string-array name="FSAAValues" translatable="false">
<integer-array name="FSAAValues" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
</string-array>
</integer-array>
<!-- Anisotropic Filtering Preference -->
<string-array name="anisotropicFilteringEntries" translatable="false">
@ -205,13 +137,13 @@
<item>8x</item>
<item>16x</item>
</string-array>
<string-array name="anisotropicFilteringValues" translatable="false">
<integer-array name="anisotropicFilteringValues" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
<item>4</item>
</string-array>
</integer-array>
<!-- Stereoscopy Preference -->
<string-array name="stereoscopyEntries" translatable="false">
@ -220,12 +152,12 @@
<item>Top-and-Bottom</item>
<item>Anaglyph</item>
</string-array>
<string-array name="stereoscopyValues" translatable="false">
<integer-array name="stereoscopyValues" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</integer-array>
<!-- Aspect Ratio Preference -->
<string-array name="aspectRatioEntries" translatable="false">
@ -234,12 +166,12 @@
<item>Force 4:3</item>
<item>Stretch To Window</item>
</string-array>
<string-array name="aspectRatioValues" translatable="false">
<integer-array name="aspectRatioValues" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
</integer-array>
<string-array name="country_names">
<item>Europe</item>

View File

@ -9,4 +9,11 @@
<dimen name="add_button_margin">16dp</dimen>
<dimen name="main_appbar_height">128dp</dimen>
<dimen name="spacing_xsmall">2dp</dimen>
<dimen name="spacing_small">4dp</dimen>
<dimen name="spacing_medium">8dp</dimen>
<dimen name="spacing_medlarge">12dp</dimen>
<dimen name="spacing_large">16dp</dimen>
<dimen name="spacing_xlarge">32dp</dimen>
</resources>

View File

@ -286,11 +286,8 @@
<string name="skip_efb_access_descrip">Ignore any requests from the CPU to read/write to the EFB.</string>
<string name="ignore_format_changes">Ignore Format Changes</string>
<string name="ignore_format_changes_descrip">Ignore any changes to the EFB format.</string>
<string name="efb_copy_method">EFB Copy Method</string>
<string name="efb_copy_method_descrip">Determines how EFB copies will be emulated.</string>
<string name="efb_copy_texture">Texture</string>
<string name="efb_copy_ram_uncached">RAM (uncached)</string>
<string name="efb_copy_ram_cached">RAM (cached)</string>
<string name="efb_copy_method">Store EFB Copies to Texture Only</string>
<string name="efb_copy_method_descrip">Stores EFB Copies exclusively on the GPU, bypassing system memory. Causes graphical defects in a small number of games. If unsure, leave this checked.</string>
<string name="texture_cache">Texture Cache</string>
<string name="texture_cache_accuracy">Texture Cache Accuracy</string>
<string name="texture_cache_accuracy_descrip">The safer the selection, the less likely the emulator will be missing any texture updates from RAM.</string>
@ -320,7 +317,8 @@
<!-- Game Grid Screen-->
<string name="grid_menu_settings">Settings</string>
<string name="grid_menu_core_settings">Settings</string>
<string name="grid_menu_video_settings">Video Settings</string>
<string name="grid_menu_refresh">Refresh Library</string>
<!-- Add Directory Screen-->
@ -334,6 +332,8 @@
<string name="preferences_extensions">Extension Bindings</string>
<string name="preferences_video">Video Settings</string>
<string name="emulation_title">Emulation Activity</string>
<string name="dialog_seekbar_pos">OK</string>
<string name="dialog_seekbar_neg">Cancel</string>
<!-- Emulation Menu -->
<string name="emulation_toggle_input">Toggle Touch Controls</string>

View File

@ -17,7 +17,7 @@
</style>
<!-- Same as above, but use default action bar, and mandate margins. -->
<style name="DolphinSettingsBase" parent="Theme.AppCompat.Light.NoActionBar">
<style name="DolphinSettingsBase" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/dolphin_blue</item>
<item name="colorPrimaryDark">@color/dolphin_blue_dark</item>
</style>

File diff suppressed because it is too large Load Diff