summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar bunnei2023-01-08 17:31:30 -0800
committerGravatar bunnei2023-06-03 00:05:28 -0700
commit4f903d8d3541b8898629e5c01c48f0d2e5a2f162 (patch)
tree90c668f6e9bd6bc01ae04e4d884e3b2339568ccf
parentexternals: add adrenotools for bcenabler (diff)
downloadyuzu-4f903d8d3541b8898629e5c01c48f0d2e5a2f162.tar.gz
yuzu-4f903d8d3541b8898629e5c01c48f0d2e5a2f162.tar.xz
yuzu-4f903d8d3541b8898629e5c01c48f0d2e5a2f162.zip
android: Integrate settings frontend with yuzu & remove unused code.
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java197
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java140
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java10
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java382
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java12
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java59
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java125
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java198
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java55
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java57
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java14
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java102
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java1
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java54
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java11
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java215
-rw-r--r--src/android/app/src/main/jni/CMakeLists.txt3
-rw-r--r--src/android/app/src/main/jni/config.cpp284
-rw-r--r--src/android/app/src/main/jni/config.h37
-rw-r--r--src/android/app/src/main/jni/default_ini.h499
-rw-r--r--src/android/app/src/main/jni/native.cpp12
-rw-r--r--src/android/app/src/main/res/menu/menu_game_grid.xml5
-rw-r--r--src/android/app/src/main/res/values/arrays.xml112
-rw-r--r--src/android/app/src/main/res/values/strings.xml113
25 files changed, 949 insertions, 1759 deletions
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
index cd64a3298..f4fca40e4 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.java
@@ -3,17 +3,13 @@ package org.yuzu.yuzu_emu.activities;
3import android.app.Activity; 3import android.app.Activity;
4import android.content.Intent; 4import android.content.Intent;
5import android.content.SharedPreferences; 5import android.content.SharedPreferences;
6import android.content.pm.PackageManager;
7import android.graphics.Rect; 6import android.graphics.Rect;
8import android.os.Bundle; 7import android.os.Bundle;
9import android.os.Handler; 8import android.os.Handler;
10import android.preference.PreferenceManager; 9import android.preference.PreferenceManager;
11import android.util.SparseIntArray;
12import android.view.InputDevice; 10import android.view.InputDevice;
13import android.view.KeyEvent; 11import android.view.KeyEvent;
14import android.view.LayoutInflater; 12import android.view.LayoutInflater;
15import android.view.Menu;
16import android.view.MenuItem;
17import android.view.MotionEvent; 13import android.view.MotionEvent;
18import android.view.View; 14import android.view.View;
19import android.widget.SeekBar; 15import android.widget.SeekBar;
@@ -31,20 +27,14 @@ import androidx.fragment.app.FragmentManager;
31 27
32import org.yuzu.yuzu_emu.NativeLibrary; 28import org.yuzu.yuzu_emu.NativeLibrary;
33import org.yuzu.yuzu_emu.R; 29import org.yuzu.yuzu_emu.R;
34import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
35import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity;
36import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
37import org.yuzu.yuzu_emu.fragments.EmulationFragment; 30import org.yuzu.yuzu_emu.fragments.EmulationFragment;
38import org.yuzu.yuzu_emu.fragments.MenuFragment; 31import org.yuzu.yuzu_emu.fragments.MenuFragment;
39import org.yuzu.yuzu_emu.utils.ControllerMappingHelper; 32import org.yuzu.yuzu_emu.utils.ControllerMappingHelper;
40import org.yuzu.yuzu_emu.utils.EmulationMenuSettings;
41import org.yuzu.yuzu_emu.utils.ForegroundService; 33import org.yuzu.yuzu_emu.utils.ForegroundService;
42 34
43import java.lang.annotation.Retention; 35import java.lang.annotation.Retention;
44import java.util.List; 36import java.util.List;
45 37
46import static android.Manifest.permission.CAMERA;
47import static android.Manifest.permission.RECORD_AUDIO;
48import static java.lang.annotation.RetentionPolicy.SOURCE; 38import static java.lang.annotation.RetentionPolicy.SOURCE;
49 39
50public final class EmulationActivity extends AppCompatActivity { 40public final class EmulationActivity extends AppCompatActivity {
@@ -198,77 +188,6 @@ public final class EmulationActivity extends AppCompatActivity {
198 } 188 }
199 } 189 }
200 190
201 // Gets button presses
202 @Override
203 public boolean dispatchKeyEvent(KeyEvent event) {
204 if (mMenuVisible || event.getKeyCode() == KeyEvent.KEYCODE_BACK)
205 {
206 return super.dispatchKeyEvent(event);
207 }
208
209 int action;
210 int button = mPreferences.getInt(InputBindingSetting.getInputButtonKey(event.getKeyCode()), event.getKeyCode());
211
212 switch (event.getAction()) {
213 case KeyEvent.ACTION_DOWN:
214 // Handling the case where the back button is pressed.
215 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
216 onBackPressed();
217 return true;
218 }
219
220 // Normal key events.
221 action = NativeLibrary.ButtonState.PRESSED;
222 break;
223 case KeyEvent.ACTION_UP:
224 action = NativeLibrary.ButtonState.RELEASED;
225 break;
226 default:
227 return false;
228 }
229 InputDevice input = event.getDevice();
230
231 if (input == null) {
232 // Controller was disconnected
233 return false;
234 }
235
236 return NativeLibrary.onGamePadEvent(input.getDescriptor(), button, action);
237 }
238
239 private void toggleControls() {
240 final SharedPreferences.Editor editor = mPreferences.edit();
241 boolean[] enabledButtons = new boolean[14];
242 AlertDialog.Builder builder = new AlertDialog.Builder(this);
243 builder.setTitle(R.string.emulation_toggle_controls);
244
245 for (int i = 0; i < enabledButtons.length; i++) {
246 // Buttons that are disabled by default
247 boolean defaultValue = true;
248 switch (i) {
249 case 6: // ZL
250 case 7: // ZR
251 case 12: // C-stick
252 defaultValue = false;
253 break;
254 }
255
256 enabledButtons[i] = mPreferences.getBoolean("buttonToggle" + i, defaultValue);
257 }
258 builder.setMultiChoiceItems(R.array.n3dsButtons, enabledButtons,
259 (dialog, indexSelected, isChecked) -> editor
260 .putBoolean("buttonToggle" + indexSelected, isChecked));
261 builder.setPositiveButton(android.R.string.ok, (dialogInterface, i) ->
262 {
263 editor.apply();
264
265 mEmulationFragment.refreshInputOverlay();
266 });
267
268 AlertDialog alertDialog = builder.create();
269 alertDialog.show();
270 }
271
272 private void adjustScale() { 191 private void adjustScale() {
273 LayoutInflater inflater = LayoutInflater.from(this); 192 LayoutInflater inflater = LayoutInflater.from(this);
274 View view = inflater.inflate(R.layout.dialog_seekbar, null); 193 View view = inflater.inflate(R.layout.dialog_seekbar, null);
@@ -377,122 +296,6 @@ public final class EmulationActivity extends AppCompatActivity {
377 return super.dispatchTouchEvent(event); 296 return super.dispatchTouchEvent(event);
378 } 297 }
379 298
380 @Override
381 public boolean dispatchGenericMotionEvent(MotionEvent event) {
382 if (mMenuVisible)
383 {
384 return false;
385 }
386
387 if (((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)) {
388 return super.dispatchGenericMotionEvent(event);
389 }
390
391 // Don't attempt to do anything if we are disconnecting a device.
392 if (event.getActionMasked() == MotionEvent.ACTION_CANCEL) {
393 return true;
394 }
395
396 InputDevice input = event.getDevice();
397 List<InputDevice.MotionRange> motions = input.getMotionRanges();
398
399 float[] axisValuesCirclePad = {0.0f, 0.0f};
400 float[] axisValuesCStick = {0.0f, 0.0f};
401 float[] axisValuesDPad = {0.0f, 0.0f};
402 boolean isTriggerPressedLMapped = false;
403 boolean isTriggerPressedRMapped = false;
404 boolean isTriggerPressedZLMapped = false;
405 boolean isTriggerPressedZRMapped = false;
406 boolean isTriggerPressedL = false;
407 boolean isTriggerPressedR = false;
408 boolean isTriggerPressedZL = false;
409 boolean isTriggerPressedZR = false;
410
411 for (InputDevice.MotionRange range : motions) {
412 int axis = range.getAxis();
413 float origValue = event.getAxisValue(axis);
414 float value = mControllerMappingHelper.scaleAxis(input, axis, origValue);
415 int nextMapping = mPreferences.getInt(InputBindingSetting.getInputAxisButtonKey(axis), -1);
416 int guestOrientation = mPreferences.getInt(InputBindingSetting.getInputAxisOrientationKey(axis), -1);
417
418 if (nextMapping == -1 || guestOrientation == -1) {
419 // Axis is unmapped
420 continue;
421 }
422
423 if ((value > 0.f && value < 0.1f) || (value < 0.f && value > -0.1f)) {
424 // Skip joystick wobble
425 value = 0.f;
426 }
427
428 if (nextMapping == NativeLibrary.ButtonType.STICK_LEFT) {
429 axisValuesCirclePad[guestOrientation] = value;
430 } else if (nextMapping == NativeLibrary.ButtonType.STICK_C) {
431 axisValuesCStick[guestOrientation] = value;
432 } else if (nextMapping == NativeLibrary.ButtonType.DPAD) {
433 axisValuesDPad[guestOrientation] = value;
434 } else if (nextMapping == NativeLibrary.ButtonType.TRIGGER_L) {
435 isTriggerPressedLMapped = true;
436 isTriggerPressedL = value != 0.f;
437 } else if (nextMapping == NativeLibrary.ButtonType.TRIGGER_R) {
438 isTriggerPressedRMapped = true;
439 isTriggerPressedR = value != 0.f;
440 } else if (nextMapping == NativeLibrary.ButtonType.BUTTON_ZL) {
441 isTriggerPressedZLMapped = true;
442 isTriggerPressedZL = value != 0.f;
443 } else if (nextMapping == NativeLibrary.ButtonType.BUTTON_ZR) {
444 isTriggerPressedZRMapped = true;
445 isTriggerPressedZR = value != 0.f;
446 }
447 }
448
449 // Circle-Pad and C-Stick status
450 NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), NativeLibrary.ButtonType.STICK_LEFT, axisValuesCirclePad[0], axisValuesCirclePad[1]);
451 NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), NativeLibrary.ButtonType.STICK_C, axisValuesCStick[0], axisValuesCStick[1]);
452
453 // Triggers L/R and ZL/ZR
454 if (isTriggerPressedLMapped) {
455 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.TRIGGER_L, isTriggerPressedL ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
456 }
457 if (isTriggerPressedRMapped) {
458 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.TRIGGER_R, isTriggerPressedR ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
459 }
460 if (isTriggerPressedZLMapped) {
461 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.BUTTON_ZL, isTriggerPressedZL ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
462 }
463 if (isTriggerPressedZRMapped) {
464 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.BUTTON_ZR, isTriggerPressedZR ? NativeLibrary.ButtonState.PRESSED : NativeLibrary.ButtonState.RELEASED);
465 }
466
467 // Work-around to allow D-pad axis to be bound to emulated buttons
468 if (axisValuesDPad[0] == 0.f) {
469 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_LEFT, NativeLibrary.ButtonState.RELEASED);
470 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_RIGHT, NativeLibrary.ButtonState.RELEASED);
471 }
472 if (axisValuesDPad[0] < 0.f) {
473 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_LEFT, NativeLibrary.ButtonState.PRESSED);
474 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_RIGHT, NativeLibrary.ButtonState.RELEASED);
475 }
476 if (axisValuesDPad[0] > 0.f) {
477 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_LEFT, NativeLibrary.ButtonState.RELEASED);
478 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_RIGHT, NativeLibrary.ButtonState.PRESSED);
479 }
480 if (axisValuesDPad[1] == 0.f) {
481 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_UP, NativeLibrary.ButtonState.RELEASED);
482 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_DOWN, NativeLibrary.ButtonState.RELEASED);
483 }
484 if (axisValuesDPad[1] < 0.f) {
485 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_UP, NativeLibrary.ButtonState.PRESSED);
486 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_DOWN, NativeLibrary.ButtonState.RELEASED);
487 }
488 if (axisValuesDPad[1] > 0.f) {
489 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_UP, NativeLibrary.ButtonState.RELEASED);
490 NativeLibrary.onGamePadEvent(NativeLibrary.TouchScreenDevice, NativeLibrary.ButtonType.DPAD_DOWN, NativeLibrary.ButtonState.PRESSED);
491 }
492
493 return true;
494 }
495
496 public boolean isActivityRecreated() { 299 public boolean isActivityRecreated() {
497 return activityRecreated; 300 return activityRecreated;
498 } 301 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java
deleted file mode 100644
index 874c1acbc..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/dialogs/MotionAlertDialog.java
+++ /dev/null
@@ -1,140 +0,0 @@
1package org.yuzu.yuzu_emu.dialogs;
2
3import android.content.Context;
4import android.view.InputDevice;
5import android.view.KeyEvent;
6import android.view.MotionEvent;
7
8import androidx.annotation.NonNull;
9import androidx.appcompat.app.AlertDialog;
10
11import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
12import org.yuzu.yuzu_emu.utils.Log;
13
14import java.util.ArrayList;
15import java.util.List;
16
17/**
18 * {@link AlertDialog} derivative that listens for
19 * motion events from controllers and joysticks.
20 */
21public final class MotionAlertDialog extends AlertDialog {
22 // The selected input preference
23 private final InputBindingSetting setting;
24 private final ArrayList<Float> mPreviousValues = new ArrayList<>();
25 private int mPrevDeviceId = 0;
26 private boolean mWaitingForEvent = true;
27
28 /**
29 * Constructor
30 *
31 * @param context The current {@link Context}.
32 * @param setting The Preference to show this dialog for.
33 */
34 public MotionAlertDialog(Context context, InputBindingSetting setting) {
35 super(context);
36
37 this.setting = setting;
38 }
39
40 public boolean onKeyEvent(int keyCode, KeyEvent event) {
41 Log.debug("[MotionAlertDialog] Received key event: " + event.getAction());
42 switch (event.getAction()) {
43 case KeyEvent.ACTION_UP:
44 setting.onKeyInput(event);
45 dismiss();
46 // Even if we ignore the key, we still consume it. Thus return true regardless.
47 return true;
48
49 default:
50 return false;
51 }
52 }
53
54 @Override
55 public boolean onKeyLongPress(int keyCode, @NonNull KeyEvent event) {
56 return super.onKeyLongPress(keyCode, event);
57 }
58
59 @Override
60 public boolean dispatchKeyEvent(KeyEvent event) {
61 // Handle this key if we care about it, otherwise pass it down the framework
62 return onKeyEvent(event.getKeyCode(), event) || super.dispatchKeyEvent(event);
63 }
64
65 @Override
66 public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event) {
67 // Handle this event if we care about it, otherwise pass it down the framework
68 return onMotionEvent(event) || super.dispatchGenericMotionEvent(event);
69 }
70
71 private boolean onMotionEvent(MotionEvent event) {
72 if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)
73 return false;
74 if (event.getAction() != MotionEvent.ACTION_MOVE)
75 return false;
76
77 InputDevice input = event.getDevice();
78
79 List<InputDevice.MotionRange> motionRanges = input.getMotionRanges();
80
81 if (input.getId() != mPrevDeviceId) {
82 mPreviousValues.clear();
83 }
84 mPrevDeviceId = input.getId();
85 boolean firstEvent = mPreviousValues.isEmpty();
86
87 int numMovedAxis = 0;
88 float axisMoveValue = 0.0f;
89 InputDevice.MotionRange lastMovedRange = null;
90 char lastMovedDir = '?';
91 if (mWaitingForEvent) {
92 for (int i = 0; i < motionRanges.size(); i++) {
93 InputDevice.MotionRange range = motionRanges.get(i);
94 int axis = range.getAxis();
95 float origValue = event.getAxisValue(axis);
96 float value = origValue;//ControllerMappingHelper.scaleAxis(input, axis, origValue);
97 if (firstEvent) {
98 mPreviousValues.add(value);
99 } else {
100 float previousValue = mPreviousValues.get(i);
101
102 // Only handle the axes that are not neutral (more than 0.5)
103 // but ignore any axis that has a constant value (e.g. always 1)
104 if (Math.abs(value) > 0.5f && value != previousValue) {
105 // It is common to have multiple axes with the same physical input. For example,
106 // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE.
107 // To handle this, we ignore an axis motion that's the exact same as a motion
108 // we already saw. This way, we ignore axes with two names, but catch the case
109 // where a joystick is moved in two directions.
110 // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html
111 if (value != axisMoveValue) {
112 axisMoveValue = value;
113 numMovedAxis++;
114 lastMovedRange = range;
115 lastMovedDir = value < 0.0f ? '-' : '+';
116 }
117 }
118 // Special case for d-pads (axis value jumps between 0 and 1 without any values
119 // in between). Without this, the user would need to press the d-pad twice
120 // due to the first press being caught by the "if (firstEvent)" case further up.
121 else if (Math.abs(value) < 0.25f && Math.abs(previousValue) > 0.75f) {
122 numMovedAxis++;
123 lastMovedRange = range;
124 lastMovedDir = previousValue < 0.0f ? '-' : '+';
125 }
126 }
127
128 mPreviousValues.set(i, value);
129 }
130
131 // If only one axis moved, that's the winner.
132 if (numMovedAxis == 1) {
133 mWaitingForEvent = false;
134 setting.onMotionInput(input, lastMovedRange, lastMovedDir);
135 dismiss();
136 }
137 }
138 return true;
139 }
140} \ No newline at end of file
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java
index efde45ee9..3126eba73 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.java
@@ -14,22 +14,18 @@ import java.util.Map;
14import java.util.TreeMap; 14import java.util.TreeMap;
15 15
16public class Settings { 16public class Settings {
17 public static final String SECTION_PREMIUM = "Premium"; 17 public static final String SECTION_GENERAL = "General";
18 public static final String SECTION_CORE = "Core";
19 public static final String SECTION_SYSTEM = "System"; 18 public static final String SECTION_SYSTEM = "System";
20 public static final String SECTION_CONTROLS = "Controls";
21 public static final String SECTION_RENDERER = "Renderer"; 19 public static final String SECTION_RENDERER = "Renderer";
22 public static final String SECTION_LAYOUT = "Layout";
23 public static final String SECTION_UTILITY = "Utility";
24 public static final String SECTION_AUDIO = "Audio"; 20 public static final String SECTION_AUDIO = "Audio";
25 public static final String SECTION_DEBUG = "Debug"; 21 public static final String SECTION_CPU = "Cpu";
26 22
27 private String gameId; 23 private String gameId;
28 24
29 private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>(); 25 private static final Map<String, List<String>> configFileSectionsMap = new HashMap<>();
30 26
31 static { 27 static {
32 configFileSectionsMap.put(SettingsFile.FILE_NAME_CONFIG, Arrays.asList(SECTION_PREMIUM, SECTION_CORE, SECTION_SYSTEM, SECTION_CONTROLS, SECTION_RENDERER, SECTION_LAYOUT, SECTION_UTILITY, SECTION_AUDIO, SECTION_DEBUG)); 28 configFileSectionsMap.put(SettingsFile.FILE_NAME_CONFIG, Arrays.asList(SECTION_GENERAL, SECTION_SYSTEM, SECTION_RENDERER, SECTION_AUDIO, SECTION_CPU));
33 } 29 }
34 30
35 /** 31 /**
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java
deleted file mode 100644
index 4ad54421e..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/InputBindingSetting.java
+++ /dev/null
@@ -1,382 +0,0 @@
1package org.yuzu.yuzu_emu.features.settings.model.view;
2
3import android.content.SharedPreferences;
4import android.preference.PreferenceManager;
5import android.view.InputDevice;
6import android.view.KeyEvent;
7import android.widget.Toast;
8
9import org.yuzu.yuzu_emu.YuzuApplication;
10import org.yuzu.yuzu_emu.NativeLibrary;
11import org.yuzu.yuzu_emu.R;
12import org.yuzu.yuzu_emu.features.settings.model.Setting;
13import org.yuzu.yuzu_emu.features.settings.model.StringSetting;
14import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
15
16public final class InputBindingSetting extends SettingsItem {
17 private static final String INPUT_MAPPING_PREFIX = "InputMapping";
18
19 public InputBindingSetting(String key, String section, int titleId, Setting setting) {
20 super(key, section, setting, titleId, 0);
21 }
22
23 public String getValue() {
24 if (getSetting() == null) {
25 return "";
26 }
27
28 StringSetting setting = (StringSetting) getSetting();
29 return setting.getValue();
30 }
31
32 /**
33 * Returns true if this key is for the 3DS Circle Pad
34 */
35 private boolean IsCirclePad() {
36 switch (getKey()) {
37 case SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL:
38 case SettingsFile.KEY_CIRCLEPAD_AXIS_VERTICAL:
39 return true;
40 }
41 return false;
42 }
43
44 /**
45 * Returns true if this key is for a horizontal axis for a 3DS analog stick or D-pad
46 */
47 public boolean IsHorizontalOrientation() {
48 switch (getKey()) {
49 case SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL:
50 case SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL:
51 case SettingsFile.KEY_DPAD_AXIS_HORIZONTAL:
52 return true;
53 }
54 return false;
55 }
56
57 /**
58 * Returns true if this key is for the 3DS C-Stick
59 */
60 private boolean IsCStick() {
61 switch (getKey()) {
62 case SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL:
63 case SettingsFile.KEY_CSTICK_AXIS_VERTICAL:
64 return true;
65 }
66 return false;
67 }
68
69 /**
70 * Returns true if this key is for the 3DS D-Pad
71 */
72 private boolean IsDPad() {
73 switch (getKey()) {
74 case SettingsFile.KEY_DPAD_AXIS_HORIZONTAL:
75 case SettingsFile.KEY_DPAD_AXIS_VERTICAL:
76 return true;
77 }
78 return false;
79 }
80
81 /**
82 * Returns true if this key is for the 3DS L/R or ZL/ZR buttons. Note, these are not real
83 * triggers on the 3DS, but we support them as such on a physical gamepad.
84 */
85 public boolean IsTrigger() {
86 switch (getKey()) {
87 case SettingsFile.KEY_BUTTON_L:
88 case SettingsFile.KEY_BUTTON_R:
89 case SettingsFile.KEY_BUTTON_ZL:
90 case SettingsFile.KEY_BUTTON_ZR:
91 return true;
92 }
93 return false;
94 }
95
96 /**
97 * Returns true if a gamepad axis can be used to map this key.
98 */
99 public boolean IsAxisMappingSupported() {
100 return IsCirclePad() || IsCStick() || IsDPad() || IsTrigger();
101 }
102
103 /**
104 * Returns true if a gamepad button can be used to map this key.
105 */
106 private boolean IsButtonMappingSupported() {
107 return !IsAxisMappingSupported() || IsTrigger();
108 }
109
110 /**
111 * Returns the yuzu button code for the settings key.
112 */
113 private int getButtonCode() {
114 switch (getKey()) {
115 case SettingsFile.KEY_BUTTON_A:
116 return NativeLibrary.ButtonType.BUTTON_A;
117 case SettingsFile.KEY_BUTTON_B:
118 return NativeLibrary.ButtonType.BUTTON_B;
119 case SettingsFile.KEY_BUTTON_X:
120 return NativeLibrary.ButtonType.BUTTON_X;
121 case SettingsFile.KEY_BUTTON_Y:
122 return NativeLibrary.ButtonType.BUTTON_Y;
123 case SettingsFile.KEY_BUTTON_L:
124 return NativeLibrary.ButtonType.TRIGGER_L;
125 case SettingsFile.KEY_BUTTON_R:
126 return NativeLibrary.ButtonType.TRIGGER_R;
127 case SettingsFile.KEY_BUTTON_ZL:
128 return NativeLibrary.ButtonType.BUTTON_ZL;
129 case SettingsFile.KEY_BUTTON_ZR:
130 return NativeLibrary.ButtonType.BUTTON_ZR;
131 case SettingsFile.KEY_BUTTON_SELECT:
132 return NativeLibrary.ButtonType.BUTTON_SELECT;
133 case SettingsFile.KEY_BUTTON_START:
134 return NativeLibrary.ButtonType.BUTTON_START;
135 case SettingsFile.KEY_BUTTON_UP:
136 return NativeLibrary.ButtonType.DPAD_UP;
137 case SettingsFile.KEY_BUTTON_DOWN:
138 return NativeLibrary.ButtonType.DPAD_DOWN;
139 case SettingsFile.KEY_BUTTON_LEFT:
140 return NativeLibrary.ButtonType.DPAD_LEFT;
141 case SettingsFile.KEY_BUTTON_RIGHT:
142 return NativeLibrary.ButtonType.DPAD_RIGHT;
143 }
144 return -1;
145 }
146
147 /**
148 * Returns the settings key for the specified yuzu button code.
149 */
150 private static String getButtonKey(int buttonCode) {
151 switch (buttonCode) {
152 case NativeLibrary.ButtonType.BUTTON_A:
153 return SettingsFile.KEY_BUTTON_A;
154 case NativeLibrary.ButtonType.BUTTON_B:
155 return SettingsFile.KEY_BUTTON_B;
156 case NativeLibrary.ButtonType.BUTTON_X:
157 return SettingsFile.KEY_BUTTON_X;
158 case NativeLibrary.ButtonType.BUTTON_Y:
159 return SettingsFile.KEY_BUTTON_Y;
160 case NativeLibrary.ButtonType.TRIGGER_L:
161 return SettingsFile.KEY_BUTTON_L;
162 case NativeLibrary.ButtonType.TRIGGER_R:
163 return SettingsFile.KEY_BUTTON_R;
164 case NativeLibrary.ButtonType.BUTTON_ZL:
165 return SettingsFile.KEY_BUTTON_ZL;
166 case NativeLibrary.ButtonType.BUTTON_ZR:
167 return SettingsFile.KEY_BUTTON_ZR;
168 case NativeLibrary.ButtonType.BUTTON_SELECT:
169 return SettingsFile.KEY_BUTTON_SELECT;
170 case NativeLibrary.ButtonType.BUTTON_START:
171 return SettingsFile.KEY_BUTTON_START;
172 case NativeLibrary.ButtonType.DPAD_UP:
173 return SettingsFile.KEY_BUTTON_UP;
174 case NativeLibrary.ButtonType.DPAD_DOWN:
175 return SettingsFile.KEY_BUTTON_DOWN;
176 case NativeLibrary.ButtonType.DPAD_LEFT:
177 return SettingsFile.KEY_BUTTON_LEFT;
178 case NativeLibrary.ButtonType.DPAD_RIGHT:
179 return SettingsFile.KEY_BUTTON_RIGHT;
180 }
181 return "";
182 }
183
184 /**
185 * Returns the key used to lookup the reverse mapping for this key, which is used to cleanup old
186 * settings on re-mapping or clearing of a setting.
187 */
188 private String getReverseKey() {
189 String reverseKey = INPUT_MAPPING_PREFIX + "_ReverseMapping_" + getKey();
190
191 if (IsAxisMappingSupported() && !IsTrigger()) {
192 // Triggers are the only axis-supported mappings without orientation
193 reverseKey += "_" + (IsHorizontalOrientation() ? 0 : 1);
194 }
195
196 return reverseKey;
197 }
198
199 /**
200 * Removes the old mapping for this key from the settings, e.g. on user clearing the setting.
201 */
202 public void removeOldMapping() {
203 // Get preferences editor
204 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
205 SharedPreferences.Editor editor = preferences.edit();
206
207 // Try remove all possible keys we wrote for this setting
208 String oldKey = preferences.getString(getReverseKey(), "");
209 if (!oldKey.equals("")) {
210 editor.remove(getKey()); // Used for ui text
211 editor.remove(oldKey); // Used for button mapping
212 editor.remove(oldKey + "_GuestOrientation"); // Used for axis orientation
213 editor.remove(oldKey + "_GuestButton"); // Used for axis button
214 }
215
216 // Apply changes
217 editor.apply();
218 }
219
220 /**
221 * Helper function to get the settings key for an gamepad button.
222 */
223 public static String getInputButtonKey(int keyCode) {
224 return INPUT_MAPPING_PREFIX + "_Button_" + keyCode;
225 }
226
227 /**
228 * Helper function to get the settings key for an gamepad axis.
229 */
230 public static String getInputAxisKey(int axis) {
231 return INPUT_MAPPING_PREFIX + "_HostAxis_" + axis;
232 }
233
234 /**
235 * Helper function to get the settings key for an gamepad axis button (stick or trigger).
236 */
237 public static String getInputAxisButtonKey(int axis) {
238 return getInputAxisKey(axis) + "_GuestButton";
239 }
240
241 /**
242 * Helper function to get the settings key for an gamepad axis orientation.
243 */
244 public static String getInputAxisOrientationKey(int axis) {
245 return getInputAxisKey(axis) + "_GuestOrientation";
246 }
247
248 /**
249 * Helper function to write a gamepad button mapping for the setting.
250 */
251 private void WriteButtonMapping(String key) {
252 // Get preferences editor
253 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
254 SharedPreferences.Editor editor = preferences.edit();
255
256 // Remove mapping for another setting using this input
257 int oldButtonCode = preferences.getInt(key, -1);
258 if (oldButtonCode != -1) {
259 String oldKey = getButtonKey(oldButtonCode);
260 editor.remove(oldKey); // Only need to remove UI text setting, others will be overwritten
261 }
262
263 // Cleanup old mapping for this setting
264 removeOldMapping();
265
266 // Write new mapping
267 editor.putInt(key, getButtonCode());
268
269 // Write next reverse mapping for future cleanup
270 editor.putString(getReverseKey(), key);
271
272 // Apply changes
273 editor.apply();
274 }
275
276 /**
277 * Helper function to write a gamepad axis mapping for the setting.
278 */
279 private void WriteAxisMapping(int axis, int value) {
280 // Get preferences editor
281 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
282 SharedPreferences.Editor editor = preferences.edit();
283
284 // Cleanup old mapping
285 removeOldMapping();
286
287 // Write new mapping
288 editor.putInt(getInputAxisOrientationKey(axis), IsHorizontalOrientation() ? 0 : 1);
289 editor.putInt(getInputAxisButtonKey(axis), value);
290
291 // Write next reverse mapping for future cleanup
292 editor.putString(getReverseKey(), getInputAxisKey(axis));
293
294 // Apply changes
295 editor.apply();
296 }
297
298 /**
299 * Saves the provided key input setting as an Android preference.
300 *
301 * @param keyEvent KeyEvent of this key press.
302 */
303 public void onKeyInput(KeyEvent keyEvent) {
304 if (!IsButtonMappingSupported()) {
305 Toast.makeText(YuzuApplication.getAppContext(), R.string.input_message_analog_only, Toast.LENGTH_LONG).show();
306 return;
307 }
308
309 InputDevice device = keyEvent.getDevice();
310
311 WriteButtonMapping(getInputButtonKey(keyEvent.getKeyCode()));
312
313 String uiString = device.getName() + ": Button " + keyEvent.getKeyCode();
314 setUiString(uiString);
315 }
316
317 /**
318 * Saves the provided motion input setting as an Android preference.
319 *
320 * @param device InputDevice from which the input event originated.
321 * @param motionRange MotionRange of the movement
322 * @param axisDir Either '-' or '+' (currently unused)
323 */
324 public void onMotionInput(InputDevice device, InputDevice.MotionRange motionRange,
325 char axisDir) {
326 if (!IsAxisMappingSupported()) {
327 Toast.makeText(YuzuApplication.getAppContext(), R.string.input_message_button_only, Toast.LENGTH_LONG).show();
328 return;
329 }
330
331 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
332 SharedPreferences.Editor editor = preferences.edit();
333
334 int button;
335 if (IsCirclePad()) {
336 button = NativeLibrary.ButtonType.STICK_LEFT;
337 } else if (IsCStick()) {
338 button = NativeLibrary.ButtonType.STICK_C;
339 } else if (IsDPad()) {
340 button = NativeLibrary.ButtonType.DPAD;
341 } else {
342 button = getButtonCode();
343 }
344
345 WriteAxisMapping(motionRange.getAxis(), button);
346
347 String uiString = device.getName() + ": Axis " + motionRange.getAxis();
348 setUiString(uiString);
349
350 editor.apply();
351 }
352
353 /**
354 * Sets the string to use in the configuration UI for the gamepad input.
355 */
356 private StringSetting setUiString(String ui) {
357 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
358 SharedPreferences.Editor editor = preferences.edit();
359
360 if (getSetting() == null) {
361 StringSetting setting = new StringSetting(getKey(), getSection(), "");
362 setSetting(setting);
363
364 editor.putString(setting.getKey(), ui);
365 editor.apply();
366
367 return setting;
368 } else {
369 StringSetting setting = (StringSetting) getSetting();
370
371 editor.putString(setting.getKey(), ui);
372 editor.apply();
373
374 return null;
375 }
376 }
377
378 @Override
379 public int getType() {
380 return TYPE_INPUT_BINDING;
381 }
382}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java
deleted file mode 100644
index 9bf95ce51..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumHeader.java
+++ /dev/null
@@ -1,12 +0,0 @@
1package org.yuzu.yuzu_emu.features.settings.model.view;
2
3public final class PremiumHeader extends SettingsItem {
4 public PremiumHeader() {
5 super(null, null, null, 0, 0);
6 }
7
8 @Override
9 public int getType() {
10 return SettingsItem.TYPE_PREMIUM;
11 }
12}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java
deleted file mode 100644
index 0c4570c8d..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/PremiumSingleChoiceSetting.java
+++ /dev/null
@@ -1,59 +0,0 @@
1package org.yuzu.yuzu_emu.features.settings.model.view;
2
3import android.content.SharedPreferences;
4import android.preference.PreferenceManager;
5
6import org.yuzu.yuzu_emu.YuzuApplication;
7import org.yuzu.yuzu_emu.R;
8import org.yuzu.yuzu_emu.features.settings.model.Setting;
9import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragmentView;
10
11public final class PremiumSingleChoiceSetting extends SettingsItem {
12 private int mDefaultValue;
13
14 private int mChoicesId;
15 private int mValuesId;
16 private SettingsFragmentView mView;
17
18 private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
19
20 public PremiumSingleChoiceSetting(String key, String section, int titleId, int descriptionId,
21 int choicesId, int valuesId, int defaultValue, Setting setting, SettingsFragmentView view) {
22 super(key, section, setting, titleId, descriptionId);
23 mValuesId = valuesId;
24 mChoicesId = choicesId;
25 mDefaultValue = defaultValue;
26 mView = view;
27 }
28
29 public int getChoicesId() {
30 return mChoicesId;
31 }
32
33 public int getValuesId() {
34 return mValuesId;
35 }
36
37 public int getSelectedValue() {
38 return mPreferences.getInt(getKey(), mDefaultValue);
39 }
40
41 /**
42 * Write a value to the backing int. If that int was previously null,
43 * initializes a new one and returns it, so it can be added to the Hashmap.
44 *
45 * @param selection New value of the int.
46 * @return null if overwritten successfully otherwise; a newly created IntSetting.
47 */
48 public void setSelectedValue(int selection) {
49 final SharedPreferences.Editor editor = mPreferences.edit();
50 editor.putInt(getKey(), selection);
51 editor.apply();
52 mView.showToastMessage(YuzuApplication.getAppContext().getString(R.string.design_updated), false);
53 }
54
55 @Override
56 public int getType() {
57 return TYPE_SINGLE_CHOICE;
58 }
59}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java
index db7fb791a..e2ba9014f 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/view/SettingsItem.java
@@ -17,10 +17,8 @@ public abstract class SettingsItem {
17 public static final int TYPE_SINGLE_CHOICE = 2; 17 public static final int TYPE_SINGLE_CHOICE = 2;
18 public static final int TYPE_SLIDER = 3; 18 public static final int TYPE_SLIDER = 3;
19 public static final int TYPE_SUBMENU = 4; 19 public static final int TYPE_SUBMENU = 4;
20 public static final int TYPE_INPUT_BINDING = 5; 20 public static final int TYPE_STRING_SINGLE_CHOICE = 5;
21 public static final int TYPE_STRING_SINGLE_CHOICE = 6; 21 public static final int TYPE_DATETIME_SETTING = 6;
22 public static final int TYPE_DATETIME_SETTING = 7;
23 public static final int TYPE_PREMIUM = 8;
24 22
25 private String mKey; 23 private String mKey;
26 private String mSection; 24 private String mSection;
@@ -48,7 +46,6 @@ public abstract class SettingsItem {
48 mSetting = setting; 46 mSetting = setting;
49 mNameId = nameId; 47 mNameId = nameId;
50 mDescriptionId = descriptionId; 48 mDescriptionId = descriptionId;
51 mIsPremium = (section == Settings.SECTION_PREMIUM);
52 } 49 }
53 50
54 /** 51 /**
@@ -93,10 +90,6 @@ public abstract class SettingsItem {
93 return mDescriptionId; 90 return mDescriptionId;
94 } 91 }
95 92
96 public boolean isPremium() {
97 return mIsPremium;
98 }
99
100 /** 93 /**
101 * Used by {@link SettingsAdapter}'s onCreateViewHolder() 94 * Used by {@link SettingsAdapter}'s onCreateViewHolder()
102 * method to determine which type of ViewHolder should be created. 95 * method to determine which type of ViewHolder should be created.
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java
index 1102d6af1..47e73bfe2 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsAdapter.java
@@ -14,14 +14,11 @@ import androidx.appcompat.app.AlertDialog;
14import androidx.recyclerview.widget.RecyclerView; 14import androidx.recyclerview.widget.RecyclerView;
15 15
16import org.yuzu.yuzu_emu.R; 16import org.yuzu.yuzu_emu.R;
17import org.yuzu.yuzu_emu.dialogs.MotionAlertDialog;
18import org.yuzu.yuzu_emu.features.settings.model.FloatSetting; 17import org.yuzu.yuzu_emu.features.settings.model.FloatSetting;
19import org.yuzu.yuzu_emu.features.settings.model.IntSetting; 18import org.yuzu.yuzu_emu.features.settings.model.IntSetting;
20import org.yuzu.yuzu_emu.features.settings.model.StringSetting; 19import org.yuzu.yuzu_emu.features.settings.model.StringSetting;
21import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting; 20import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting;
22import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting; 21import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting;
23import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
24import org.yuzu.yuzu_emu.features.settings.model.view.PremiumSingleChoiceSetting;
25import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; 22import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
26import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; 23import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting;
27import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting; 24import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting;
@@ -30,13 +27,10 @@ import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting;
30import org.yuzu.yuzu_emu.features.settings.ui.viewholder.CheckBoxSettingViewHolder; 27import org.yuzu.yuzu_emu.features.settings.ui.viewholder.CheckBoxSettingViewHolder;
31import org.yuzu.yuzu_emu.features.settings.ui.viewholder.DateTimeViewHolder; 28import org.yuzu.yuzu_emu.features.settings.ui.viewholder.DateTimeViewHolder;
32import org.yuzu.yuzu_emu.features.settings.ui.viewholder.HeaderViewHolder; 29import org.yuzu.yuzu_emu.features.settings.ui.viewholder.HeaderViewHolder;
33import org.yuzu.yuzu_emu.features.settings.ui.viewholder.InputBindingSettingViewHolder;
34import org.yuzu.yuzu_emu.features.settings.ui.viewholder.PremiumViewHolder;
35import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SettingViewHolder; 30import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SettingViewHolder;
36import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SingleChoiceViewHolder; 31import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SingleChoiceViewHolder;
37import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SliderViewHolder; 32import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SliderViewHolder;
38import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SubmenuViewHolder; 33import org.yuzu.yuzu_emu.features.settings.ui.viewholder.SubmenuViewHolder;
39import org.yuzu.yuzu_emu.ui.main.MainActivity;
40import org.yuzu.yuzu_emu.utils.Log; 34import org.yuzu.yuzu_emu.utils.Log;
41 35
42import java.util.ArrayList; 36import java.util.ArrayList;
@@ -87,18 +81,10 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
87 view = inflater.inflate(R.layout.list_item_setting, parent, false); 81 view = inflater.inflate(R.layout.list_item_setting, parent, false);
88 return new SubmenuViewHolder(view, this); 82 return new SubmenuViewHolder(view, this);
89 83
90 case SettingsItem.TYPE_INPUT_BINDING:
91 view = inflater.inflate(R.layout.list_item_setting, parent, false);
92 return new InputBindingSettingViewHolder(view, this, mContext);
93
94 case SettingsItem.TYPE_DATETIME_SETTING: 84 case SettingsItem.TYPE_DATETIME_SETTING:
95 view = inflater.inflate(R.layout.list_item_setting, parent, false); 85 view = inflater.inflate(R.layout.list_item_setting, parent, false);
96 return new DateTimeViewHolder(view, this); 86 return new DateTimeViewHolder(view, this);
97 87
98 case SettingsItem.TYPE_PREMIUM:
99 view = inflater.inflate(R.layout.premium_item_setting, parent, false);
100 return new PremiumViewHolder(view, this, mView);
101
102 default: 88 default:
103 Log.error("[SettingsAdapter] Invalid view type: " + viewType); 89 Log.error("[SettingsAdapter] Invalid view type: " + viewType);
104 return null; 90 return null;
@@ -144,19 +130,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
144 mView.onSettingChanged(); 130 mView.onSettingChanged();
145 } 131 }
146 132
147 public void onSingleChoiceClick(PremiumSingleChoiceSetting item) {
148 mClickedItem = item;
149
150 int value = getSelectionForSingleChoiceValue(item);
151
152 AlertDialog.Builder builder = new AlertDialog.Builder(mView.getActivity());
153
154 builder.setTitle(item.getNameId());
155 builder.setSingleChoiceItems(item.getChoicesId(), value, this);
156
157 mDialog = builder.show();
158 }
159
160 public void onSingleChoiceClick(SingleChoiceSetting item) { 133 public void onSingleChoiceClick(SingleChoiceSetting item) {
161 mClickedItem = item; 134 mClickedItem = item;
162 135
@@ -172,28 +145,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
172 145
173 public void onSingleChoiceClick(SingleChoiceSetting item, int position) { 146 public void onSingleChoiceClick(SingleChoiceSetting item, int position) {
174 mClickedPosition = position; 147 mClickedPosition = position;
175 148 onSingleChoiceClick(item);
176 if (!item.isPremium() || MainActivity.isPremiumActive()) {
177 // Setting is either not Premium, or the user has Premium
178 onSingleChoiceClick(item);
179 return;
180 }
181
182 // User needs Premium, invoke the billing flow
183 MainActivity.invokePremiumBilling(() -> onSingleChoiceClick(item));
184 }
185
186 public void onSingleChoiceClick(PremiumSingleChoiceSetting item, int position) {
187 mClickedPosition = position;
188
189 if (!item.isPremium() || MainActivity.isPremiumActive()) {
190 // Setting is either not Premium, or the user has Premium
191 onSingleChoiceClick(item);
192 return;
193 }
194
195 // User needs Premium, invoke the billing flow
196 MainActivity.invokePremiumBilling(() -> onSingleChoiceClick(item));
197 } 149 }
198 150
199 public void onStringSingleChoiceClick(StringSingleChoiceSetting item) { 151 public void onStringSingleChoiceClick(StringSingleChoiceSetting item) {
@@ -209,15 +161,7 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
209 161
210 public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) { 162 public void onStringSingleChoiceClick(StringSingleChoiceSetting item, int position) {
211 mClickedPosition = position; 163 mClickedPosition = position;
212 164 onStringSingleChoiceClick(item);
213 if (!item.isPremium() || MainActivity.isPremiumActive()) {
214 // Setting is either not Premium, or the user has Premium
215 onStringSingleChoiceClick(item);
216 return;
217 }
218
219 // User needs Premium, invoke the billing flow
220 MainActivity.invokePremiumBilling(() -> onStringSingleChoiceClick(item));
221 } 165 }
222 166
223 DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog(); 167 DialogInterface.OnClickListener defaultCancelListener = (dialog, which) -> closeDialog();
@@ -309,37 +253,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
309 mView.loadSubMenu(item.getMenuKey()); 253 mView.loadSubMenu(item.getMenuKey());
310 } 254 }
311 255
312 public void onInputBindingClick(final InputBindingSetting item, final int position) {
313 final MotionAlertDialog dialog = new MotionAlertDialog(mContext, item);
314 dialog.setTitle(R.string.input_binding);
315
316 int messageResId = R.string.input_binding_description;
317 if (item.IsAxisMappingSupported() && !item.IsTrigger()) {
318 // Use specialized message for axis left/right or up/down
319 if (item.IsHorizontalOrientation()) {
320 messageResId = R.string.input_binding_description_horizontal_axis;
321 } else {
322 messageResId = R.string.input_binding_description_vertical_axis;
323 }
324 }
325
326 dialog.setMessage(String.format(mContext.getString(messageResId), mContext.getString(item.getNameId())));
327 dialog.setButton(AlertDialog.BUTTON_NEGATIVE, mContext.getString(android.R.string.cancel), this);
328 dialog.setButton(AlertDialog.BUTTON_NEUTRAL, mContext.getString(R.string.clear), (dialogInterface, i) ->
329 item.removeOldMapping());
330 dialog.setOnDismissListener(dialog1 ->
331 {
332 StringSetting setting = new StringSetting(item.getKey(), item.getSection(), item.getValue());
333 notifyItemChanged(position);
334
335 mView.putSetting(setting);
336
337 mView.onSettingChanged();
338 });
339 dialog.setCanceledOnTouchOutside(false);
340 dialog.show();
341 }
342
343 @Override 256 @Override
344 public void onClick(DialogInterface dialog, int which) { 257 public void onClick(DialogInterface dialog, int which) {
345 if (mClickedItem instanceof SingleChoiceSetting) { 258 if (mClickedItem instanceof SingleChoiceSetting) {
@@ -357,10 +270,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
357 } 270 }
358 271
359 closeDialog(); 272 closeDialog();
360 } else if (mClickedItem instanceof PremiumSingleChoiceSetting) {
361 PremiumSingleChoiceSetting scSetting = (PremiumSingleChoiceSetting) mClickedItem;
362 scSetting.setSelectedValue(getValueForSingleChoiceSelection(scSetting, which));
363 closeDialog();
364 } else if (mClickedItem instanceof StringSingleChoiceSetting) { 273 } else if (mClickedItem instanceof StringSingleChoiceSetting) {
365 StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem; 274 StringSingleChoiceSetting scSetting = (StringSingleChoiceSetting) mClickedItem;
366 String value = scSetting.getValueAt(which); 275 String value = scSetting.getValueAt(which);
@@ -436,17 +345,6 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
436 } 345 }
437 } 346 }
438 347
439 private int getValueForSingleChoiceSelection(PremiumSingleChoiceSetting item, int which) {
440 int valuesId = item.getValuesId();
441
442 if (valuesId > 0) {
443 int[] valuesArray = mContext.getResources().getIntArray(valuesId);
444 return valuesArray[which];
445 } else {
446 return which;
447 }
448 }
449
450 private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) { 348 private int getSelectionForSingleChoiceValue(SingleChoiceSetting item) {
451 int value = item.getSelectedValue(); 349 int value = item.getSelectedValue();
452 int valuesId = item.getValuesId(); 350 int valuesId = item.getValuesId();
@@ -465,23 +363,4 @@ public final class SettingsAdapter extends RecyclerView.Adapter<SettingViewHolde
465 363
466 return -1; 364 return -1;
467 } 365 }
468
469 private int getSelectionForSingleChoiceValue(PremiumSingleChoiceSetting item) {
470 int value = item.getSelectedValue();
471 int valuesId = item.getValuesId();
472
473 if (valuesId > 0) {
474 int[] valuesArray = mContext.getResources().getIntArray(valuesId);
475 for (int index = 0; index < valuesArray.length; index++) {
476 int current = valuesArray[index];
477 if (current == value) {
478 return index;
479 }
480 }
481 } else {
482 return value;
483 }
484
485 return -1;
486 }
487} 366}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java
index 27f0adf29..c84467c16 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsFragmentPresenter.java
@@ -1,10 +1,5 @@
1package org.yuzu.yuzu_emu.features.settings.ui; 1package org.yuzu.yuzu_emu.features.settings.ui;
2 2
3import android.app.Activity;
4import android.content.Context;
5import android.hardware.camera2.CameraAccessException;
6import android.hardware.camera2.CameraCharacteristics;
7import android.hardware.camera2.CameraManager;
8import android.text.TextUtils; 3import android.text.TextUtils;
9 4
10import org.yuzu.yuzu_emu.R; 5import org.yuzu.yuzu_emu.R;
@@ -15,20 +10,13 @@ import org.yuzu.yuzu_emu.features.settings.model.StringSetting;
15import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting; 10import org.yuzu.yuzu_emu.features.settings.model.view.CheckBoxSetting;
16import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting; 11import org.yuzu.yuzu_emu.features.settings.model.view.DateTimeSetting;
17import org.yuzu.yuzu_emu.features.settings.model.view.HeaderSetting; 12import org.yuzu.yuzu_emu.features.settings.model.view.HeaderSetting;
18import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
19import org.yuzu.yuzu_emu.features.settings.model.view.PremiumHeader;
20import org.yuzu.yuzu_emu.features.settings.model.view.PremiumSingleChoiceSetting;
21import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; 13import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
22import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; 14import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting;
23import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting; 15import org.yuzu.yuzu_emu.features.settings.model.view.SliderSetting;
24import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting;
25import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting; 16import org.yuzu.yuzu_emu.features.settings.model.view.SubmenuSetting;
26import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile; 17import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
27import org.yuzu.yuzu_emu.utils.Log;
28 18
29import java.util.ArrayList; 19import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Objects;
32 20
33public final class SettingsFragmentPresenter { 21public final class SettingsFragmentPresenter {
34 private SettingsFragmentView mView; 22 private SettingsFragmentView mView;
@@ -106,27 +94,18 @@ public final class SettingsFragmentPresenter {
106 case SettingsFile.FILE_NAME_CONFIG: 94 case SettingsFile.FILE_NAME_CONFIG:
107 addConfigSettings(sl); 95 addConfigSettings(sl);
108 break; 96 break;
109 case Settings.SECTION_PREMIUM: 97 case Settings.SECTION_GENERAL:
110 addPremiumSettings(sl);
111 break;
112 case Settings.SECTION_CORE:
113 addGeneralSettings(sl); 98 addGeneralSettings(sl);
114 break; 99 break;
115 case Settings.SECTION_SYSTEM: 100 case Settings.SECTION_SYSTEM:
116 addSystemSettings(sl); 101 addSystemSettings(sl);
117 break; 102 break;
118 case Settings.SECTION_CONTROLS:
119 addInputSettings(sl);
120 break;
121 case Settings.SECTION_RENDERER: 103 case Settings.SECTION_RENDERER:
122 addGraphicsSettings(sl); 104 addGraphicsSettings(sl);
123 break; 105 break;
124 case Settings.SECTION_AUDIO: 106 case Settings.SECTION_AUDIO:
125 addAudioSettings(sl); 107 addAudioSettings(sl);
126 break; 108 break;
127 case Settings.SECTION_DEBUG:
128 addDebugSettings(sl);
129 break;
130 default: 109 default:
131 mView.showToastMessage("Unimplemented menu", false); 110 mView.showToastMessage("Unimplemented menu", false);
132 return; 111 return;
@@ -139,184 +118,61 @@ public final class SettingsFragmentPresenter {
139 private void addConfigSettings(ArrayList<SettingsItem> sl) { 118 private void addConfigSettings(ArrayList<SettingsItem> sl) {
140 mView.getActivity().setTitle(R.string.preferences_settings); 119 mView.getActivity().setTitle(R.string.preferences_settings);
141 120
142 sl.add(new SubmenuSetting(null, null, R.string.preferences_premium, 0, Settings.SECTION_PREMIUM)); 121 sl.add(new SubmenuSetting(null, null, R.string.preferences_general, 0, Settings.SECTION_GENERAL));
143 sl.add(new SubmenuSetting(null, null, R.string.preferences_general, 0, Settings.SECTION_CORE));
144 sl.add(new SubmenuSetting(null, null, R.string.preferences_system, 0, Settings.SECTION_SYSTEM)); 122 sl.add(new SubmenuSetting(null, null, R.string.preferences_system, 0, Settings.SECTION_SYSTEM));
145 sl.add(new SubmenuSetting(null, null, R.string.preferences_controls, 0, Settings.SECTION_CONTROLS));
146 sl.add(new SubmenuSetting(null, null, R.string.preferences_graphics, 0, Settings.SECTION_RENDERER)); 123 sl.add(new SubmenuSetting(null, null, R.string.preferences_graphics, 0, Settings.SECTION_RENDERER));
147 sl.add(new SubmenuSetting(null, null, R.string.preferences_audio, 0, Settings.SECTION_AUDIO)); 124 sl.add(new SubmenuSetting(null, null, R.string.preferences_audio, 0, Settings.SECTION_AUDIO));
148 sl.add(new SubmenuSetting(null, null, R.string.preferences_debug, 0, Settings.SECTION_DEBUG));
149 }
150
151 private void addPremiumSettings(ArrayList<SettingsItem> sl) {
152 mView.getActivity().setTitle(R.string.preferences_premium);
153
154 SettingSection premiumSection = mSettings.getSection(Settings.SECTION_PREMIUM);
155 Setting design = premiumSection.getSetting(SettingsFile.KEY_DESIGN);
156
157 sl.add(new PremiumHeader());
158
159 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
160 sl.add(new PremiumSingleChoiceSetting(SettingsFile.KEY_DESIGN, Settings.SECTION_PREMIUM, R.string.design, 0, R.array.designNames, R.array.designValues, 0, design, mView));
161 } else {
162 // Pre-Android 10 does not support System Default
163 sl.add(new PremiumSingleChoiceSetting(SettingsFile.KEY_DESIGN, Settings.SECTION_PREMIUM, R.string.design, 0, R.array.designNamesOld, R.array.designValuesOld, 0, design, mView));
164 }
165
166 //Setting textureFilterName = premiumSection.getSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME);
167 //sl.add(new StringSingleChoiceSetting(SettingsFile.KEY_TEXTURE_FILTER_NAME, Settings.SECTION_PREMIUM, R.string.texture_filter_name, R.string.texture_filter_description, textureFilterNames, textureFilterNames, "none", textureFilterName));
168 } 125 }
169 126
170 private void addGeneralSettings(ArrayList<SettingsItem> sl) { 127 private void addGeneralSettings(ArrayList<SettingsItem> sl) {
171 mView.getActivity().setTitle(R.string.preferences_general); 128 mView.getActivity().setTitle(R.string.preferences_general);
172 129
173 SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); 130 SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
174 Setting frameLimitEnable = rendererSection.getSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED); 131 Setting frameLimitEnable = rendererSection.getSetting(SettingsFile.KEY_RENDERER_USE_SPEED_LIMIT);
175 Setting frameLimitValue = rendererSection.getSetting(SettingsFile.KEY_FRAME_LIMIT); 132 Setting frameLimitValue = rendererSection.getSetting(SettingsFile.KEY_RENDERER_SPEED_LIMIT);
133
134 sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_USE_SPEED_LIMIT, Settings.SECTION_RENDERER, R.string.frame_limit_enable, R.string.frame_limit_enable_description, true, frameLimitEnable));
135 sl.add(new SliderSetting(SettingsFile.KEY_RENDERER_SPEED_LIMIT, Settings.SECTION_RENDERER, R.string.frame_limit_slider, R.string.frame_limit_slider_description, 1, 200, "%", 100, frameLimitValue));
176 136
177 sl.add(new CheckBoxSetting(SettingsFile.KEY_FRAME_LIMIT_ENABLED, Settings.SECTION_RENDERER, R.string.frame_limit_enable, R.string.frame_limit_enable_description, true, frameLimitEnable)); 137 SettingSection cpuSection = mSettings.getSection(Settings.SECTION_CPU);
178 sl.add(new SliderSetting(SettingsFile.KEY_FRAME_LIMIT, Settings.SECTION_RENDERER, R.string.frame_limit_slider, R.string.frame_limit_slider_description, 1, 200, "%", 100, frameLimitValue)); 138 Setting cpuAccuracy = cpuSection.getSetting(SettingsFile.KEY_CPU_ACCURACY);
139 sl.add(new SingleChoiceSetting(SettingsFile.KEY_CPU_ACCURACY, Settings.SECTION_CPU, R.string.cpu_accuracy, 0, R.array.cpuAccuracyNames, R.array.cpuAccuracyValues, 0, cpuAccuracy));
179 } 140 }
180 141
181 private void addSystemSettings(ArrayList<SettingsItem> sl) { 142 private void addSystemSettings(ArrayList<SettingsItem> sl) {
182 mView.getActivity().setTitle(R.string.preferences_system); 143 mView.getActivity().setTitle(R.string.preferences_system);
183 144
184 SettingSection systemSection = mSettings.getSection(Settings.SECTION_SYSTEM); 145 SettingSection systemSection = mSettings.getSection(Settings.SECTION_SYSTEM);
185 Setting region = systemSection.getSetting(SettingsFile.KEY_REGION_VALUE); 146 Setting dockedMode = systemSection.getSetting(SettingsFile.KEY_USE_DOCKED_MODE);
186 Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE); 147 Setting region = systemSection.getSetting(SettingsFile.KEY_REGION_INDEX);
187 Setting systemClock = systemSection.getSetting(SettingsFile.KEY_INIT_CLOCK); 148 Setting language = systemSection.getSetting(SettingsFile.KEY_LANGUAGE_INDEX);
188 Setting dateTime = systemSection.getSetting(SettingsFile.KEY_INIT_TIME);
189
190 sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_VALUE, Settings.SECTION_SYSTEM, R.string.emulated_region, 0, R.array.regionNames, R.array.regionValues, -1, region));
191 sl.add(new SingleChoiceSetting(SettingsFile.KEY_LANGUAGE, Settings.SECTION_SYSTEM, R.string.emulated_language, 0, R.array.languageNames, R.array.languageValues, 1, language));
192 sl.add(new SingleChoiceSetting(SettingsFile.KEY_INIT_CLOCK, Settings.SECTION_SYSTEM, R.string.init_clock, R.string.init_clock_description, R.array.systemClockNames, R.array.systemClockValues, 0, systemClock));
193 sl.add(new DateTimeSetting(SettingsFile.KEY_INIT_TIME, Settings.SECTION_SYSTEM, R.string.init_time, R.string.init_time_description, "2000-01-01 00:00:01", dateTime));
194 }
195 149
196 private void addInputSettings(ArrayList<SettingsItem> sl) { 150 sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_DOCKED_MODE, Settings.SECTION_SYSTEM, R.string.use_docked_mode, R.string.use_docked_mode_description, true, dockedMode));
197 mView.getActivity().setTitle(R.string.preferences_controls); 151 sl.add(new SingleChoiceSetting(SettingsFile.KEY_REGION_INDEX, Settings.SECTION_SYSTEM, R.string.emulated_region, 0, R.array.regionNames, R.array.regionValues, -1, region));
198 152 sl.add(new SingleChoiceSetting(SettingsFile.KEY_LANGUAGE_INDEX, Settings.SECTION_SYSTEM, R.string.emulated_language, 0, R.array.languageNames, R.array.languageValues, 1, language));
199 SettingSection controlsSection = mSettings.getSection(Settings.SECTION_CONTROLS);
200 Setting buttonA = controlsSection.getSetting(SettingsFile.KEY_BUTTON_A);
201 Setting buttonB = controlsSection.getSetting(SettingsFile.KEY_BUTTON_B);
202 Setting buttonX = controlsSection.getSetting(SettingsFile.KEY_BUTTON_X);
203 Setting buttonY = controlsSection.getSetting(SettingsFile.KEY_BUTTON_Y);
204 Setting buttonSelect = controlsSection.getSetting(SettingsFile.KEY_BUTTON_SELECT);
205 Setting buttonStart = controlsSection.getSetting(SettingsFile.KEY_BUTTON_START);
206 Setting circlepadAxisVert = controlsSection.getSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_VERTICAL);
207 Setting circlepadAxisHoriz = controlsSection.getSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL);
208 Setting cstickAxisVert = controlsSection.getSetting(SettingsFile.KEY_CSTICK_AXIS_VERTICAL);
209 Setting cstickAxisHoriz = controlsSection.getSetting(SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL);
210 Setting dpadAxisVert = controlsSection.getSetting(SettingsFile.KEY_DPAD_AXIS_VERTICAL);
211 Setting dpadAxisHoriz = controlsSection.getSetting(SettingsFile.KEY_DPAD_AXIS_HORIZONTAL);
212 // Setting buttonUp = controlsSection.getSetting(SettingsFile.KEY_BUTTON_UP);
213 // Setting buttonDown = controlsSection.getSetting(SettingsFile.KEY_BUTTON_DOWN);
214 // Setting buttonLeft = controlsSection.getSetting(SettingsFile.KEY_BUTTON_LEFT);
215 // Setting buttonRight = controlsSection.getSetting(SettingsFile.KEY_BUTTON_RIGHT);
216 Setting buttonL = controlsSection.getSetting(SettingsFile.KEY_BUTTON_L);
217 Setting buttonR = controlsSection.getSetting(SettingsFile.KEY_BUTTON_R);
218 Setting buttonZL = controlsSection.getSetting(SettingsFile.KEY_BUTTON_ZL);
219 Setting buttonZR = controlsSection.getSetting(SettingsFile.KEY_BUTTON_ZR);
220
221 sl.add(new HeaderSetting(null, null, R.string.generic_buttons, 0));
222 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_A, Settings.SECTION_CONTROLS, R.string.button_a, buttonA));
223 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_B, Settings.SECTION_CONTROLS, R.string.button_b, buttonB));
224 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_X, Settings.SECTION_CONTROLS, R.string.button_x, buttonX));
225 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_Y, Settings.SECTION_CONTROLS, R.string.button_y, buttonY));
226 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_SELECT, Settings.SECTION_CONTROLS, R.string.button_select, buttonSelect));
227 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_START, Settings.SECTION_CONTROLS, R.string.button_start, buttonStart));
228
229 sl.add(new HeaderSetting(null, null, R.string.controller_circlepad, 0));
230 sl.add(new InputBindingSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_VERTICAL, Settings.SECTION_CONTROLS, R.string.controller_axis_vertical, circlepadAxisVert));
231 sl.add(new InputBindingSetting(SettingsFile.KEY_CIRCLEPAD_AXIS_HORIZONTAL, Settings.SECTION_CONTROLS, R.string.controller_axis_horizontal, circlepadAxisHoriz));
232
233 sl.add(new HeaderSetting(null, null, R.string.controller_c, 0));
234 sl.add(new InputBindingSetting(SettingsFile.KEY_CSTICK_AXIS_VERTICAL, Settings.SECTION_CONTROLS, R.string.controller_axis_vertical, cstickAxisVert));
235 sl.add(new InputBindingSetting(SettingsFile.KEY_CSTICK_AXIS_HORIZONTAL, Settings.SECTION_CONTROLS, R.string.controller_axis_horizontal, cstickAxisHoriz));
236
237 sl.add(new HeaderSetting(null, null, R.string.controller_dpad, 0));
238 sl.add(new InputBindingSetting(SettingsFile.KEY_DPAD_AXIS_VERTICAL, Settings.SECTION_CONTROLS, R.string.controller_axis_vertical, dpadAxisVert));
239 sl.add(new InputBindingSetting(SettingsFile.KEY_DPAD_AXIS_HORIZONTAL, Settings.SECTION_CONTROLS, R.string.controller_axis_horizontal, dpadAxisHoriz));
240
241 // TODO(bunnei): Figure out what to do with these. Configuring is functional, but removing for MVP because they are confusing.
242 // sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_UP, Settings.SECTION_CONTROLS, R.string.generic_up, buttonUp));
243 // sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_DOWN, Settings.SECTION_CONTROLS, R.string.generic_down, buttonDown));
244 // sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_LEFT, Settings.SECTION_CONTROLS, R.string.generic_left, buttonLeft));
245 // sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_RIGHT, Settings.SECTION_CONTROLS, R.string.generic_right, buttonRight));
246
247 sl.add(new HeaderSetting(null, null, R.string.controller_triggers, 0));
248 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_L, Settings.SECTION_CONTROLS, R.string.button_l, buttonL));
249 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_R, Settings.SECTION_CONTROLS, R.string.button_r, buttonR));
250 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_ZL, Settings.SECTION_CONTROLS, R.string.button_zl, buttonZL));
251 sl.add(new InputBindingSetting(SettingsFile.KEY_BUTTON_ZR, Settings.SECTION_CONTROLS, R.string.button_zr, buttonZR));
252 } 153 }
253 154
254 private void addGraphicsSettings(ArrayList<SettingsItem> sl) { 155 private void addGraphicsSettings(ArrayList<SettingsItem> sl) {
255 mView.getActivity().setTitle(R.string.preferences_graphics); 156 mView.getActivity().setTitle(R.string.preferences_graphics);
256 157
257 SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER); 158 SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
258 Setting resolutionFactor = rendererSection.getSetting(SettingsFile.KEY_RESOLUTION_FACTOR); 159 Setting rendererBackend = rendererSection.getSetting(SettingsFile.KEY_RENDERER_BACKEND);
259 Setting filterMode = rendererSection.getSetting(SettingsFile.KEY_FILTER_MODE); 160 Setting rendererAccuracy = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ACCURACY);
260 Setting shadersAccurateMul = rendererSection.getSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL); 161 Setting rendererReolution = rendererSection.getSetting(SettingsFile.KEY_RENDERER_RESOLUTION);
261 Setting render3dMode = rendererSection.getSetting(SettingsFile.KEY_RENDER_3D); 162 Setting rendererAsynchronousShaders = rendererSection.getSetting(SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS);
262 Setting factor3d = rendererSection.getSetting(SettingsFile.KEY_FACTOR_3D); 163
263 Setting useDiskShaderCache = rendererSection.getSetting(SettingsFile.KEY_USE_DISK_SHADER_CACHE); 164 sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDERER_BACKEND, Settings.SECTION_RENDERER, R.string.renderer_api, 0, R.array.rendererApiNames, R.array.rendererApiValues, 0, rendererBackend));
264 SettingSection layoutSection = mSettings.getSection(Settings.SECTION_LAYOUT); 165 sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDERER_ACCURACY, Settings.SECTION_RENDERER, R.string.renderer_accuracy, 0, R.array.rendererAccuracyNames, R.array.rendererAccuracyValues, 1, rendererAccuracy));
265 Setting cardboardScreenSize = layoutSection.getSetting(SettingsFile.KEY_CARDBOARD_SCREEN_SIZE); 166 sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDERER_RESOLUTION, Settings.SECTION_RENDERER, R.string.renderer_resolution, 0, R.array.rendererResolutionNames, R.array.rendererResolutionValues, 2, rendererReolution));
266 Setting cardboardXShift = layoutSection.getSetting(SettingsFile.KEY_CARDBOARD_X_SHIFT); 167 sl.add(new CheckBoxSetting(SettingsFile.KEY_RENDERER_ASYNCHRONOUS_SHADERS, Settings.SECTION_RENDERER, R.string.renderer_asynchronous_shaders, R.string.renderer_asynchronous_shaders_description, false, rendererAsynchronousShaders));
267 Setting cardboardYShift = layoutSection.getSetting(SettingsFile.KEY_CARDBOARD_Y_SHIFT);
268 SettingSection utilitySection = mSettings.getSection(Settings.SECTION_UTILITY);
269 Setting dumpTextures = utilitySection.getSetting(SettingsFile.KEY_DUMP_TEXTURES);
270 Setting customTextures = utilitySection.getSetting(SettingsFile.KEY_CUSTOM_TEXTURES);
271 //Setting preloadTextures = utilitySection.getSetting(SettingsFile.KEY_PRELOAD_TEXTURES);
272
273 sl.add(new HeaderSetting(null, null, R.string.renderer, 0));
274 sl.add(new SliderSetting(SettingsFile.KEY_RESOLUTION_FACTOR, Settings.SECTION_RENDERER, R.string.internal_resolution, R.string.internal_resolution_description, 1, 4, "x", 1, resolutionFactor));
275 sl.add(new CheckBoxSetting(SettingsFile.KEY_FILTER_MODE, Settings.SECTION_RENDERER, R.string.linear_filtering, R.string.linear_filtering_description, true, filterMode));
276 sl.add(new CheckBoxSetting(SettingsFile.KEY_SHADERS_ACCURATE_MUL, Settings.SECTION_RENDERER, R.string.shaders_accurate_mul, R.string.shaders_accurate_mul_description, false, shadersAccurateMul));
277 sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_DISK_SHADER_CACHE, Settings.SECTION_RENDERER, R.string.use_disk_shader_cache, R.string.use_disk_shader_cache_description, true, useDiskShaderCache));
278
279 sl.add(new HeaderSetting(null, null, R.string.stereoscopy, 0));
280 sl.add(new SingleChoiceSetting(SettingsFile.KEY_RENDER_3D, Settings.SECTION_RENDERER, R.string.render3d, 0, R.array.render3dModes, R.array.render3dValues, 0, render3dMode));
281 sl.add(new SliderSetting(SettingsFile.KEY_FACTOR_3D, Settings.SECTION_RENDERER, R.string.factor3d, R.string.factor3d_description, 0, 100, "%", 0, factor3d));
282
283 sl.add(new HeaderSetting(null, null, R.string.cardboard_vr, 0));
284 sl.add(new SliderSetting(SettingsFile.KEY_CARDBOARD_SCREEN_SIZE, Settings.SECTION_LAYOUT, R.string.cardboard_screen_size, R.string.cardboard_screen_size_description, 30, 100, "%", 85, cardboardScreenSize));
285 sl.add(new SliderSetting(SettingsFile.KEY_CARDBOARD_X_SHIFT, Settings.SECTION_LAYOUT, R.string.cardboard_x_shift, R.string.cardboard_x_shift_description, -100, 100, "%", 0, cardboardXShift));
286 sl.add(new SliderSetting(SettingsFile.KEY_CARDBOARD_Y_SHIFT, Settings.SECTION_LAYOUT, R.string.cardboard_y_shift, R.string.cardboard_y_shift_description, -100, 100, "%", 0, cardboardYShift));
287
288 sl.add(new HeaderSetting(null, null, R.string.utility, 0));
289 sl.add(new CheckBoxSetting(SettingsFile.KEY_DUMP_TEXTURES, Settings.SECTION_UTILITY, R.string.dump_textures, R.string.dump_textures_description, false, dumpTextures));
290 sl.add(new CheckBoxSetting(SettingsFile.KEY_CUSTOM_TEXTURES, Settings.SECTION_UTILITY, R.string.custom_textures, R.string.custom_textures_description, false, customTextures));
291 //Disabled until custom texture implementation gets rewrite, current one overloads RAM and crashes yuzu.
292 //sl.add(new CheckBoxSetting(SettingsFile.KEY_PRELOAD_TEXTURES, Settings.SECTION_UTILITY, R.string.preload_textures, R.string.preload_textures_description, false, preloadTextures));
293 } 168 }
294 169
295 private void addAudioSettings(ArrayList<SettingsItem> sl) { 170 private void addAudioSettings(ArrayList<SettingsItem> sl) {
296 mView.getActivity().setTitle(R.string.preferences_audio); 171 mView.getActivity().setTitle(R.string.preferences_audio);
297 172
298 SettingSection audioSection = mSettings.getSection(Settings.SECTION_AUDIO); 173 SettingSection audioSection = mSettings.getSection(Settings.SECTION_AUDIO);
299 Setting audioStretch = audioSection.getSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING); 174 Setting audioVolume = audioSection.getSetting(SettingsFile.KEY_AUDIO_VOLUME);
300 Setting micInputType = audioSection.getSetting(SettingsFile.KEY_MIC_INPUT_TYPE);
301 175
302 sl.add(new CheckBoxSetting(SettingsFile.KEY_ENABLE_AUDIO_STRETCHING, Settings.SECTION_AUDIO, R.string.audio_stretch, R.string.audio_stretch_description, true, audioStretch)); 176 sl.add(new SliderSetting(SettingsFile.KEY_AUDIO_VOLUME, Settings.SECTION_AUDIO, R.string.audio_volume, R.string.audio_volume_description, 0, 100, "%", 100, audioVolume));
303 sl.add(new SingleChoiceSetting(SettingsFile.KEY_MIC_INPUT_TYPE, Settings.SECTION_AUDIO, R.string.audio_input_type, 0, R.array.audioInputTypeNames, R.array.audioInputTypeValues, 1, micInputType));
304 }
305
306 private void addDebugSettings(ArrayList<SettingsItem> sl) {
307 mView.getActivity().setTitle(R.string.preferences_debug);
308
309 SettingSection coreSection = mSettings.getSection(Settings.SECTION_CORE);
310 SettingSection rendererSection = mSettings.getSection(Settings.SECTION_RENDERER);
311 Setting useCpuJit = coreSection.getSetting(SettingsFile.KEY_CPU_JIT);
312 Setting hardwareRenderer = rendererSection.getSetting(SettingsFile.KEY_HW_RENDERER);
313 Setting hardwareShader = rendererSection.getSetting(SettingsFile.KEY_HW_SHADER);
314 Setting vsyncEnable = rendererSection.getSetting(SettingsFile.KEY_USE_VSYNC);
315
316 sl.add(new HeaderSetting(null, null, R.string.debug_warning, 0));
317 sl.add(new CheckBoxSetting(SettingsFile.KEY_CPU_JIT, Settings.SECTION_CORE, R.string.cpu_jit, R.string.cpu_jit_description, true, useCpuJit, true, mView));
318 sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_RENDERER, Settings.SECTION_RENDERER, R.string.hw_renderer, R.string.hw_renderer_description, true, hardwareRenderer, true, mView));
319 sl.add(new CheckBoxSetting(SettingsFile.KEY_HW_SHADER, Settings.SECTION_RENDERER, R.string.hw_shaders, R.string.hw_shaders_description, true, hardwareShader, true, mView));
320 sl.add(new CheckBoxSetting(SettingsFile.KEY_USE_VSYNC, Settings.SECTION_RENDERER, R.string.vsync, R.string.vsync_description, true, vsyncEnable));
321 } 177 }
322} 178}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java
deleted file mode 100644
index 6f8bef7d7..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/InputBindingSettingViewHolder.java
+++ /dev/null
@@ -1,55 +0,0 @@
1package org.yuzu.yuzu_emu.features.settings.ui.viewholder;
2
3import android.content.Context;
4import android.content.SharedPreferences;
5import android.preference.PreferenceManager;
6import android.view.View;
7import android.widget.TextView;
8
9import org.yuzu.yuzu_emu.R;
10import org.yuzu.yuzu_emu.features.settings.model.view.InputBindingSetting;
11import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
12import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter;
13
14public final class InputBindingSettingViewHolder extends SettingViewHolder {
15 private InputBindingSetting mItem;
16
17 private TextView mTextSettingName;
18 private TextView mTextSettingDescription;
19
20 private Context mContext;
21
22 public InputBindingSettingViewHolder(View itemView, SettingsAdapter adapter, Context context) {
23 super(itemView, adapter);
24
25 mContext = context;
26 }
27
28 @Override
29 protected void findViews(View root) {
30 mTextSettingName = root.findViewById(R.id.text_setting_name);
31 mTextSettingDescription = root.findViewById(R.id.text_setting_description);
32 }
33
34 @Override
35 public void bind(SettingsItem item) {
36 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
37
38 mItem = (InputBindingSetting) item;
39
40 mTextSettingName.setText(item.getNameId());
41
42 String key = sharedPreferences.getString(mItem.getKey(), "");
43 if (key != null && !key.isEmpty()) {
44 mTextSettingDescription.setText(key);
45 mTextSettingDescription.setVisibility(View.VISIBLE);
46 } else {
47 mTextSettingDescription.setVisibility(View.GONE);
48 }
49 }
50
51 @Override
52 public void onClick(View clicked) {
53 getAdapter().onInputBindingClick(mItem, getAdapterPosition());
54 }
55}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java
deleted file mode 100644
index 1f862b281..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/PremiumViewHolder.java
+++ /dev/null
@@ -1,57 +0,0 @@
1package org.yuzu.yuzu_emu.features.settings.ui.viewholder;
2
3import android.view.View;
4import android.widget.TextView;
5
6import org.yuzu.yuzu_emu.R;
7import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
8import org.yuzu.yuzu_emu.features.settings.ui.SettingsAdapter;
9import org.yuzu.yuzu_emu.features.settings.ui.SettingsFragmentView;
10import org.yuzu.yuzu_emu.ui.main.MainActivity;
11
12public final class PremiumViewHolder extends SettingViewHolder {
13 private TextView mHeaderName;
14 private TextView mTextDescription;
15 private SettingsFragmentView mView;
16
17 public PremiumViewHolder(View itemView, SettingsAdapter adapter, SettingsFragmentView view) {
18 super(itemView, adapter);
19 mView = view;
20 itemView.setOnClickListener(this);
21 }
22
23 @Override
24 protected void findViews(View root) {
25 mHeaderName = root.findViewById(R.id.text_setting_name);
26 mTextDescription = root.findViewById(R.id.text_setting_description);
27 }
28
29 @Override
30 public void bind(SettingsItem item) {
31 updateText();
32 }
33
34 @Override
35 public void onClick(View clicked) {
36 if (MainActivity.isPremiumActive()) {
37 return;
38 }
39
40 // Invoke billing flow if Premium is not already active, then refresh the UI to indicate
41 // the purchase has completed.
42 MainActivity.invokePremiumBilling(() -> updateText());
43 }
44
45 /**
46 * Update the text shown to the user, based on whether Premium is active
47 */
48 private void updateText() {
49 if (MainActivity.isPremiumActive()) {
50 mHeaderName.setText(R.string.premium_settings_welcome);
51 mTextDescription.setText(R.string.premium_settings_welcome_description);
52 } else {
53 mHeaderName.setText(R.string.premium_settings_upsell);
54 mTextDescription.setText(R.string.premium_settings_upsell_description);
55 }
56 }
57}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java
index e3766f55e..539710395 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/viewholder/SingleChoiceViewHolder.java
@@ -5,7 +5,6 @@ import android.view.View;
5import android.widget.TextView; 5import android.widget.TextView;
6 6
7import org.yuzu.yuzu_emu.R; 7import org.yuzu.yuzu_emu.R;
8import org.yuzu.yuzu_emu.features.settings.model.view.PremiumSingleChoiceSetting;
9import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem; 8import org.yuzu.yuzu_emu.features.settings.model.view.SettingsItem;
10import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting; 9import org.yuzu.yuzu_emu.features.settings.model.view.SingleChoiceSetting;
11import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting; 10import org.yuzu.yuzu_emu.features.settings.model.view.StringSingleChoiceSetting;
@@ -46,17 +45,6 @@ public final class SingleChoiceViewHolder extends SettingViewHolder {
46 mTextSettingDescription.setText(choices[i]); 45 mTextSettingDescription.setText(choices[i]);
47 } 46 }
48 } 47 }
49 } else if (item instanceof PremiumSingleChoiceSetting) {
50 PremiumSingleChoiceSetting setting = (PremiumSingleChoiceSetting) item;
51 int selected = setting.getSelectedValue();
52 Resources resMgr = mTextSettingDescription.getContext().getResources();
53 String[] choices = resMgr.getStringArray(setting.getChoicesId());
54 int[] values = resMgr.getIntArray(setting.getValuesId());
55 for (int i = 0; i < values.length; ++i) {
56 if (values[i] == selected) {
57 mTextSettingDescription.setText(choices[i]);
58 }
59 }
60 } else { 48 } else {
61 mTextSettingDescription.setVisibility(View.GONE); 49 mTextSettingDescription.setVisibility(View.GONE);
62 } 50 }
@@ -67,8 +55,6 @@ public final class SingleChoiceViewHolder extends SettingViewHolder {
67 int position = getAdapterPosition(); 55 int position = getAdapterPosition();
68 if (mItem instanceof SingleChoiceSetting) { 56 if (mItem instanceof SingleChoiceSetting) {
69 getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem, position); 57 getAdapter().onSingleChoiceClick((SingleChoiceSetting) mItem, position);
70 } else if (mItem instanceof PremiumSingleChoiceSetting) {
71 getAdapter().onSingleChoiceClick((PremiumSingleChoiceSetting) mItem, position);
72 } else if (mItem instanceof StringSingleChoiceSetting) { 58 } else if (mItem instanceof StringSingleChoiceSetting) {
73 getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem, position); 59 getAdapter().onStringSingleChoiceClick((StringSingleChoiceSetting) mItem, position);
74 } 60 }
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java
index 9e58dedc2..6526f9139 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/utils/SettingsFile.java
@@ -33,95 +33,23 @@ import java.util.TreeSet;
33public final class SettingsFile { 33public final class SettingsFile {
34 public static final String FILE_NAME_CONFIG = "config"; 34 public static final String FILE_NAME_CONFIG = "config";
35 35
36 public static final String KEY_CPU_JIT = "use_cpu_jit";
37
38 public static final String KEY_DESIGN = "design"; 36 public static final String KEY_DESIGN = "design";
39 37
40 public static final String KEY_PREMIUM = "premium"; 38 // CPU
41 39 public static final String KEY_CPU_ACCURACY = "cpu_accuracy";
42 public static final String KEY_HW_RENDERER = "use_hw_renderer"; 40 // System
43 public static final String KEY_HW_SHADER = "use_hw_shader"; 41 public static final String KEY_USE_DOCKED_MODE = "use_docked_mode";
44 public static final String KEY_SHADERS_ACCURATE_MUL = "shaders_accurate_mul"; 42 public static final String KEY_REGION_INDEX = "region_index";
45 public static final String KEY_USE_SHADER_JIT = "use_shader_jit"; 43 public static final String KEY_LANGUAGE_INDEX = "language_index";
46 public static final String KEY_USE_DISK_SHADER_CACHE = "use_disk_shader_cache"; 44 public static final String KEY_RENDERER_BACKEND = "backend";
47 public static final String KEY_USE_VSYNC = "use_vsync_new"; 45 // Renderer
48 public static final String KEY_RESOLUTION_FACTOR = "resolution_factor"; 46 public static final String KEY_RENDERER_RESOLUTION = "resolution_setup";
49 public static final String KEY_FRAME_LIMIT_ENABLED = "use_frame_limit"; 47 public static final String KEY_RENDERER_ACCURACY = "gpu_accuracy";
50 public static final String KEY_FRAME_LIMIT = "frame_limit"; 48 public static final String KEY_RENDERER_ASYNCHRONOUS_SHADERS = "use_asynchronous_shaders";
51 public static final String KEY_BACKGROUND_RED = "bg_red"; 49 public static final String KEY_RENDERER_USE_SPEED_LIMIT = "use_speed_limit";
52 public static final String KEY_BACKGROUND_BLUE = "bg_blue"; 50 public static final String KEY_RENDERER_SPEED_LIMIT = "speed_limit";
53 public static final String KEY_BACKGROUND_GREEN = "bg_green"; 51 // Audio
54 public static final String KEY_RENDER_3D = "render_3d"; 52 public static final String KEY_AUDIO_VOLUME = "volume";
55 public static final String KEY_FACTOR_3D = "factor_3d";
56 public static final String KEY_PP_SHADER_NAME = "pp_shader_name";
57 public static final String KEY_FILTER_MODE = "filter_mode";
58 public static final String KEY_TEXTURE_FILTER_NAME = "texture_filter_name";
59 public static final String KEY_USE_ASYNCHRONOUS_GPU_EMULATION = "use_asynchronous_gpu_emulation";
60
61 public static final String KEY_LAYOUT_OPTION = "layout_option";
62 public static final String KEY_SWAP_SCREEN = "swap_screen";
63 public static final String KEY_CARDBOARD_SCREEN_SIZE = "cardboard_screen_size";
64 public static final String KEY_CARDBOARD_X_SHIFT = "cardboard_x_shift";
65 public static final String KEY_CARDBOARD_Y_SHIFT = "cardboard_y_shift";
66
67 public static final String KEY_DUMP_TEXTURES = "dump_textures";
68 public static final String KEY_CUSTOM_TEXTURES = "custom_textures";
69 public static final String KEY_PRELOAD_TEXTURES = "preload_textures";
70
71 public static final String KEY_AUDIO_OUTPUT_ENGINE = "output_engine";
72 public static final String KEY_ENABLE_AUDIO_STRETCHING = "enable_audio_stretching";
73 public static final String KEY_VOLUME = "volume";
74 public static final String KEY_MIC_INPUT_TYPE = "mic_input_type";
75
76 public static final String KEY_USE_VIRTUAL_SD = "use_virtual_sd";
77
78 public static final String KEY_IS_NEW_3DS = "is_new_3ds";
79 public static final String KEY_REGION_VALUE = "region_value";
80 public static final String KEY_LANGUAGE = "language";
81
82 public static final String KEY_INIT_CLOCK = "init_clock";
83 public static final String KEY_INIT_TIME = "init_time";
84
85 public static final String KEY_BUTTON_A = "button_a";
86 public static final String KEY_BUTTON_B = "button_b";
87 public static final String KEY_BUTTON_X = "button_x";
88 public static final String KEY_BUTTON_Y = "button_y";
89 public static final String KEY_BUTTON_SELECT = "button_select";
90 public static final String KEY_BUTTON_START = "button_start";
91 public static final String KEY_BUTTON_UP = "button_up";
92 public static final String KEY_BUTTON_DOWN = "button_down";
93 public static final String KEY_BUTTON_LEFT = "button_left";
94 public static final String KEY_BUTTON_RIGHT = "button_right";
95 public static final String KEY_BUTTON_L = "button_l";
96 public static final String KEY_BUTTON_R = "button_r";
97 public static final String KEY_BUTTON_ZL = "button_zl";
98 public static final String KEY_BUTTON_ZR = "button_zr";
99 public static final String KEY_CIRCLEPAD_AXIS_VERTICAL = "circlepad_axis_vertical";
100 public static final String KEY_CIRCLEPAD_AXIS_HORIZONTAL = "circlepad_axis_horizontal";
101 public static final String KEY_CSTICK_AXIS_VERTICAL = "cstick_axis_vertical";
102 public static final String KEY_CSTICK_AXIS_HORIZONTAL = "cstick_axis_horizontal";
103 public static final String KEY_DPAD_AXIS_VERTICAL = "dpad_axis_vertical";
104 public static final String KEY_DPAD_AXIS_HORIZONTAL = "dpad_axis_horizontal";
105 public static final String KEY_CIRCLEPAD_UP = "circlepad_up";
106 public static final String KEY_CIRCLEPAD_DOWN = "circlepad_down";
107 public static final String KEY_CIRCLEPAD_LEFT = "circlepad_left";
108 public static final String KEY_CIRCLEPAD_RIGHT = "circlepad_right";
109 public static final String KEY_CSTICK_UP = "cstick_up";
110 public static final String KEY_CSTICK_DOWN = "cstick_down";
111 public static final String KEY_CSTICK_LEFT = "cstick_left";
112 public static final String KEY_CSTICK_RIGHT = "cstick_right";
113
114 public static final String KEY_CAMERA_OUTER_RIGHT_NAME = "camera_outer_right_name";
115 public static final String KEY_CAMERA_OUTER_RIGHT_CONFIG = "camera_outer_right_config";
116 public static final String KEY_CAMERA_OUTER_RIGHT_FLIP = "camera_outer_right_flip";
117 public static final String KEY_CAMERA_OUTER_LEFT_NAME = "camera_outer_left_name";
118 public static final String KEY_CAMERA_OUTER_LEFT_CONFIG = "camera_outer_left_config";
119 public static final String KEY_CAMERA_OUTER_LEFT_FLIP = "camera_outer_left_flip";
120 public static final String KEY_CAMERA_INNER_NAME = "camera_inner_name";
121 public static final String KEY_CAMERA_INNER_CONFIG = "camera_inner_config";
122 public static final String KEY_CAMERA_INNER_FLIP = "camera_inner_flip";
123
124 public static final String KEY_LOG_FILTER = "log_filter";
125 53
126 private static BiMap<String, String> sectionsMap = new BiMap<>(); 54 private static BiMap<String, String> sectionsMap = new BiMap<>();
127 55
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java
index b2083f858..f7a242171 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.java
@@ -29,7 +29,6 @@ import org.yuzu.yuzu_emu.overlay.InputOverlay;
29import org.yuzu.yuzu_emu.utils.DirectoryInitialization; 29import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
30import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState; 30import org.yuzu.yuzu_emu.utils.DirectoryInitialization.DirectoryInitializationState;
31import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver; 31import org.yuzu.yuzu_emu.utils.DirectoryStateReceiver;
32import org.yuzu.yuzu_emu.utils.EmulationMenuSettings;
33import org.yuzu.yuzu_emu.utils.Log; 32import org.yuzu.yuzu_emu.utils.Log;
34 33
35public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback { 34public final class EmulationFragment extends Fragment implements SurfaceHolder.Callback, Choreographer.FrameCallback {
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
index 6558a05c9..d419750a3 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainActivity.java
@@ -18,7 +18,6 @@ import org.yuzu.yuzu_emu.features.settings.ui.SettingsActivity;
18import org.yuzu.yuzu_emu.model.GameProvider; 18import org.yuzu.yuzu_emu.model.GameProvider;
19import org.yuzu.yuzu_emu.ui.platform.PlatformGamesFragment; 19import org.yuzu.yuzu_emu.ui.platform.PlatformGamesFragment;
20import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; 20import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
21import org.yuzu.yuzu_emu.utils.BillingManager;
22import org.yuzu.yuzu_emu.utils.DirectoryInitialization; 21import org.yuzu.yuzu_emu.utils.DirectoryInitialization;
23import org.yuzu.yuzu_emu.utils.FileBrowserHelper; 22import org.yuzu.yuzu_emu.utils.FileBrowserHelper;
24import org.yuzu.yuzu_emu.utils.PermissionsHandler; 23import org.yuzu.yuzu_emu.utils.PermissionsHandler;
@@ -40,11 +39,6 @@ public final class MainActivity extends AppCompatActivity implements MainView {
40 39
41 private MainPresenter mPresenter = new MainPresenter(this); 40 private MainPresenter mPresenter = new MainPresenter(this);
42 41
43 // Singleton to manage user billing state
44 private static BillingManager mBillingManager;
45
46 private static MenuItem mPremiumButton;
47
48 @Override 42 @Override
49 protected void onCreate(Bundle savedInstanceState) { 43 protected void onCreate(Bundle savedInstanceState) {
50 ThemeUtil.applyTheme(); 44 ThemeUtil.applyTheme();
@@ -71,9 +65,6 @@ public final class MainActivity extends AppCompatActivity implements MainView {
71 } 65 }
72 PicassoUtils.init(); 66 PicassoUtils.init();
73 67
74 // Setup billing manager, so we can globally query for Premium status
75 mBillingManager = new BillingManager(this);
76
77 // Dismiss previous notifications (should not happen unless a crash occurred) 68 // Dismiss previous notifications (should not happen unless a crash occurred)
78 EmulationActivity.tryDismissRunningNotification(this); 69 EmulationActivity.tryDismissRunningNotification(this);
79 } 70 }
@@ -107,22 +98,10 @@ public final class MainActivity extends AppCompatActivity implements MainView {
107 public boolean onCreateOptionsMenu(Menu menu) { 98 public boolean onCreateOptionsMenu(Menu menu) {
108 MenuInflater inflater = getMenuInflater(); 99 MenuInflater inflater = getMenuInflater();
109 inflater.inflate(R.menu.menu_game_grid, menu); 100 inflater.inflate(R.menu.menu_game_grid, menu);
110 mPremiumButton = menu.findItem(R.id.button_premium);
111
112 if (mBillingManager.isPremiumCached()) {
113 // User had premium in a previous session, hide upsell option
114 setPremiumButtonVisible(false);
115 }
116 101
117 return true; 102 return true;
118 } 103 }
119 104
120 static public void setPremiumButtonVisible(boolean isVisible) {
121 if (mPremiumButton != null) {
122 mPremiumButton.setVisible(isVisible);
123 }
124 }
125
126 /** 105 /**
127 * MainView 106 * MainView
128 */ 107 */
@@ -155,15 +134,8 @@ public final class MainActivity extends AppCompatActivity implements MainView {
155 FileBrowserHelper.openDirectoryPicker(this, 134 FileBrowserHelper.openDirectoryPicker(this,
156 MainPresenter.REQUEST_ADD_DIRECTORY, 135 MainPresenter.REQUEST_ADD_DIRECTORY,
157 R.string.select_game_folder, 136 R.string.select_game_folder,
158 Arrays.asList("xci", "nsp", "cci", "3ds", 137 Arrays.asList("nso", "nro", "nca", "xci",
159 "cxi", "app", "3dsx", "cia", 138 "nsp", "kip"));
160 "rar", "zip", "7z", "torrent",
161 "tar", "gz", "nro"));
162 break;
163 case MainPresenter.REQUEST_INSTALL_CIA:
164 FileBrowserHelper.openFilePicker(this, MainPresenter.REQUEST_INSTALL_CIA,
165 R.string.install_cia_title,
166 Collections.singletonList("cia"), true);
167 break; 139 break;
168 } 140 }
169 } else { 141 } else {
@@ -191,12 +163,6 @@ public final class MainActivity extends AppCompatActivity implements MainView {
191 mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result)); 163 mPresenter.onDirectorySelected(FileBrowserHelper.getSelectedDirectory(result));
192 } 164 }
193 break; 165 break;
194 case MainPresenter.REQUEST_INSTALL_CIA:
195 // If the user picked a file, as opposed to just backing out.
196 if (resultCode == MainActivity.RESULT_OK) {
197 mPresenter.refeshGameList();
198 }
199 break;
200 } 166 }
201 } 167 }
202 168
@@ -248,20 +214,4 @@ public final class MainActivity extends AppCompatActivity implements MainView {
248 EmulationActivity.tryDismissRunningNotification(this); 214 EmulationActivity.tryDismissRunningNotification(this);
249 super.onDestroy(); 215 super.onDestroy();
250 } 216 }
251
252 /**
253 * @return true if Premium subscription is currently active
254 */
255 public static boolean isPremiumActive() {
256 return mBillingManager.isPremiumActive();
257 }
258
259 /**
260 * Invokes the billing flow for Premium
261 *
262 * @param callback Optional callback, called once, on completion of billing
263 */
264 public static void invokePremiumBilling(Runnable callback) {
265 mBillingManager.invokePremiumBilling(callback);
266 }
267} 217}
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
index 2608df2c2..4cf643552 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/ui/main/MainPresenter.java
@@ -5,15 +5,12 @@ import android.os.SystemClock;
5import org.yuzu.yuzu_emu.BuildConfig; 5import org.yuzu.yuzu_emu.BuildConfig;
6import org.yuzu.yuzu_emu.YuzuApplication; 6import org.yuzu.yuzu_emu.YuzuApplication;
7import org.yuzu.yuzu_emu.R; 7import org.yuzu.yuzu_emu.R;
8import org.yuzu.yuzu_emu.features.settings.model.Settings;
9import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile; 8import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
10import org.yuzu.yuzu_emu.model.GameDatabase; 9import org.yuzu.yuzu_emu.model.GameDatabase;
11import org.yuzu.yuzu_emu.utils.AddDirectoryHelper; 10import org.yuzu.yuzu_emu.utils.AddDirectoryHelper;
12 11
13public final class MainPresenter { 12public final class MainPresenter {
14 public static final int REQUEST_ADD_DIRECTORY = 1; 13 public static final int REQUEST_ADD_DIRECTORY = 1;
15 public static final int REQUEST_INSTALL_CIA = 2;
16
17 private final MainView mView; 14 private final MainView mView;
18 private String mDirToAdd; 15 private String mDirToAdd;
19 private long mLastClickTime = 0; 16 private long mLastClickTime = 0;
@@ -49,14 +46,6 @@ public final class MainPresenter {
49 case R.id.button_add_directory: 46 case R.id.button_add_directory:
50 launchFileListActivity(REQUEST_ADD_DIRECTORY); 47 launchFileListActivity(REQUEST_ADD_DIRECTORY);
51 return true; 48 return true;
52
53 case R.id.button_install_cia:
54 launchFileListActivity(REQUEST_INSTALL_CIA);
55 return true;
56
57 case R.id.button_premium:
58 mView.launchSettingsActivity(Settings.SECTION_PREMIUM);
59 return true;
60 } 49 }
61 50
62 return false; 51 return false;
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java
deleted file mode 100644
index 3d6dd1481..000000000
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/BillingManager.java
+++ /dev/null
@@ -1,215 +0,0 @@
1package org.yuzu.yuzu_emu.utils;
2
3import android.app.Activity;
4import android.content.SharedPreferences;
5import android.preference.PreferenceManager;
6import android.widget.Toast;
7
8import com.android.billingclient.api.AcknowledgePurchaseParams;
9import com.android.billingclient.api.AcknowledgePurchaseResponseListener;
10import com.android.billingclient.api.BillingClient;
11import com.android.billingclient.api.BillingClientStateListener;
12import com.android.billingclient.api.BillingFlowParams;
13import com.android.billingclient.api.BillingResult;
14import com.android.billingclient.api.Purchase;
15import com.android.billingclient.api.Purchase.PurchasesResult;
16import com.android.billingclient.api.PurchasesUpdatedListener;
17import com.android.billingclient.api.SkuDetails;
18import com.android.billingclient.api.SkuDetailsParams;
19
20import org.yuzu.yuzu_emu.YuzuApplication;
21import org.yuzu.yuzu_emu.R;
22import org.yuzu.yuzu_emu.features.settings.utils.SettingsFile;
23import org.yuzu.yuzu_emu.ui.main.MainActivity;
24
25import java.util.ArrayList;
26import java.util.List;
27
28public class BillingManager implements PurchasesUpdatedListener {
29 private final String BILLING_SKU_PREMIUM = "yuzu.yuzu_emu.product_id.premium";
30
31 private final Activity mActivity;
32 private BillingClient mBillingClient;
33 private SkuDetails mSkuPremium;
34 private boolean mIsPremiumActive = false;
35 private boolean mIsServiceConnected = false;
36 private Runnable mUpdateBillingCallback;
37
38 private static SharedPreferences mPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.getAppContext());
39
40 public BillingManager(Activity activity) {
41 mActivity = activity;
42 mBillingClient = BillingClient.newBuilder(mActivity).enablePendingPurchases().setListener(this).build();
43 querySkuDetails();
44 }
45
46 static public boolean isPremiumCached() {
47 return mPreferences.getBoolean(SettingsFile.KEY_PREMIUM, false);
48 }
49
50 /**
51 * @return true if Premium subscription is currently active
52 */
53 public boolean isPremiumActive() {
54 return mIsPremiumActive;
55 }
56
57 /**
58 * Invokes the billing flow for Premium
59 *
60 * @param callback Optional callback, called once, on completion of billing
61 */
62 public void invokePremiumBilling(Runnable callback) {
63 if (mSkuPremium == null) {
64 return;
65 }
66
67 // Optional callback to refresh the UI for the caller when billing completes
68 mUpdateBillingCallback = callback;
69
70 // Invoke the billing flow
71 BillingFlowParams flowParams = BillingFlowParams.newBuilder()
72 .setSkuDetails(mSkuPremium)
73 .build();
74 mBillingClient.launchBillingFlow(mActivity, flowParams);
75 }
76
77 private void updatePremiumState(boolean isPremiumActive) {
78 mIsPremiumActive = isPremiumActive;
79
80 // Cache state for synchronous UI
81 SharedPreferences.Editor editor = mPreferences.edit();
82 editor.putBoolean(SettingsFile.KEY_PREMIUM, isPremiumActive);
83 editor.apply();
84
85 // No need to show button in action bar if Premium is active
86 MainActivity.setPremiumButtonVisible(!isPremiumActive);
87 }
88
89 @Override
90 public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchaseList) {
91 if (purchaseList == null || purchaseList.isEmpty()) {
92 // Premium is not active, or billing is unavailable
93 updatePremiumState(false);
94 return;
95 }
96
97 Purchase premiumPurchase = null;
98 for (Purchase purchase : purchaseList) {
99 if (purchase.getSku().equals(BILLING_SKU_PREMIUM)) {
100 premiumPurchase = purchase;
101 }
102 }
103
104 if (premiumPurchase != null && premiumPurchase.getPurchaseState() == Purchase.PurchaseState.PURCHASED) {
105 // Premium has been purchased
106 updatePremiumState(true);
107
108 // Acknowledge the purchase if it hasn't already been acknowledged.
109 if (!premiumPurchase.isAcknowledged()) {
110 AcknowledgePurchaseParams acknowledgePurchaseParams =
111 AcknowledgePurchaseParams.newBuilder()
112 .setPurchaseToken(premiumPurchase.getPurchaseToken())
113 .build();
114
115 AcknowledgePurchaseResponseListener acknowledgePurchaseResponseListener = billingResult1 -> {
116 Toast.makeText(mActivity, R.string.premium_settings_welcome, Toast.LENGTH_SHORT).show();
117 };
118 mBillingClient.acknowledgePurchase(acknowledgePurchaseParams, acknowledgePurchaseResponseListener);
119 }
120
121 if (mUpdateBillingCallback != null) {
122 try {
123 mUpdateBillingCallback.run();
124 } catch (Exception e) {
125 e.printStackTrace();
126 }
127 mUpdateBillingCallback = null;
128 }
129 }
130 }
131
132 private void onQuerySkuDetailsFinished(List<SkuDetails> skuDetailsList) {
133 if (skuDetailsList == null) {
134 // This can happen when no user is signed in
135 return;
136 }
137
138 if (skuDetailsList.isEmpty()) {
139 return;
140 }
141
142 mSkuPremium = skuDetailsList.get(0);
143
144 queryPurchases();
145 }
146
147 private void querySkuDetails() {
148 Runnable queryToExecute = new Runnable() {
149 @Override
150 public void run() {
151 SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
152 List<String> skuList = new ArrayList<>();
153
154 skuList.add(BILLING_SKU_PREMIUM);
155 params.setSkusList(skuList).setType(BillingClient.SkuType.INAPP);
156
157 mBillingClient.querySkuDetailsAsync(params.build(),
158 (billingResult, skuDetailsList) -> onQuerySkuDetailsFinished(skuDetailsList));
159 }
160 };
161
162 executeServiceRequest(queryToExecute);
163 }
164
165 private void onQueryPurchasesFinished(PurchasesResult result) {
166 // Have we been disposed of in the meantime? If so, or bad result code, then quit
167 if (mBillingClient == null || result.getResponseCode() != BillingClient.BillingResponseCode.OK) {
168 updatePremiumState(false);
169 return;
170 }
171 // Update the UI and purchases inventory with new list of purchases
172 onPurchasesUpdated(result.getBillingResult(), result.getPurchasesList());
173 }
174
175 private void queryPurchases() {
176 Runnable queryToExecute = new Runnable() {
177 @Override
178 public void run() {
179 final PurchasesResult purchasesResult = mBillingClient.queryPurchases(BillingClient.SkuType.INAPP);
180 onQueryPurchasesFinished(purchasesResult);
181 }
182 };
183
184 executeServiceRequest(queryToExecute);
185 }
186
187 private void startServiceConnection(final Runnable executeOnFinish) {
188 mBillingClient.startConnection(new BillingClientStateListener() {
189 @Override
190 public void onBillingSetupFinished(BillingResult billingResult) {
191 if (billingResult.getResponseCode() == BillingClient.BillingResponseCode.OK) {
192 mIsServiceConnected = true;
193 }
194
195 if (executeOnFinish != null) {
196 executeOnFinish.run();
197 }
198 }
199
200 @Override
201 public void onBillingServiceDisconnected() {
202 mIsServiceConnected = false;
203 }
204 });
205 }
206
207 private void executeServiceRequest(Runnable runnable) {
208 if (mIsServiceConnected) {
209 runnable.run();
210 } else {
211 // If billing service was disconnected, we try to reconnect 1 time.
212 startServiceConnection(runnable);
213 }
214 }
215}
diff --git a/src/android/app/src/main/jni/CMakeLists.txt b/src/android/app/src/main/jni/CMakeLists.txt
index 373c0e8bd..e5c9d57f2 100644
--- a/src/android/app/src/main/jni/CMakeLists.txt
+++ b/src/android/app/src/main/jni/CMakeLists.txt
@@ -1,4 +1,7 @@
1add_library(yuzu-android SHARED 1add_library(yuzu-android SHARED
2 config.cpp
3 config.h
4 default_ini.h
2 emu_window/emu_window.cpp 5 emu_window/emu_window.cpp
3 emu_window/emu_window.h 6 emu_window/emu_window.h
4 id_cache.cpp 7 id_cache.cpp
diff --git a/src/android/app/src/main/jni/config.cpp b/src/android/app/src/main/jni/config.cpp
new file mode 100644
index 000000000..326dab5fc
--- /dev/null
+++ b/src/android/app/src/main/jni/config.cpp
@@ -0,0 +1,284 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#include <memory>
5#include <optional>
6#include <sstream>
7
8#include <INIReader.h>
9#include "common/fs/file.h"
10#include "common/fs/fs.h"
11#include "common/fs/path_util.h"
12#include "common/logging/log.h"
13#include "common/settings.h"
14#include "core/hle/service/acc/profile_manager.h"
15#include "input_common/main.h"
16#include "jni/config.h"
17#include "jni/default_ini.h"
18
19namespace FS = Common::FS;
20
21const std::filesystem::path default_config_path =
22 FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "config.ini";
23
24Config::Config(std::optional<std::filesystem::path> config_path)
25 : config_loc{config_path.value_or(default_config_path)},
26 config{std::make_unique<INIReader>(FS::PathToUTF8String(config_loc))} {
27 Reload();
28}
29
30Config::~Config() = default;
31
32bool Config::LoadINI(const std::string& default_contents, bool retry) {
33 const auto config_loc_str = FS::PathToUTF8String(config_loc);
34 if (config->ParseError() < 0) {
35 if (retry) {
36 LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...",
37 config_loc_str);
38
39 void(FS::CreateParentDir(config_loc));
40 void(FS::WriteStringToFile(config_loc, FS::FileType::TextFile, default_contents));
41
42 config = std::make_unique<INIReader>(config_loc_str);
43
44 return LoadINI(default_contents, false);
45 }
46 LOG_ERROR(Config, "Failed.");
47 return false;
48 }
49 LOG_INFO(Config, "Successfully loaded {}", config_loc_str);
50 return true;
51}
52
53template <>
54void Config::ReadSetting(const std::string& group, Settings::Setting<std::string>& setting) {
55 std::string setting_value = config->Get(group, setting.GetLabel(), setting.GetDefault());
56 if (setting_value.empty()) {
57 setting_value = setting.GetDefault();
58 }
59 setting = std::move(setting_value);
60}
61
62template <>
63void Config::ReadSetting(const std::string& group, Settings::Setting<bool>& setting) {
64 setting = config->GetBoolean(group, setting.GetLabel(), setting.GetDefault());
65}
66
67template <typename Type, bool ranged>
68void Config::ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting) {
69 setting = static_cast<Type>(config->GetInteger(group, setting.GetLabel(),
70 static_cast<long>(setting.GetDefault())));
71}
72
73void Config::ReadValues() {
74 ReadSetting("ControlsGeneral", Settings::values.mouse_enabled);
75 ReadSetting("ControlsGeneral", Settings::values.touch_device);
76 ReadSetting("ControlsGeneral", Settings::values.keyboard_enabled);
77 ReadSetting("ControlsGeneral", Settings::values.debug_pad_enabled);
78 ReadSetting("ControlsGeneral", Settings::values.vibration_enabled);
79 ReadSetting("ControlsGeneral", Settings::values.enable_accurate_vibrations);
80 ReadSetting("ControlsGeneral", Settings::values.motion_enabled);
81 Settings::values.touchscreen.enabled =
82 config->GetBoolean("ControlsGeneral", "touch_enabled", true);
83 Settings::values.touchscreen.rotation_angle =
84 config->GetInteger("ControlsGeneral", "touch_angle", 0);
85 Settings::values.touchscreen.diameter_x =
86 config->GetInteger("ControlsGeneral", "touch_diameter_x", 15);
87 Settings::values.touchscreen.diameter_y =
88 config->GetInteger("ControlsGeneral", "touch_diameter_y", 15);
89
90 int num_touch_from_button_maps =
91 config->GetInteger("ControlsGeneral", "touch_from_button_map", 0);
92 if (num_touch_from_button_maps > 0) {
93 for (int i = 0; i < num_touch_from_button_maps; ++i) {
94 Settings::TouchFromButtonMap map;
95 map.name = config->Get("ControlsGeneral",
96 std::string("touch_from_button_maps_") + std::to_string(i) +
97 std::string("_name"),
98 "default");
99 const int num_touch_maps = config->GetInteger(
100 "ControlsGeneral",
101 std::string("touch_from_button_maps_") + std::to_string(i) + std::string("_count"),
102 0);
103 map.buttons.reserve(num_touch_maps);
104
105 for (int j = 0; j < num_touch_maps; ++j) {
106 std::string touch_mapping =
107 config->Get("ControlsGeneral",
108 std::string("touch_from_button_maps_") + std::to_string(i) +
109 std::string("_bind_") + std::to_string(j),
110 "");
111 map.buttons.emplace_back(std::move(touch_mapping));
112 }
113
114 Settings::values.touch_from_button_maps.emplace_back(std::move(map));
115 }
116 } else {
117 Settings::values.touch_from_button_maps.emplace_back(
118 Settings::TouchFromButtonMap{"default", {}});
119 num_touch_from_button_maps = 1;
120 }
121 Settings::values.touch_from_button_map_index = std::clamp(
122 Settings::values.touch_from_button_map_index.GetValue(), 0, num_touch_from_button_maps - 1);
123
124 ReadSetting("ControlsGeneral", Settings::values.udp_input_servers);
125
126 // Data Storage
127 ReadSetting("Data Storage", Settings::values.use_virtual_sd);
128 FS::SetYuzuPath(FS::YuzuPath::NANDDir,
129 config->Get("Data Storage", "nand_directory",
130 FS::GetYuzuPathString(FS::YuzuPath::NANDDir)));
131 FS::SetYuzuPath(FS::YuzuPath::SDMCDir,
132 config->Get("Data Storage", "sdmc_directory",
133 FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)));
134 FS::SetYuzuPath(FS::YuzuPath::LoadDir,
135 config->Get("Data Storage", "load_directory",
136 FS::GetYuzuPathString(FS::YuzuPath::LoadDir)));
137 FS::SetYuzuPath(FS::YuzuPath::DumpDir,
138 config->Get("Data Storage", "dump_directory",
139 FS::GetYuzuPathString(FS::YuzuPath::DumpDir)));
140 ReadSetting("Data Storage", Settings::values.gamecard_inserted);
141 ReadSetting("Data Storage", Settings::values.gamecard_current_game);
142 ReadSetting("Data Storage", Settings::values.gamecard_path);
143
144 // System
145 ReadSetting("System", Settings::values.use_docked_mode);
146
147 ReadSetting("System", Settings::values.current_user);
148 Settings::values.current_user = std::clamp<int>(Settings::values.current_user.GetValue(), 0,
149 Service::Account::MAX_USERS - 1);
150
151 const auto rng_seed_enabled = config->GetBoolean("System", "rng_seed_enabled", false);
152 if (rng_seed_enabled) {
153 Settings::values.rng_seed.SetValue(config->GetInteger("System", "rng_seed", 0));
154 } else {
155 Settings::values.rng_seed.SetValue(std::nullopt);
156 }
157
158 const auto custom_rtc_enabled = config->GetBoolean("System", "custom_rtc_enabled", false);
159 if (custom_rtc_enabled) {
160 Settings::values.custom_rtc = config->GetInteger("System", "custom_rtc", 0);
161 } else {
162 Settings::values.custom_rtc = std::nullopt;
163 }
164
165 ReadSetting("System", Settings::values.language_index);
166 ReadSetting("System", Settings::values.region_index);
167 ReadSetting("System", Settings::values.time_zone_index);
168 ReadSetting("System", Settings::values.sound_index);
169
170 // Core
171 ReadSetting("Core", Settings::values.use_multi_core);
172 ReadSetting("Core", Settings::values.use_unsafe_extended_memory_layout);
173
174 // Cpu
175 ReadSetting("Cpu", Settings::values.cpu_accuracy);
176 ReadSetting("Cpu", Settings::values.cpu_debug_mode);
177 ReadSetting("Cpu", Settings::values.cpuopt_page_tables);
178 ReadSetting("Cpu", Settings::values.cpuopt_block_linking);
179 ReadSetting("Cpu", Settings::values.cpuopt_return_stack_buffer);
180 ReadSetting("Cpu", Settings::values.cpuopt_fast_dispatcher);
181 ReadSetting("Cpu", Settings::values.cpuopt_context_elimination);
182 ReadSetting("Cpu", Settings::values.cpuopt_const_prop);
183 ReadSetting("Cpu", Settings::values.cpuopt_misc_ir);
184 ReadSetting("Cpu", Settings::values.cpuopt_reduce_misalign_checks);
185 ReadSetting("Cpu", Settings::values.cpuopt_fastmem);
186 ReadSetting("Cpu", Settings::values.cpuopt_fastmem_exclusives);
187 ReadSetting("Cpu", Settings::values.cpuopt_recompile_exclusives);
188 ReadSetting("Cpu", Settings::values.cpuopt_ignore_memory_aborts);
189 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_unfuse_fma);
190 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_reduce_fp_error);
191 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_standard_fpcr);
192 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_inaccurate_nan);
193 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_fastmem_check);
194 ReadSetting("Cpu", Settings::values.cpuopt_unsafe_ignore_global_monitor);
195
196 // Renderer
197 ReadSetting("Renderer", Settings::values.renderer_backend);
198 ReadSetting("Renderer", Settings::values.renderer_force_max_clock);
199 ReadSetting("Renderer", Settings::values.renderer_debug);
200 ReadSetting("Renderer", Settings::values.renderer_shader_feedback);
201 ReadSetting("Renderer", Settings::values.enable_nsight_aftermath);
202 ReadSetting("Renderer", Settings::values.disable_shader_loop_safety_checks);
203 ReadSetting("Renderer", Settings::values.vulkan_device);
204
205 ReadSetting("Renderer", Settings::values.resolution_setup);
206 ReadSetting("Renderer", Settings::values.scaling_filter);
207 ReadSetting("Renderer", Settings::values.fsr_sharpening_slider);
208 ReadSetting("Renderer", Settings::values.anti_aliasing);
209 ReadSetting("Renderer", Settings::values.fullscreen_mode);
210 ReadSetting("Renderer", Settings::values.aspect_ratio);
211 ReadSetting("Renderer", Settings::values.max_anisotropy);
212 ReadSetting("Renderer", Settings::values.use_speed_limit);
213 ReadSetting("Renderer", Settings::values.speed_limit);
214 ReadSetting("Renderer", Settings::values.use_disk_shader_cache);
215 ReadSetting("Renderer", Settings::values.gpu_accuracy);
216 ReadSetting("Renderer", Settings::values.use_asynchronous_gpu_emulation);
217 ReadSetting("Renderer", Settings::values.vsync_mode);
218 ReadSetting("Renderer", Settings::values.shader_backend);
219 ReadSetting("Renderer", Settings::values.use_asynchronous_shaders);
220 ReadSetting("Renderer", Settings::values.nvdec_emulation);
221 ReadSetting("Renderer", Settings::values.accelerate_astc);
222 ReadSetting("Renderer", Settings::values.use_fast_gpu_time);
223 ReadSetting("Renderer", Settings::values.use_vulkan_driver_pipeline_cache);
224
225 ReadSetting("Renderer", Settings::values.bg_red);
226 ReadSetting("Renderer", Settings::values.bg_green);
227 ReadSetting("Renderer", Settings::values.bg_blue);
228
229 // Audio
230 ReadSetting("Audio", Settings::values.sink_id);
231 ReadSetting("Audio", Settings::values.audio_output_device_id);
232 ReadSetting("Audio", Settings::values.volume);
233
234 // Miscellaneous
235 // log_filter has a different default here than from common
236 Settings::values.log_filter = "*:Info";
237 ReadSetting("Miscellaneous", Settings::values.use_dev_keys);
238
239 // Debugging
240 Settings::values.record_frame_times =
241 config->GetBoolean("Debugging", "record_frame_times", false);
242 ReadSetting("Debugging", Settings::values.dump_exefs);
243 ReadSetting("Debugging", Settings::values.dump_nso);
244 ReadSetting("Debugging", Settings::values.enable_fs_access_log);
245 ReadSetting("Debugging", Settings::values.reporting_services);
246 ReadSetting("Debugging", Settings::values.quest_flag);
247 ReadSetting("Debugging", Settings::values.use_debug_asserts);
248 ReadSetting("Debugging", Settings::values.use_auto_stub);
249 ReadSetting("Debugging", Settings::values.disable_macro_jit);
250 ReadSetting("Debugging", Settings::values.disable_macro_hle);
251 ReadSetting("Debugging", Settings::values.use_gdbstub);
252 ReadSetting("Debugging", Settings::values.gdbstub_port);
253
254 const auto title_list = config->Get("AddOns", "title_ids", "");
255 std::stringstream ss(title_list);
256 std::string line;
257 while (std::getline(ss, line, '|')) {
258 const auto title_id = std::stoul(line, nullptr, 16);
259 const auto disabled_list = config->Get("AddOns", "disabled_" + line, "");
260
261 std::stringstream inner_ss(disabled_list);
262 std::string inner_line;
263 std::vector<std::string> out;
264 while (std::getline(inner_ss, inner_line, '|')) {
265 out.push_back(inner_line);
266 }
267
268 Settings::values.disabled_addons.insert_or_assign(title_id, out);
269 }
270
271 // Web Service
272 ReadSetting("WebService", Settings::values.enable_telemetry);
273 ReadSetting("WebService", Settings::values.web_api_url);
274 ReadSetting("WebService", Settings::values.yuzu_username);
275 ReadSetting("WebService", Settings::values.yuzu_token);
276
277 // Network
278 ReadSetting("Network", Settings::values.network_interface);
279}
280
281void Config::Reload() {
282 LoadINI(DefaultINI::android_config_file);
283 ReadValues();
284}
diff --git a/src/android/app/src/main/jni/config.h b/src/android/app/src/main/jni/config.h
new file mode 100644
index 000000000..0d7d6e94d
--- /dev/null
+++ b/src/android/app/src/main/jni/config.h
@@ -0,0 +1,37 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <filesystem>
7#include <memory>
8#include <optional>
9#include <string>
10
11#include "common/settings.h"
12
13class INIReader;
14
15class Config {
16 std::filesystem::path config_loc;
17 std::unique_ptr<INIReader> config;
18
19 bool LoadINI(const std::string& default_contents = "", bool retry = true);
20 void ReadValues();
21
22public:
23 explicit Config(std::optional<std::filesystem::path> config_path = std::nullopt);
24 ~Config();
25
26 void Reload();
27
28private:
29 /**
30 * Applies a value read from the sdl2_config to a Setting.
31 *
32 * @param group The name of the INI group
33 * @param setting The yuzu setting to modify
34 */
35 template <typename Type, bool ranged>
36 void ReadSetting(const std::string& group, Settings::Setting<Type, ranged>& setting);
37};
diff --git a/src/android/app/src/main/jni/default_ini.h b/src/android/app/src/main/jni/default_ini.h
new file mode 100644
index 000000000..60db951c8
--- /dev/null
+++ b/src/android/app/src/main/jni/default_ini.h
@@ -0,0 +1,499 @@
1// SPDX-FileCopyrightText: 2023 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6namespace DefaultINI {
7
8const char* android_config_file = R"(
9
10[ControlsP0]
11# The input devices and parameters for each Switch native input
12# The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ...
13# It should be in the format of "engine:[engine_name],[param1]:[value1],[param2]:[value2]..."
14# Escape characters $0 (for ':'), $1 (for ',') and $2 (for '$') can be used in values
15
16# Indicates if this player should be connected at boot
17connected=
18
19# for button input, the following devices are available:
20# - "keyboard" (default) for keyboard input. Required parameters:
21# - "code": the code of the key to bind
22# - "sdl" for joystick input using SDL. Required parameters:
23# - "guid": SDL identification GUID of the joystick
24# - "port": the index of the joystick to bind
25# - "button"(optional): the index of the button to bind
26# - "hat"(optional): the index of the hat to bind as direction buttons
27# - "axis"(optional): the index of the axis to bind
28# - "direction"(only used for hat): the direction name of the hat to bind. Can be "up", "down", "left" or "right"
29# - "threshold"(only used for axis): a float value in (-1.0, 1.0) which the button is
30# triggered if the axis value crosses
31# - "direction"(only used for axis): "+" means the button is triggered when the axis value
32# is greater than the threshold; "-" means the button is triggered when the axis value
33# is smaller than the threshold
34button_a=
35button_b=
36button_x=
37button_y=
38button_lstick=
39button_rstick=
40button_l=
41button_r=
42button_zl=
43button_zr=
44button_plus=
45button_minus=
46button_dleft=
47button_dup=
48button_dright=
49button_ddown=
50button_lstick_left=
51button_lstick_up=
52button_lstick_right=
53button_lstick_down=
54button_sl=
55button_sr=
56button_home=
57button_screenshot=
58
59# for analog input, the following devices are available:
60# - "analog_from_button" (default) for emulating analog input from direction buttons. Required parameters:
61# - "up", "down", "left", "right": sub-devices for each direction.
62# Should be in the format as a button input devices using escape characters, for example, "engine$0keyboard$1code$00"
63# - "modifier": sub-devices as a modifier.
64# - "modifier_scale": a float number representing the applied modifier scale to the analog input.
65# Must be in range of 0.0-1.0. Defaults to 0.5
66# - "sdl" for joystick input using SDL. Required parameters:
67# - "guid": SDL identification GUID of the joystick
68# - "port": the index of the joystick to bind
69# - "axis_x": the index of the axis to bind as x-axis (default to 0)
70# - "axis_y": the index of the axis to bind as y-axis (default to 1)
71lstick=
72rstick=
73
74# for motion input, the following devices are available:
75# - "keyboard" (default) for emulating random motion input from buttons. Required parameters:
76# - "code": the code of the key to bind
77# - "sdl" for motion input using SDL. Required parameters:
78# - "guid": SDL identification GUID of the joystick
79# - "port": the index of the joystick to bind
80# - "motion": the index of the motion sensor to bind
81# - "cemuhookudp" for motion input using Cemu Hook protocol. Required parameters:
82# - "guid": the IP address of the cemu hook server encoded to a hex string. for example 192.168.0.1 = "c0a80001"
83# - "port": the port of the cemu hook server
84# - "pad": the index of the joystick
85# - "motion": the index of the motion sensor of the joystick to bind
86motionleft=
87motionright=
88
89[ControlsGeneral]
90# To use the debug_pad, prepend `debug_pad_` before each button setting above.
91# i.e. debug_pad_button_a=
92
93# Enable debug pad inputs to the guest
94# 0 (default): Disabled, 1: Enabled
95debug_pad_enabled =
96
97# Whether to enable or disable vibration
98# 0: Disabled, 1 (default): Enabled
99vibration_enabled=
100
101# Whether to enable or disable accurate vibrations
102# 0 (default): Disabled, 1: Enabled
103enable_accurate_vibrations=
104
105# Enables controller motion inputs
106# 0: Disabled, 1 (default): Enabled
107motion_enabled =
108
109# Defines the udp device's touch screen coordinate system for cemuhookudp devices
110# - "min_x", "min_y", "max_x", "max_y"
111touch_device=
112
113# for mapping buttons to touch inputs.
114#touch_from_button_map=1
115#touch_from_button_maps_0_name=default
116#touch_from_button_maps_0_count=2
117#touch_from_button_maps_0_bind_0=foo
118#touch_from_button_maps_0_bind_1=bar
119# etc.
120
121# List of Cemuhook UDP servers, delimited by ','.
122# Default: 127.0.0.1:26760
123# Example: 127.0.0.1:26760,123.4.5.67:26761
124udp_input_servers =
125
126# Enable controlling an axis via a mouse input.
127# 0 (default): Off, 1: On
128mouse_panning =
129
130# Set mouse sensitivity.
131# Default: 1.0
132mouse_panning_sensitivity =
133
134# Emulate an analog control stick from keyboard inputs.
135# 0 (default): Disabled, 1: Enabled
136emulate_analog_keyboard =
137
138# Enable mouse inputs to the guest
139# 0 (default): Disabled, 1: Enabled
140mouse_enabled =
141
142# Enable keyboard inputs to the guest
143# 0 (default): Disabled, 1: Enabled
144keyboard_enabled =
145
146[Core]
147# Whether to use multi-core for CPU emulation
148# 0: Disabled, 1 (default): Enabled
149use_multi_core =
150
151# Enable unsafe extended guest system memory layout (8GB DRAM)
152# 0 (default): Disabled, 1: Enabled
153use_unsafe_extended_memory_layout =
154
155[Cpu]
156# Adjusts various optimizations.
157# Auto-select mode enables choice unsafe optimizations.
158# Accurate enables only safe optimizations.
159# Unsafe allows any unsafe optimizations.
160# 0 (default): Auto-select, 1: Accurate, 2: Enable unsafe optimizations
161cpu_accuracy =
162
163# Allow disabling safe optimizations.
164# 0 (default): Disabled, 1: Enabled
165cpu_debug_mode =
166
167# Enable inline page tables optimization (faster guest memory access)
168# 0: Disabled, 1 (default): Enabled
169cpuopt_page_tables =
170
171# Enable block linking CPU optimization (reduce block dispatcher use during predictable jumps)
172# 0: Disabled, 1 (default): Enabled
173cpuopt_block_linking =
174
175# Enable return stack buffer CPU optimization (reduce block dispatcher use during predictable returns)
176# 0: Disabled, 1 (default): Enabled
177cpuopt_return_stack_buffer =
178
179# Enable fast dispatcher CPU optimization (use a two-tiered dispatcher architecture)
180# 0: Disabled, 1 (default): Enabled
181cpuopt_fast_dispatcher =
182
183# Enable context elimination CPU Optimization (reduce host memory use for guest context)
184# 0: Disabled, 1 (default): Enabled
185cpuopt_context_elimination =
186
187# Enable constant propagation CPU optimization (basic IR optimization)
188# 0: Disabled, 1 (default): Enabled
189cpuopt_const_prop =
190
191# Enable miscellaneous CPU optimizations (basic IR optimization)
192# 0: Disabled, 1 (default): Enabled
193cpuopt_misc_ir =
194
195# Enable reduction of memory misalignment checks (reduce memory fallbacks for misaligned access)
196# 0: Disabled, 1 (default): Enabled
197cpuopt_reduce_misalign_checks =
198
199# Enable Host MMU Emulation (faster guest memory access)
200# 0: Disabled, 1 (default): Enabled
201cpuopt_fastmem =
202
203# Enable Host MMU Emulation for exclusive memory instructions (faster guest memory access)
204# 0: Disabled, 1 (default): Enabled
205cpuopt_fastmem_exclusives =
206
207# Enable fallback on failure of fastmem of exclusive memory instructions (faster guest memory access)
208# 0: Disabled, 1 (default): Enabled
209cpuopt_recompile_exclusives =
210
211# Enable optimization to ignore invalid memory accesses (faster guest memory access)
212# 0: Disabled, 1 (default): Enabled
213cpuopt_ignore_memory_aborts =
214
215# Enable unfuse FMA (improve performance on CPUs without FMA)
216# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
217# 0: Disabled, 1 (default): Enabled
218cpuopt_unsafe_unfuse_fma =
219
220# Enable faster FRSQRTE and FRECPE
221# Only enabled if cpu_accuracy is set to Unsafe.
222# 0: Disabled, 1 (default): Enabled
223cpuopt_unsafe_reduce_fp_error =
224
225# Enable faster ASIMD instructions (32 bits only)
226# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
227# 0: Disabled, 1 (default): Enabled
228cpuopt_unsafe_ignore_standard_fpcr =
229
230# Enable inaccurate NaN handling
231# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
232# 0: Disabled, 1 (default): Enabled
233cpuopt_unsafe_inaccurate_nan =
234
235# Disable address space checks (64 bits only)
236# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
237# 0: Disabled, 1 (default): Enabled
238cpuopt_unsafe_fastmem_check =
239
240# Enable faster exclusive instructions
241# Only enabled if cpu_accuracy is set to Unsafe. Automatically chosen with cpu_accuracy = Auto-select.
242# 0: Disabled, 1 (default): Enabled
243cpuopt_unsafe_ignore_global_monitor =
244
245[Renderer]
246# Which backend API to use.
247# 0: OpenGL (unsupported), 1 (default): Vulkan, 2: Null
248backend =
249
250# Enable graphics API debugging mode.
251# 0 (default): Disabled, 1: Enabled
252debug =
253
254# Enable shader feedback.
255# 0 (default): Disabled, 1: Enabled
256renderer_shader_feedback =
257
258# Enable Nsight Aftermath crash dumps
259# 0 (default): Disabled, 1: Enabled
260nsight_aftermath =
261
262# Disable shader loop safety checks, executing the shader without loop logic changes
263# 0 (default): Disabled, 1: Enabled
264disable_shader_loop_safety_checks =
265
266# Which Vulkan physical device to use (defaults to 0)
267vulkan_device =
268
269# 0: 0.5x (360p/540p) [EXPERIMENTAL]
270# 1: 0.75x (540p/810p) [EXPERIMENTAL]
271# 2 (default): 1x (720p/1080p)
272# 3: 2x (1440p/2160p)
273# 4: 3x (2160p/3240p)
274# 5: 4x (2880p/4320p)
275# 6: 5x (3600p/5400p)
276# 7: 6x (4320p/6480p)
277resolution_setup =
278
279# Pixel filter to use when up- or down-sampling rendered frames.
280# 0: Nearest Neighbor
281# 1 (default): Bilinear
282# 2: Bicubic
283# 3: Gaussian
284# 4: ScaleForce
285# 5: AMD FidelityFX™️ Super Resolution [Vulkan Only]
286scaling_filter =
287
288# Anti-Aliasing (AA)
289# 0 (default): None, 1: FXAA
290anti_aliasing =
291
292# Whether to use fullscreen or borderless window mode
293# 0 (Windows default): Borderless window, 1 (All other default): Exclusive fullscreen
294fullscreen_mode =
295
296# Aspect ratio
297# 0: Default (16:9), 1: Force 4:3, 2: Force 21:9, 3: Stretch to Window
298aspect_ratio =
299
300# Anisotropic filtering
301# 0: Default, 1: 2x, 2: 4x, 3: 8x, 4: 16x
302max_anisotropy =
303
304# Whether to enable VSync or not.
305# OpenGL: Values other than 0 enable VSync
306# Vulkan: FIFO is selected if the requested mode is not supported by the driver.
307# FIFO (VSync) does not drop frames or exhibit tearing but is limited by the screen refresh rate.
308# FIFO Relaxed is similar to FIFO but allows tearing as it recovers from a slow down.
309# Mailbox can have lower latency than FIFO and does not tear but may drop frames.
310# Immediate (no synchronization) just presents whatever is available and can exhibit tearing.
311# 0: Immediate (Off), 1: Mailbox, 2 (Default): FIFO (On), 3: FIFO Relaxed
312use_vsync =
313
314# Selects the OpenGL shader backend. NV_gpu_program5 is required for GLASM. If NV_gpu_program5 is
315# not available and GLASM is selected, GLSL will be used.
316# 0: GLSL, 1 (default): GLASM, 2: SPIR-V
317shader_backend =
318
319# Whether to allow asynchronous shader building.
320# 0 (default): Off, 1: On
321use_asynchronous_shaders =
322
323# NVDEC emulation.
324# 0: Disabled, 1: CPU Decoding, 2 (default): GPU Decoding
325nvdec_emulation =
326
327# Accelerate ASTC texture decoding.
328# 0: Off, 1 (default): On
329accelerate_astc =
330
331# Turns on the speed limiter, which will limit the emulation speed to the desired speed limit value
332# 0: Off, 1: On (default)
333use_speed_limit =
334
335# Limits the speed of the game to run no faster than this value as a percentage of target speed
336# 1 - 9999: Speed limit as a percentage of target game speed. 100 (default)
337speed_limit =
338
339# Whether to use disk based shader cache
340# 0: Off, 1 (default): On
341use_disk_shader_cache =
342
343# Which gpu accuracy level to use
344# 0: Normal, 1 (default): High, 2: Extreme (Very slow)
345gpu_accuracy =
346
347# Whether to use asynchronous GPU emulation
348# 0 : Off (slow), 1 (default): On (fast)
349use_asynchronous_gpu_emulation =
350
351# Inform the guest that GPU operations completed more quickly than they did.
352# 0: Off, 1 (default): On
353use_fast_gpu_time =
354
355# Force unmodified buffers to be flushed, which can cost performance.
356# 0: Off (default), 1: On
357use_pessimistic_flushes =
358
359# Whether to use garbage collection or not for GPU caches.
360# 0 (default): Off, 1: On
361use_caches_gc =
362
363# The clear color for the renderer. What shows up on the sides of the bottom screen.
364# Must be in range of 0-255. Defaults to 0 for all.
365bg_red =
366bg_blue =
367bg_green =
368
369[Audio]
370# Which audio output engine to use.
371# auto (default): Auto-select
372# cubeb: Cubeb audio engine (if available)
373# sdl2: SDL2 audio engine (if available)
374# null: No audio output
375output_engine =
376
377# Which audio device to use.
378# auto (default): Auto-select
379output_device =
380
381# Output volume.
382# 100 (default): 100%, 0; mute
383volume =
384
385[Data Storage]
386# Whether to create a virtual SD card.
387# 1 (default): Yes, 0: No
388use_virtual_sd =
389
390# Whether or not to enable gamecard emulation
391# 1: Yes, 0 (default): No
392gamecard_inserted =
393
394# Whether or not the gamecard should be emulated as the current game
395# If 'gamecard_inserted' is 0 this setting is irrelevant
396# 1: Yes, 0 (default): No
397gamecard_current_game =
398
399# Path to an XCI file to use as the gamecard
400# If 'gamecard_inserted' is 0 this setting is irrelevant
401# If 'gamecard_current_game' is 1 this setting is irrelevant
402gamecard_path =
403
404[System]
405# Whether the system is docked
406# 1 (default): Yes, 0: No
407use_docked_mode =
408
409# Sets the seed for the RNG generator built into the switch
410# rng_seed will be ignored and randomly generated if rng_seed_enabled is false
411rng_seed_enabled =
412rng_seed =
413
414# Sets the current time (in seconds since 12:00 AM Jan 1, 1970) that will be used by the time service
415# This will auto-increment, with the time set being the time the game is started
416# This override will only occur if custom_rtc_enabled is true, otherwise the current time is used
417custom_rtc_enabled =
418custom_rtc =
419
420# Sets the systems language index
421# 0: Japanese, 1: English (default), 2: French, 3: German, 4: Italian, 5: Spanish, 6: Chinese,
422# 7: Korean, 8: Dutch, 9: Portuguese, 10: Russian, 11: Taiwanese, 12: British English, 13: Canadian French,
423# 14: Latin American Spanish, 15: Simplified Chinese, 16: Traditional Chinese, 17: Brazilian Portuguese
424language_index =
425
426# The system region that yuzu will use during emulation
427# -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan
428region_index =
429
430# The system time zone that yuzu will use during emulation
431# 0: Auto-select (default), 1: Default (system archive value), Others: Index for specified time zone
432time_zone_index =
433
434# Sets the sound output mode.
435# 0: Mono, 1 (default): Stereo, 2: Surround
436sound_index =
437
438[Miscellaneous]
439# A filter which removes logs below a certain logging level.
440# Examples: *:Debug Kernel.SVC:Trace Service.*:Critical
441log_filter = *:Trace
442
443# Use developer keys
444# 0 (default): Disabled, 1: Enabled
445use_dev_keys =
446
447[Debugging]
448# Record frame time data, can be found in the log directory. Boolean value
449record_frame_times =
450# Determines whether or not yuzu will dump the ExeFS of all games it attempts to load while loading them
451dump_exefs=false
452# Determines whether or not yuzu will dump all NSOs it attempts to load while loading them
453dump_nso=false
454# Determines whether or not yuzu will save the filesystem access log.
455enable_fs_access_log=false
456# Enables verbose reporting services
457reporting_services =
458# Determines whether or not yuzu will report to the game that the emulated console is in Kiosk Mode
459# false: Retail/Normal Mode (default), true: Kiosk Mode
460quest_flag =
461# Determines whether debug asserts should be enabled, which will throw an exception on asserts.
462# false: Disabled (default), true: Enabled
463use_debug_asserts =
464# Determines whether unimplemented HLE service calls should be automatically stubbed.
465# false: Disabled (default), true: Enabled
466use_auto_stub =
467# Enables/Disables the macro JIT compiler
468disable_macro_jit=false
469# Determines whether to enable the GDB stub and wait for the debugger to attach before running.
470# false: Disabled (default), true: Enabled
471use_gdbstub=false
472# The port to use for the GDB server, if it is enabled.
473gdbstub_port=6543
474
475[WebService]
476# Whether or not to enable telemetry
477# 0: No, 1 (default): Yes
478enable_telemetry =
479# URL for Web API
480web_api_url = https://api.yuzu-emu.org
481# Username and token for yuzu Web Service
482# See https://profile.yuzu-emu.org/ for more info
483yuzu_username =
484yuzu_token =
485
486[Network]
487# Name of the network interface device to use with yuzu LAN play.
488# e.g. On *nix: 'enp7s0', 'wlp6s0u1u3u3', 'lo'
489# e.g. On Windows: 'Ethernet', 'Wi-Fi'
490network_interface =
491
492[AddOns]
493# Used to disable add-ons
494# List of title IDs of games that will have add-ons disabled (separated by '|'):
495title_ids =
496# For each title ID, have a key/value pair called `disabled_<title_id>` equal to the names of the add-ons to disable (sep. by '|')
497# e.x. disabled_0100000000010000 = Update|DLC <- disables Updates and DLC on Super Mario Odyssey
498)";
499} // namespace DefaultINI
diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp
index f548931f1..5d93da237 100644
--- a/src/android/app/src/main/jni/native.cpp
+++ b/src/android/app/src/main/jni/native.cpp
@@ -19,6 +19,7 @@
19#include "core/file_sys/vfs_real.h" 19#include "core/file_sys/vfs_real.h"
20#include "core/hle/service/filesystem/filesystem.h" 20#include "core/hle/service/filesystem/filesystem.h"
21#include "core/perf_stats.h" 21#include "core/perf_stats.h"
22#include "jni/config.h"
22#include "jni/emu_window/emu_window.h" 23#include "jni/emu_window/emu_window.h"
23#include "jni/id_cache.h" 24#include "jni/id_cache.h"
24#include "video_core/rasterizer_interface.h" 25#include "video_core/rasterizer_interface.h"
@@ -67,6 +68,9 @@ static Core::SystemResultStatus RunEmulation(const std::string& filepath) {
67 return Core::SystemResultStatus::ErrorLoader; 68 return Core::SystemResultStatus::ErrorLoader;
68 } 69 }
69 70
71 // Loads the configuration.
72 Config{};
73
70 system_.Initialize(); 74 system_.Initialize();
71 system_.ApplySettings(); 75 system_.ApplySettings();
72 76
@@ -245,7 +249,9 @@ jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_GetGitRevision([[maybe_unused]] JN
245} 249}
246 250
247void Java_org_yuzu_yuzu_1emu_NativeLibrary_CreateConfigFile 251void Java_org_yuzu_yuzu_1emu_NativeLibrary_CreateConfigFile
248 [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {} 252 [[maybe_unused]] (JNIEnv* env, [[maybe_unused]] jclass clazz) {
253 Config{};
254}
249 255
250jint Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore([[maybe_unused]] JNIEnv* env, 256jint Java_org_yuzu_yuzu_1emu_NativeLibrary_DefaultCPUCore([[maybe_unused]] JNIEnv* env,
251 [[maybe_unused]] jclass clazz) { 257 [[maybe_unused]] jclass clazz) {
@@ -257,7 +263,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_Run__Ljava_lang_String_2Ljava_lang_St
257 [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {} 263 [[maybe_unused]] jstring j_savestate, [[maybe_unused]] jboolean j_delete_savestate) {}
258 264
259void Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings([[maybe_unused]] JNIEnv* env, 265void Java_org_yuzu_yuzu_1emu_NativeLibrary_ReloadSettings([[maybe_unused]] JNIEnv* env,
260 [[maybe_unused]] jclass clazz) {} 266 [[maybe_unused]] jclass clazz) {
267 Config{};
268}
261 269
262jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting([[maybe_unused]] JNIEnv* env, 270jstring Java_org_yuzu_yuzu_1emu_NativeLibrary_GetUserSetting([[maybe_unused]] JNIEnv* env,
263 [[maybe_unused]] jclass clazz, 271 [[maybe_unused]] jclass clazz,
diff --git a/src/android/app/src/main/res/menu/menu_game_grid.xml b/src/android/app/src/main/res/menu/menu_game_grid.xml
index 9cdcc7f08..cd515afbf 100644
--- a/src/android/app/src/main/res/menu/menu_game_grid.xml
+++ b/src/android/app/src/main/res/menu/menu_game_grid.xml
@@ -3,11 +3,6 @@
3 xmlns:app="http://schemas.android.com/apk/res-auto"> 3 xmlns:app="http://schemas.android.com/apk/res-auto">
4 4
5 <item 5 <item
6 android:id="@+id/button_premium"
7 android:icon="@drawable/ic_premium"
8 android:title="@string/premium_text"
9 app:showAsAction="ifRoom" />
10 <item
11 android:id="@+id/button_file_menu" 6 android:id="@+id/button_file_menu"
12 android:icon="@drawable/ic_folder" 7 android:icon="@drawable/ic_folder"
13 android:title="@string/select_game_folder" 8 android:title="@string/select_game_folder"
diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml
index c948e6a8b..bf9922be8 100644
--- a/src/android/app/src/main/res/values/arrays.xml
+++ b/src/android/app/src/main/res/values/arrays.xml
@@ -61,16 +61,22 @@
61 <string-array name="languageNames"> 61 <string-array name="languageNames">
62 <item>Japanese (日本語)</item> 62 <item>Japanese (日本語)</item>
63 <item>English</item> 63 <item>English</item>
64 <item>French (français)</item> 64 <item>French (Français)</item>
65 <item>German (Deutsch)</item> 65 <item>German (Deutsch)</item>
66 <item>Italian (italiano)</item> 66 <item>Italian (Italiano)</item>
67 <item>Spanish (español)</item> 67 <item>Spanish (Español)</item>
68 <item>Simplified Chinese (简体中文)</item> 68 <item>Chinese (简体中文)</item>
69 <item>Korean (한국어)</item> 69 <item>Korean (한국어)</item>
70 <item>Dutch (Nederlands)</item> 70 <item>Dutch (Nederlands)</item>
71 <item>Portuguese (português)</item> 71 <item>Portuguese (Português)</item>
72 <item>Russian (Русский)</item> 72 <item>Russian (Русский)</item>
73 <item>Traditional Chinese (正體中文)</item> 73 <item>Taiwanese (台湾)</item>
74 <item>British English</item>
75 <item>Canadian French (Français canadien)</item>
76 <item>Latin American Spanish (Español latinoamericano)</item>
77 <item>Simplified Chinese (简体中文)</item>
78 <item>Traditional Chinese (正體中文))</item>
79 <item>Brazilian Portuguese (Portugues do Brasil)</item>
74 </string-array> 80 </string-array>
75 81
76 <integer-array name="languageValues"> 82 <integer-array name="languageValues">
@@ -86,89 +92,65 @@
86 <item>9</item> 92 <item>9</item>
87 <item>10</item> 93 <item>10</item>
88 <item>11</item> 94 <item>11</item>
95 <item>12</item>
96 <item>13</item>
97 <item>14</item>
98 <item>15</item>
99 <item>16</item>
100 <item>17</item>
89 </integer-array> 101 </integer-array>
90 102
91 <string-array name="n3dsButtons"> 103 <string-array name="rendererApiNames">
92 <item>a</item> 104 <item>Vulkan</item>
93 <item>b</item> 105 <item>None</item>
94 <item>x</item>
95 <item>y</item>
96 <item>L</item>
97 <item>R</item>
98 <item>ZL</item>
99 <item>ZR</item>
100 <item>Start</item>
101 <item>Select</item>
102 <item>D-Pad</item>
103 <item>Circle Pad</item>
104 <item>C Stick</item>
105 </string-array>
106
107 <string-array name="cameraImageSourceNames">
108 <item>Blank</item>
109 <item>Still Image</item>
110 <item>Device Camera</item>
111 </string-array>
112
113 <string-array name="cameraImageSourceValues">
114 <item>blank</item>
115 <item>image</item>
116 <item>ndk</item>
117 </string-array>
118
119 <string-array name="cameraDeviceNames">
120 <item>Default</item>
121 <item>Any Front Camera</item>
122 <item>Any Back Camera</item>
123 </string-array> 106 </string-array>
124 107
125 <string-array name="cameraDeviceValues"> 108 <integer-array name="rendererApiValues">
126 <item /> 109 <item>1</item>
127 <item>_front</item> 110 <item>2</item>
128 <item>_back</item> 111 </integer-array>
129 </string-array>
130 112
131 <string-array name="cameraFlipNames"> 113 <string-array name="rendererAccuracyNames">
132 <item>None</item> 114 <item>Normal</item>
133 <item>Horizontal</item> 115 <item>High</item>
134 <item>Vertical</item> 116 <item>Extreme (Slow)</item>
135 <item>Reverse</item>
136 </string-array> 117 </string-array>
137 118
138 <integer-array name="cameraFlipValues"> 119 <integer-array name="rendererAccuracyValues">
139 <item>0</item> 120 <item>0</item>
140 <item>1</item> 121 <item>1</item>
141 <item>2</item> 122 <item>2</item>
142 <item>3</item>
143 </integer-array> 123 </integer-array>
144 124
145 <string-array name="audioInputTypeNames"> 125 <string-array name="rendererResolutionNames">
146 <item>None</item> 126 <item>0.5X (360p/540p)</item>
147 <item>Real Device</item> 127 <item>0.75X (540p/810p)</item>
148 <item>Static Noise</item> 128 <item>1X (720p/1080p)</item>
129 <item>2X (1440p/2160p) (Slow)</item>
130 <item>3X (2160p/3240p) (Slow)</item>
131 <item>4X (2880p/4320p) (Slow)</item>
149 </string-array> 132 </string-array>
150 133
151 <integer-array name="audioInputTypeValues"> 134 <integer-array name="rendererResolutionValues">
152 <item>0</item> 135 <item>0</item>
153 <item>1</item> 136 <item>1</item>
154 <item>2</item> 137 <item>2</item>
138 <item>3</item>
139 <item>4</item>
140 <item>5</item>
155 </integer-array> 141 </integer-array>
156 142
157 <string-array name="render3dModes"> 143 <string-array name="cpuAccuracyNames">
158 <item>Off</item> 144 <item>Auto</item>
159 <item>Side by Side</item> 145 <item>Accurate</item>
160 <item>Anaglyph</item> 146 <item>Unsafe</item>
161 <item>Interlaced</item> 147 <item>Paranoid (Slow)</item>
162 <item>Reverse Interlaced</item>
163 <item>Cardboard VR</item>
164 </string-array> 148 </string-array>
165 149
166 <integer-array name="render3dValues"> 150 <integer-array name="cpuAccuracyValues">
167 <item>0</item> 151 <item>0</item>
168 <item>1</item> 152 <item>1</item>
169 <item>2</item> 153 <item>2</item>
170 <item>3</item> 154 <item>3</item>
171 <item>4</item>
172 <item>5</item>
173 </integer-array> 155 </integer-array>
174</resources> 156</resources>
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index 58d80398f..239fde48d 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -9,105 +9,31 @@
9 <string name="app_notification_channel_description">yuzu Switch emulator notifications</string> 9 <string name="app_notification_channel_description">yuzu Switch emulator notifications</string>
10 <string name="app_notification_running">yuzu is running</string> 10 <string name="app_notification_running">yuzu is running</string>
11 11
12 <!-- Input related strings --> 12 <!-- General settings strings -->
13 <string name="controller_circlepad">Circle Pad</string> 13 <string name="frame_limit_enable">Enable limit speed</string>
14 <string name="controller_c">C-Stick</string> 14 <string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed.</string>
15 <string name="controller_triggers">Triggers</string> 15 <string name="frame_limit_slider">Limit speed percent</string>
16 <string name="controller_dpad">D-Pad</string> 16 <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
17 <string name="controller_axis_vertical">Up/Down Axis</string> 17 <string name="cpu_accuracy">CPU accuracy</string>
18 <string name="controller_axis_horizontal">Left/Right Axis</string>
19 <string name="input_binding">Input Binding</string>
20 <string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
21 <string name="input_binding_description_vertical_axis">Move your joystick up or down.</string>
22 <string name="input_binding_description_horizontal_axis">Move your joystick left or right.</string>
23 <string name="button_a" translatable="false">A</string>
24 <string name="button_b" translatable="false">B</string>
25 <string name="button_select" translatable="false">SELECT</string>
26 <string name="button_start" translatable="false">START</string>
27 <string name="button_x" translatable="false">X</string>
28 <string name="button_y" translatable="false">Y</string>
29 <string name="button_l" translatable="false">L</string>
30 <string name="button_r" translatable="false">R</string>
31 <string name="button_zl" translatable="false">ZL</string>
32 <string name="button_zr" translatable="false">ZR</string>
33 <string name="input_message_analog_only">This control must be bound to a gamepad analog stick or D-pad axis!</string>
34 <string name="input_message_button_only">This control must be bound to a gamepad button!</string>
35
36 <!-- Generic buttons (Shared with lots of stuff) -->
37 <string name="generic_buttons">Buttons</string>
38
39 <!-- Premium settings strings -->
40 <string name="design">Change Theme (Light, Dark)</string>
41 <string name="design_updated">Theme will update when exiting Settings</string>
42
43 <!-- Core settings strings -->
44 <string name="cpu_jit">Enable CPU JIT</string>
45 <string name="cpu_jit_description">Uses the Just-in-Time (JIT) compiler for CPU emulation. When enabled, game performance will be significantly improved.</string>
46 <string name="init_clock">System clock type</string>
47 <string name="init_clock_description">Set the emulated Console clock to either reflect that of your device or start at a simulated date and time.</string>
48 18
49 <!-- System settings strings --> 19 <!-- System settings strings -->
20 <string name="use_docked_mode">Docked mode</string>
21 <string name="use_docked_mode_description">Emulates in docked mode, which increases the resolution at the expense of performance.</string>
50 <string name="init_time">System clock starting time override</string> 22 <string name="init_time">System clock starting time override</string>
51 <string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string> 23 <string name="init_time_description">If the \"System clock type\" setting is set to \"Simulated clock\", this changes the fixed date and time to start at.</string>
52 <string name="emulated_region">Emulated region</string> 24 <string name="emulated_region">Emulated region</string>
53 <string name="emulated_language">Emulated language</string> 25 <string name="emulated_language">Emulated language</string>
54 26
55 <!-- Graphics settings strings --> 27 <!-- Graphics settings strings -->
56 <string name="renderer">Renderer</string> 28 <string name="renderer_api">API</string>
57 <string name="vsync">Enable V-Sync</string> 29 <string name="renderer_accuracy">Accuracy level</string>
58 <string name="vsync_description">Synchronizes the game frame rate to the refresh rate of your device.</string> 30 <string name="renderer_resolution">Resolution</string>
59 <string name="linear_filtering">Enable linear filtering</string> 31 <string name="renderer_asynchronous_shaders">Use asynchronous shaders</string>
60 <string name="linear_filtering_description">Enables linear filtering, which causes game visuals to appear smoother.</string> 32 <string name="renderer_asynchronous_shaders_description">Compiles shaders asynchronously, which will reduce stutter but may introduce glitches.</string>
61 <string name="texture_filter_name">Texture Filter</string>
62 <string name="texture_filter_description">Enhances the visuals of games by applying a filter to textures. The supported filters are Anime4K Ultrafast, Bicubic, ScaleForce, and xBRZ freescale.</string>
63 <string name="hw_renderer">Enable hardware renderer</string>
64 <string name="hw_renderer_description">Uses hardware to emulate 3DS graphics. When enabled, game performance will be significantly improved.</string>
65 <string name="hw_shaders">Enable hardware shader</string>
66 <string name="hw_shaders_description">Uses hardware to emulate 3DS shaders. When enabled, game performance will be significantly improved.</string>
67 <string name="shaders_accurate_mul">Enable accurate shader multiplication</string>
68 <string name="shaders_accurate_mul_description">Uses more accurate multiplication in hardware shaders, which may fix some graphical bugs. When enabled, performance will be reduced.</string>
69 <string name="asynchronous_gpu">Enable asynchronous GPU emulation</string>
70 <string name="asynchronous_gpu_description">Uses a separate thread to emulate the GPU asynchronously. When enabled, performance will be improved.</string>
71 <string name="frame_limit_enable">Enable limit speed</string>
72 <string name="frame_limit_enable_description">When enabled, emulation speed will be limited to a specified percentage of normal speed.</string>
73 <string name="frame_limit_slider">Limit speed percent</string>
74 <string name="frame_limit_slider_description">Specifies the percentage to limit emulation speed. With the default of 100% emulation will be limited to normal speed. Values higher or lower will increase or decrease the speed limit.</string>
75 <string name="internal_resolution">Internal resolution</string>
76 <string name="internal_resolution_description">Specifies the resolution used to render at. A high resolution will improve visual quality a lot but is also quite heavy on performance and might cause glitches in certain games.</string>
77 <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
78 <string name="debug_warning">Warning: Modifying these settings will slow emulation</string>
79 <string name="stereoscopy">Stereoscopy</string>
80 <string name="render3d">Stereoscopic 3D Mode</string>
81 <string name="factor3d">Depth</string>
82 <string name="factor3d_description">Specifies the value of the 3D slider. This should be set to higher than 0% when Stereoscopic 3D is enabled.</string>
83 <string name="cardboard_vr">Cardboard VR</string>
84 <string name="cardboard_screen_size">Cardboard Screen size</string>
85 <string name="cardboard_screen_size_description">Scales the screen to a percentage of its original size.</string>
86 <string name="cardboard_x_shift">Horizontal shift</string>
87 <string name="cardboard_x_shift_description">Specifies the percentage of empty space to shift the screens horizontally. Positive values move the two eyes closer to the middle, while negative values move them away.</string>
88 <string name="cardboard_y_shift">Vertical shift</string>
89 <string name="cardboard_y_shift_description">Specifies the percentage of empty space to shift the screens vertically. Positive values move the two eyes towards the bottom, while negative values move them towards the top.</string>
90 <string name="use_shader_jit">Use shader JIT</string>
91 <string name="use_disk_shader_cache">Use disk shader cache</string>
92 <string name="use_disk_shader_cache_description">Reduce stuttering by storing and loading generated shaders to disk. It cannot be used without Enabling Hardware Shader.</string>
93 <string name="utility">Utility</string>
94 <string name="dump_textures">Dump textures</string>
95 <string name="dump_textures_description">Dumps textures to dump/textures/[GAME ID]</string>
96 <string name="custom_textures">Use custom textures</string>
97 <string name="custom_textures_description">Uses custom textures found in load/textures/[GAME ID]</string>
98 <string name="preload_textures">Preload custom textures</string>
99 <string name="preload_textures_description">Loads all custom textures into memory. This feature can use a lot of memory.</string>
100 <!-- Premium strings -->
101 <string name="premium_text">Premium</string>
102 <string name="premium_settings_upsell">Upgrade to Premium and support yuzu!</string>
103 <string name="premium_settings_upsell_description">With Premium, you will support the developers to continue improving yuzu, and gain access to these exclusive features!</string>
104 <string name="premium_settings_welcome">Welcome to Premium.</string>
105 <string name="premium_settings_welcome_description">Thank you for your support!</string>
106 33
107 <!-- Audio settings strings --> 34 <!-- Audio settings strings -->
108 <string name="audio_stretch">Enable audio stretching</string> 35 <string name="audio_volume">Volume</string>
109 <string name="audio_stretch_description">Stretches audio to reduce stuttering. When enabled, increases audio latency and slightly reduces performance.</string> 36 <string name="audio_volume_description">Specifies the volume of audio output.</string>
110 <string name="audio_input_type">Audio Input Device</string>
111 37
112 <!-- Miscellaneous --> 38 <!-- Miscellaneous -->
113 <string name="clear">Clear</string> 39 <string name="clear">Clear</string>
@@ -126,14 +52,10 @@
126 52
127 <!-- Preferences Screen --> 53 <!-- Preferences Screen -->
128 <string name="preferences_settings">Settings</string> 54 <string name="preferences_settings">Settings</string>
129 <string name="preferences_premium">Premium</string>
130 <string name="preferences_general">General</string> 55 <string name="preferences_general">General</string>
131 <string name="preferences_system">System</string> 56 <string name="preferences_system">System</string>
132 <string name="preferences_camera">Camera</string>
133 <string name="preferences_controls">Gamepad</string>
134 <string name="preferences_graphics">Graphics</string> 57 <string name="preferences_graphics">Graphics</string>
135 <string name="preferences_audio">Audio</string> 58 <string name="preferences_audio">Audio</string>
136 <string name="preferences_debug">Debug</string>
137 59
138 <!-- ROM loading errors --> 60 <!-- ROM loading errors -->
139 <string name="loader_error_encrypted">Your ROM is encrypted</string> 61 <string name="loader_error_encrypted">Your ROM is encrypted</string>
@@ -158,7 +80,7 @@
158 80
159 <string name="do_not_show_this_again">Do not show this again</string> 81 <string name="do_not_show_this_again">Do not show this again</string>
160 82
161 <!-- Software Keyboard --> 83 <!-- Software keyboard -->
162 <string name="software_keyboard">Software Keyboard</string> 84 <string name="software_keyboard">Software Keyboard</string>
163 <string name="i_forgot">I Forgot</string> 85 <string name="i_forgot">I Forgot</string>
164 <string name="fixed_length_required">Text length is not correct (should be %d characters)</string> 86 <string name="fixed_length_required">Text length is not correct (should be %d characters)</string>
@@ -166,7 +88,7 @@
166 <string name="blank_input_not_allowed">Blank input is not allowed</string> 88 <string name="blank_input_not_allowed">Blank input is not allowed</string>
167 <string name="empty_input_not_allowed">Empty input is not allowed</string> 89 <string name="empty_input_not_allowed">Empty input is not allowed</string>
168 90
169 <!-- Core Errors --> 91 <!-- Errors and warnings -->
170 <string name="abort_button">Abort</string> 92 <string name="abort_button">Abort</string>
171 <string name="continue_button">Continue</string> 93 <string name="continue_button">Continue</string>
172 <string name="system_archive_not_found">System Archive Not Found</string> 94 <string name="system_archive_not_found">System Archive Not Found</string>
@@ -175,4 +97,5 @@
175 <string name="save_load_error">Save/Load Error</string> 97 <string name="save_load_error">Save/Load Error</string>
176 <string name="fatal_error">Fatal Error</string> 98 <string name="fatal_error">Fatal Error</string>
177 <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> 99 <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string>
100 <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string>
178</resources> 101</resources>