lots of ui menu changes

This commit is contained in:
izzy2lost
2025-09-11 23:14:41 -04:00
parent 1c676f1250
commit d8fd73eb96
25 changed files with 1323 additions and 607 deletions
+6
View File
@@ -1211,6 +1211,12 @@ Java_com_izzy2lost_psx2_NativeApp_resume(JNIEnv *env, jclass clazz) {
}).detach();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_izzy2lost_psx2_NativeApp_isPaused(JNIEnv *env, jclass clazz) {
return VMManager::GetState() == VMState::Paused;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_izzy2lost_psx2_NativeApp_shutdown(JNIEnv *env, jclass clazz) {
@@ -114,6 +114,13 @@ public class GameSettingsDialogFragment extends DialogFragment {
serialView.setVisibility(View.GONE);
}
// Aspect Ratio Spinner
Spinner spAspectRatio = view.findViewById(R.id.sp_aspect_ratio);
ArrayAdapter<CharSequence> aspectAdapter = ArrayAdapter.createFromResource(ctx,
R.array.aspect_ratio_entries, android.R.layout.simple_spinner_item);
aspectAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spAspectRatio.setAdapter(aspectAdapter);
// Blending Accuracy Spinner
Spinner spBlendingAccuracy = view.findViewById(R.id.sp_blending_accuracy);
ArrayAdapter<CharSequence> blendingAdapter = ArrayAdapter.createFromResource(ctx,
@@ -147,6 +154,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
android.content.SharedPreferences gp = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
int gRenderer = gp.getInt("renderer", -1);
float gScale = gp.getFloat("upscale_multiplier", 1.0f);
int gAspect = gp.getInt("aspect_ratio", 1);
int gBlend = gp.getInt("blending_accuracy", 1);
boolean gWide = gp.getBoolean("widescreen_patches", true);
boolean gNoInt = gp.getBoolean("no_interlacing_patches", true);
@@ -157,6 +165,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
// Map to indices
int defaultRendererIdx = (gRenderer == -1 ? 0 : (gRenderer == 14 ? 1 : (gRenderer == 12 ? 2 : 3)));
int defaultScaleIdx = Math.max(0, Math.min(7, Math.round(gScale) - 1));
spAspectRatio.setSelection(Math.max(0, Math.min(4, gAspect)));
spRenderer.setSelection(defaultRendererIdx);
spResolution.setSelection(defaultScaleIdx);
spBlendingAccuracy.setSelection(Math.max(0, Math.min(5, gBlend)));
@@ -206,6 +215,22 @@ public class GameSettingsDialogFragment extends DialogFragment {
if (m.find()) {
try { float mult = Float.parseFloat(m.group(1)); int sel = Math.max(0, Math.min(7, Math.round(mult - 1))); spResolution.setSelection(sel); } catch (Exception ignored) {}
}
m = java.util.regex.Pattern.compile("(?m)^AspectRatio=\\s*(.+)$").matcher(content);
if (m.find()) {
String av = m.group(1).trim();
int idx = 1; // default Auto 4:3/3:2
try {
int num = Integer.parseInt(av);
if (num >= 0 && num <= 4) idx = num;
} catch (Exception e) {
if ("Stretch".equalsIgnoreCase(av)) idx = 0;
else if ("Auto".equalsIgnoreCase(av)) idx = 1;
else if ("4:3".equalsIgnoreCase(av)) idx = 2;
else if ("16:9".equalsIgnoreCase(av)) idx = 3;
else if ("10:7".equalsIgnoreCase(av)) idx = 4;
}
spAspectRatio.setSelection(idx);
}
m = java.util.regex.Pattern.compile("(?m)^accurate_blending_unit=\\s*(.+)$").matcher(content);
if (m.find()) {
String bv = m.group(1).trim();
@@ -255,6 +280,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
}
} catch (Throwable ignored) {
// Fallback to defaults if loading fails
spAspectRatio.setSelection(1);
spBlendingAccuracy.setSelection(1);
// Mirror global default when error
android.content.SharedPreferences prefs = ctx.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
@@ -276,6 +302,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
.setView(view)
.setNegativeButton("Cancel", (d, w) -> d.dismiss())
.setPositiveButton("Save", (d, w) -> {
final int aspectIdx = spAspectRatio.getSelectedItemPosition();
final int blendLevel = spBlendingAccuracy.getSelectedItemPosition();
final int rendererIdx = spRenderer.getSelectedItemPosition();
final int resIdx = spResolution.getSelectedItemPosition();
@@ -288,7 +315,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
// Persist per-game INI explicitly (supports Auto as well)
writeGameSettingsIni(ctx, gameSerial, gameCrc,
blendLevel, rendererIdx, resIdx, wide, noInt,
aspectIdx, blendLevel, rendererIdx, resIdx, wide, noInt,
/*enablePatches*/ enablePatches, enableCheats);
// Live-apply per-game settings in one batch
@@ -301,6 +328,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
else renderer = 13;
float scale = Math.max(1, Math.min(8, resIdx + 1));
NativeApp.setAspectRatio(aspectIdx);
NativeApp.setLoadTextures(loadTex);
NativeApp.setAsyncTextureLoading(asyncTex);
NativeApp.applyPerGameSettingsBatch(renderer, scale, blendLevel, wide, noInt, enablePatches, enableCheats);
@@ -385,6 +413,7 @@ public class GameSettingsDialogFragment extends DialogFragment {
private static void writeGameSettingsIni(Context ctx,
String gameSerial,
String gameCrc,
int aspectRatioIdx,
int blendingAccuracyIdx,
int rendererIdx,
int resolutionIdx,
@@ -403,11 +432,13 @@ public class GameSettingsDialogFragment extends DialogFragment {
String rendererName = (rendererIdx == 0) ? "Auto" : (rendererIdx == 1 ? "Vulkan" : (rendererIdx == 2 ? "OpenGL" : "Software"));
float upscale = Math.max(1, Math.min(8, resolutionIdx + 1));
int abl = Math.max(0, Math.min(5, blendingAccuracyIdx));
int aspectRatio = Math.max(0, Math.min(4, aspectRatioIdx));
StringBuilder sb = new StringBuilder();
sb.append("[EmuCore/GS]\n");
sb.append("Renderer=").append(rendererName).append('\n');
sb.append("upscale_multiplier=").append((int) upscale).append('\n');
sb.append("AspectRatio=").append(aspectRatio).append('\n');
sb.append("accurate_blending_unit=").append(abl).append('\n');
sb.append('\n');
sb.append("[EmuCore]\n");
File diff suppressed because it is too large Load Diff
@@ -1,69 +0,0 @@
package com.izzy2lost.psx2;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class LettersAdapter extends RecyclerView.Adapter<LettersAdapter.VH> {
public interface OnClick {
void onClick(char letter);
}
private final List<Character> letters;
private final OnClick onClick;
public LettersAdapter(List<Character> letters, OnClick onClick) {
this.letters = letters;
this.onClick = onClick;
setHasStableIds(true);
}
@Override
public long getItemId(int position) {
// Ensure uniqueness across the infinite range by including position
return position;
}
@NonNull
@Override
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_letter, parent, false);
return new VH(v);
}
@Override
public void onBindViewHolder(@NonNull VH holder, int position) {
int n = letters != null ? letters.size() : 0;
if (n == 0) return;
int real = position % n;
char ch = letters.get(real);
holder.text.setText(String.valueOf(ch));
holder.itemView.setOnClickListener(v -> {
android.util.Log.d("LettersAdapter", "Letter clicked: " + ch + " at position " + position);
if (onClick != null) {
onClick.onClick(ch);
} else {
android.util.Log.d("LettersAdapter", "onClick is null!");
}
});
}
@Override
public int getItemCount() {
return (letters != null && !letters.isEmpty()) ? Integer.MAX_VALUE : 0;
}
static class VH extends RecyclerView.ViewHolder {
final TextView text;
VH(@NonNull View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text_letter);
}
}
}
@@ -18,6 +18,8 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.view.ViewGroup;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -74,6 +76,7 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
private boolean joyRightPressed = false;
private boolean controllerUiApplied = false;
private AlertDialog mBiosPromptDialog = null;
private boolean mControllerHintShowing = false;
private boolean isThread() {
if (mEmulationThread != null) {
@@ -223,6 +226,15 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
w.setAttributes(lp);
}
// Common flags for legacy immersive (works as belt-and-suspenders on newer API too)
final int legacyFlags = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
// Tell Window to lay out edge-to-edge and hide all system bars
w.setDecorFitsSystemWindows(false);
@@ -231,15 +243,22 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
controller.hide(WindowInsets.Type.systemBars());
controller.setSystemBarsBehavior(WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
}
// Also apply legacy flags to decor; helps on 3-button nav devices
View decor = w.getDecorView();
decor.setSystemUiVisibility(legacyFlags);
decor.setOnSystemUiVisibilityChangeListener(vis -> {
if ((vis & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
decor.setSystemUiVisibility(legacyFlags);
}
});
} else {
View decor = w.getDecorView();
int flags = View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
decor.setSystemUiVisibility(flags);
decor.setSystemUiVisibility(legacyFlags);
decor.setOnSystemUiVisibilityChangeListener(vis -> {
if ((vis & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
decor.setSystemUiVisibility(legacyFlags);
}
});
}
}
@@ -314,7 +333,7 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
}
private static final String[] GAME_EXTS = new String[]{
".iso", ".bin", ".img", ".mdf", ".nrg", ".chd"
".iso", ".bin", ".img", ".mdf", ".nrg", ".chd", ".cso", ".zso", ".gz"
};
private static boolean hasGameExt(String name) {
@@ -483,11 +502,23 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
if (btn_settings != null) {
btn_settings.setOnClickListener(v -> {
try {
// Refresh drawer settings before opening
refreshDrawerSettings();
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer != null) drawer.openDrawer(androidx.core.view.GravityCompat.START);
} catch (Throwable ignored) {}
});
}
// Pause/Play button toggles emulation pause state
MaterialButton btn_pause_play = findViewById(R.id.btn_pause_play);
if (btn_pause_play != null) {
btn_pause_play.setOnClickListener(v -> {
try {
togglePauseState();
} catch (Throwable ignored) {}
});
}
// Wire left drawer header controls (title/power/reboot/renderer)
try {
NavigationView nav = findViewById(R.id.nav_view);
@@ -542,6 +573,18 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
} catch (Throwable ignored) {}
});
}
View btnGames = header.findViewById(R.id.drawer_btn_games);
if (btnGames != null) {
btnGames.setOnClickListener(v -> {
try {
// Close drawer first
DrawerLayout drawer = findViewById(R.id.drawer_layout);
if (drawer != null) drawer.closeDrawer(androidx.core.view.GravityCompat.START);
// Open games dialog
openGamesDialog();
} catch (Throwable ignored) {}
});
}
if (btnGameState != null) {
btnGameState.setOnClickListener(v -> {
try {
@@ -550,6 +593,9 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
} catch (Throwable ignored) {}
});
}
// Setup drawer settings controls to mirror quick actions
setupDrawerSettings(header);
}
}
} catch (Throwable ignored) {}
@@ -809,6 +855,11 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
// Hide on-screen touch controls when a physical controller is connected
setControlsVisible(!hasController);
// Show controller hint when no controller is connected (only once)
if (!hasController) {
showControllerHintIfNeeded();
}
}
private int getCurrentRendererPref() {
@@ -1333,7 +1384,7 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
SDLControllerManager.initialize();
// Load and apply saved settings
SettingsDialogFragment.loadAndApplySettings(this);
loadAndApplyStoredSettings();
mHIDDeviceManager = HIDDeviceManager.acquire(this);
// Initialize HID device manager for USB and Bluetooth controllers
@@ -1360,6 +1411,14 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
if(!isThread()) {
mEmulationThread = new Thread(() -> NativeApp.runVMThread(m_szGamefile));
mEmulationThread.start();
// Show pause button when game starts (game is always running initially)
runOnUiThread(() -> {
MaterialButton btn_pause_play = findViewById(R.id.btn_pause_play);
if (btn_pause_play != null) {
btn_pause_play.setVisibility(View.VISIBLE);
btn_pause_play.setIcon(ContextCompat.getDrawable(this, R.drawable.pause_circle_24px));
}
});
}
}
@@ -1395,6 +1454,7 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
} else {
// No game loaded; just shutdown for safety
NativeApp.shutdown();
updatePausePlayButton();
}
}
@@ -1704,10 +1764,320 @@ public class MainActivity extends AppCompatActivity implements GamesCoverDialogF
break;
}
}
private void setupDrawerSettings(View header) {
if (header == null) return;
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
// Aspect Ratio spinner - setup like QuickActions
Spinner spAspect = header.findViewById(R.id.drawer_sp_aspect_ratio);
if (spAspect != null) {
if (spAspect.getAdapter() == null) {
ArrayAdapter<CharSequence> aspectAdapter = ArrayAdapter.createFromResource(this,
R.array.aspect_ratio_entries, android.R.layout.simple_spinner_item);
aspectAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spAspect.setAdapter(aspectAdapter);
spAspect.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
prefs.edit().putInt("aspect_ratio", position).apply();
try { NativeApp.setAspectRatio(position); } catch (Throwable ignored) {}
}
@Override public void onNothingSelected(android.widget.AdapterView<?> parent) {}
});
}
ArrayAdapter<?> adapter = (ArrayAdapter<?>) spAspect.getAdapter();
if (adapter != null) {
int savedAspect = prefs.getInt("aspect_ratio", 1);
if (savedAspect < 0 || savedAspect >= adapter.getCount()) savedAspect = 1;
spAspect.setSelection(savedAspect);
}
}
// Resolution Scale spinner
Spinner spScale = header.findViewById(R.id.drawer_sp_scale);
if (spScale != null) {
if (spScale.getAdapter() == null) {
ArrayAdapter<CharSequence> scaleAdapter = ArrayAdapter.createFromResource(this,
R.array.scale_entries, android.R.layout.simple_spinner_item);
scaleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spScale.setAdapter(scaleAdapter);
spScale.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
float scale = Math.max(1, Math.min(8, position + 1));
prefs.edit().putFloat("upscale_multiplier", scale).apply();
try { NativeApp.renderUpscalemultiplier(scale); } catch (Throwable ignored) {}
}
@Override public void onNothingSelected(android.widget.AdapterView<?> parent) {}
});
}
ArrayAdapter<?> adapter = (ArrayAdapter<?>) spScale.getAdapter();
if (adapter != null) {
float savedScale = prefs.getFloat("upscale_multiplier", 1.0f);
int scaleIndex = Math.max(0, Math.min(adapter.getCount() - 1, Math.round(savedScale) - 1));
spScale.setSelection(scaleIndex);
}
}
// Blending Accuracy spinner - setup like QuickActions
Spinner spBlending = header.findViewById(R.id.drawer_sp_blending_accuracy);
if (spBlending != null) {
if (spBlending.getAdapter() == null) {
ArrayAdapter<CharSequence> blendAdapter = ArrayAdapter.createFromResource(this,
R.array.blending_accuracy_entries, android.R.layout.simple_spinner_item);
blendAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spBlending.setAdapter(blendAdapter);
spBlending.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() {
@Override public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) {
prefs.edit().putInt("blending_accuracy", position).apply();
try { NativeApp.setBlendingAccuracy(position); } catch (Throwable ignored) {}
}
@Override public void onNothingSelected(android.widget.AdapterView<?> parent) {}
});
}
ArrayAdapter<?> adapter = (ArrayAdapter<?>) spBlending.getAdapter();
if (adapter != null) {
int savedBlend = prefs.getInt("blending_accuracy", 1);
if (savedBlend < 0 || savedBlend >= adapter.getCount()) savedBlend = 1;
spBlending.setSelection(savedBlend);
}
}
// Setup switch listeners only once
setupDrawerSwitchListeners(header, prefs);
}
private void setupDrawerSwitchListeners(View header, SharedPreferences prefs) {
// Widescreen Patches switch
com.google.android.material.materialswitch.MaterialSwitch swWide = header.findViewById(R.id.drawer_sw_widescreen);
if (swWide != null && swWide.getTag() == null) {
swWide.setTag("setup"); // Mark as setup to avoid duplicate listeners
swWide.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean("widescreen_patches", isChecked).apply();
try { NativeApp.setWidescreenPatches(isChecked); } catch (Throwable ignored) {}
});
}
// No Interlacing switch
com.google.android.material.materialswitch.MaterialSwitch swNoInt = header.findViewById(R.id.drawer_sw_no_interlacing);
if (swNoInt != null && swNoInt.getTag() == null) {
swNoInt.setTag("setup");
swNoInt.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean("no_interlacing_patches", isChecked).apply();
try { NativeApp.setNoInterlacingPatches(isChecked); } catch (Throwable ignored) {}
});
}
// Load Textures switch
com.google.android.material.materialswitch.MaterialSwitch swLoadTex = header.findViewById(R.id.drawer_sw_load_textures);
if (swLoadTex != null && swLoadTex.getTag() == null) {
swLoadTex.setTag("setup");
swLoadTex.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean("load_textures", isChecked).apply();
try { NativeApp.setLoadTextures(isChecked); } catch (Throwable ignored) {}
});
}
// Async Textures switch
com.google.android.material.materialswitch.MaterialSwitch swAsyncTex = header.findViewById(R.id.drawer_sw_async_textures);
if (swAsyncTex != null && swAsyncTex.getTag() == null) {
swAsyncTex.setTag("setup");
swAsyncTex.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean("async_texture_loading", isChecked).apply();
try { NativeApp.setAsyncTextureLoading(isChecked); } catch (Throwable ignored) {}
});
}
// Precache Textures switch
com.google.android.material.materialswitch.MaterialSwitch swPrecache = header.findViewById(R.id.drawer_sw_precache_textures);
if (swPrecache != null && swPrecache.getTag() == null) {
swPrecache.setTag("setup");
swPrecache.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean("precache_textures", isChecked).apply();
try { NativeApp.setPrecacheTextureReplacements(isChecked); } catch (Throwable ignored) {}
});
}
// HUD Developer switch
com.google.android.material.materialswitch.MaterialSwitch swDevHud = header.findViewById(R.id.drawer_sw_dev_hud);
if (swDevHud != null && swDevHud.getTag() == null) {
swDevHud.setTag("setup");
swDevHud.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefs.edit().putBoolean("hud_visible", isChecked).apply();
try { NativeApp.setHudVisible(isChecked); } catch (Throwable ignored) {}
});
}
}
private void refreshDrawerSettings() {
try {
NavigationView nav = findViewById(R.id.nav_view);
if (nav != null && nav.getHeaderCount() > 0) {
View header = nav.getHeaderView(0);
if (header != null) {
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
// Refresh switch states only (spinners are set up once in setupDrawerSettings)
com.google.android.material.materialswitch.MaterialSwitch swWide = header.findViewById(R.id.drawer_sw_widescreen);
if (swWide != null) {
swWide.setChecked(prefs.getBoolean("widescreen_patches", true));
}
com.google.android.material.materialswitch.MaterialSwitch swNoInt = header.findViewById(R.id.drawer_sw_no_interlacing);
if (swNoInt != null) {
swNoInt.setChecked(prefs.getBoolean("no_interlacing_patches", true));
}
com.google.android.material.materialswitch.MaterialSwitch swLoadTex = header.findViewById(R.id.drawer_sw_load_textures);
if (swLoadTex != null) {
swLoadTex.setChecked(prefs.getBoolean("load_textures", false));
}
com.google.android.material.materialswitch.MaterialSwitch swAsyncTex = header.findViewById(R.id.drawer_sw_async_textures);
if (swAsyncTex != null) {
swAsyncTex.setChecked(prefs.getBoolean("async_texture_loading", true));
}
com.google.android.material.materialswitch.MaterialSwitch swPrecache = header.findViewById(R.id.drawer_sw_precache_textures);
if (swPrecache != null) {
swPrecache.setChecked(prefs.getBoolean("precache_textures", false));
}
com.google.android.material.materialswitch.MaterialSwitch swDevHud = header.findViewById(R.id.drawer_sw_dev_hud);
if (swDevHud != null) {
swDevHud.setChecked(prefs.getBoolean("hud_visible", false));
}
}
}
} catch (Throwable ignored) {}
}
private void loadAndApplyStoredSettings() {
try {
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
// Apply aspect ratio setting
int aspectRatio = prefs.getInt("aspect_ratio", 1);
NativeApp.setAspectRatio(aspectRatio);
// Apply blending accuracy setting
int blendingAccuracy = prefs.getInt("blending_accuracy", 1);
NativeApp.setBlendingAccuracy(blendingAccuracy);
// Apply other settings
boolean widescreenPatches = prefs.getBoolean("widescreen_patches", true);
NativeApp.setWidescreenPatches(widescreenPatches);
boolean noInterlacing = prefs.getBoolean("no_interlacing_patches", true);
NativeApp.setNoInterlacingPatches(noInterlacing);
boolean loadTextures = prefs.getBoolean("load_textures", false);
NativeApp.setLoadTextures(loadTextures);
boolean asyncTextures = prefs.getBoolean("async_texture_loading", true);
NativeApp.setAsyncTextureLoading(asyncTextures);
boolean precacheTextures = prefs.getBoolean("precache_textures", false);
NativeApp.setPrecacheTextureReplacements(precacheTextures);
// Apply renderer setting
int renderer = prefs.getInt("renderer", -1);
NativeApp.renderGpu(renderer);
} catch (Throwable ignored) {}
}
private void showControllerHintIfNeeded() {
// Prevent multiple dialogs from showing
if (mControllerHintShowing) return;
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
boolean hasShownHint = prefs.getBoolean("controller_hint_shown", false);
// Only show the hint once, and only if setup is complete
if (!hasShownHint && prefs.getBoolean("first_run_done", false)) {
mControllerHintShowing = true;
// Small delay to avoid showing immediately on startup
findViewById(android.R.id.content).postDelayed(() -> {
if (!isFinishing() && !mSetupWizardActive && mControllerHintShowing) {
showControllerHintDialog();
}
}, 2000); // 2 second delay
}
}
private void showControllerHintDialog() {
// Double-check to prevent multiple dialogs
if (!mControllerHintShowing) return;
View dialogView = getLayoutInflater().inflate(R.layout.dialog_controller_hint, null);
com.google.android.material.materialswitch.MaterialSwitch swDontShow = dialogView.findViewById(R.id.sw_dont_show_again);
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this,
com.google.android.material.R.style.ThemeOverlay_Material3_MaterialAlertDialog)
.setCustomTitle(UiUtils.centeredDialogTitle(this, "Controller Tip"))
.setView(dialogView)
.setPositiveButton("Got it!", (dialog, which) -> {
SharedPreferences prefs = getSharedPreferences("app_prefs", MODE_PRIVATE);
if (swDontShow != null && swDontShow.isChecked()) {
prefs.edit().putBoolean("controller_hint_shown", true).apply();
}
mControllerHintShowing = false;
dialog.dismiss();
})
.setCancelable(true)
.setOnDismissListener(dialog -> {
mControllerHintShowing = false;
});
AlertDialog dialog = builder.create();
dialog.show();
}
// Pause/Play toggle functionality
public void togglePauseState() {
try {
boolean isPaused = NativeApp.isPaused();
if (isPaused) {
NativeApp.resume();
} else {
NativeApp.pause();
}
updatePausePlayButton();
} catch (Throwable ignored) {}
}
private void updatePausePlayButton() {
MaterialButton btn_pause_play = findViewById(R.id.btn_pause_play);
if (btn_pause_play != null) {
try {
boolean isPaused = NativeApp.isPaused();
boolean hasGame = hasSelectedGame() && isThread();
// Show button only when a game is running
btn_pause_play.setVisibility(hasGame ? View.VISIBLE : View.GONE);
if (hasGame) {
// Update icon to show the action it will perform
if (isPaused) {
// Game is paused, show pause icon (will pause more/stay paused)
btn_pause_play.setIcon(ContextCompat.getDrawable(this, R.drawable.pause_circle_24px));
} else {
// Game is running, show play icon (will keep playing)
btn_pause_play.setIcon(ContextCompat.getDrawable(this, R.drawable.play_circle_24px));
}
}
} catch (Throwable ignored) {}
}
}
// Call this method when game starts/stops to update button visibility
public void updateGameState() {
updatePausePlayButton();
}
}
@@ -159,6 +159,7 @@ public class NativeApp {
public static native void pause();
public static native void resume();
public static native boolean isPaused();
public static native void shutdown();
public static native boolean saveStateToSlot(int slot);
@@ -10,6 +10,7 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.materialswitch.MaterialSwitch;
import android.widget.Spinner;
@@ -22,13 +23,14 @@ public class QuickActionsDialogFragment extends DialogFragment {
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
View view = getLayoutInflater().inflate(R.layout.dialog_quick_actions, null, false);
MaterialButton btnPower = view.findViewById(R.id.btn_quick_power);
MaterialButton btnReboot = view.findViewById(R.id.btn_quick_reboot);
FloatingActionButton btnPower = view.findViewById(R.id.btn_quick_power);
FloatingActionButton btnReboot = view.findViewById(R.id.btn_quick_reboot);
com.google.android.material.button.MaterialButtonToggleGroup tgRenderer = view.findViewById(R.id.qa_tg_renderer);
View tbAt = view.findViewById(R.id.qa_tb_at);
View tbVk = view.findViewById(R.id.qa_tb_vk);
View tbGl = view.findViewById(R.id.qa_tb_gl);
View tbSw = view.findViewById(R.id.qa_tb_sw);
FloatingActionButton btnPausePlay = view.findViewById(R.id.btn_quick_pause_play);
MaterialButton btnGames = view.findViewById(R.id.btn_quick_games);
MaterialButton btnSaves = view.findViewById(R.id.btn_quick_saves);
MaterialButton btnCancel = view.findViewById(R.id.btn_cancel);
@@ -51,6 +53,26 @@ public class QuickActionsDialogFragment extends DialogFragment {
.show());
}
// Pause/Play toggle
if (btnPausePlay != null) {
try {
boolean isPaused = NativeApp.isPaused();
btnPausePlay.setImageResource(R.drawable.play_pause_24px);
} catch (Throwable ignored) {}
btnPausePlay.setOnClickListener(v -> {
try {
if (requireActivity() instanceof MainActivity) {
((MainActivity) requireActivity()).togglePauseState();
} else {
boolean paused = NativeApp.isPaused();
if (paused) NativeApp.resume(); else NativeApp.pause();
}
boolean isPausedNow = NativeApp.isPaused();
btnPausePlay.setImageResource(R.drawable.play_pause_24px);
} catch (Throwable ignored) {}
});
}
// Reboot game
if (btnReboot != null) {
btnReboot.setOnClickListener(v -> new MaterialAlertDialogBuilder(requireContext(),
@@ -208,3 +230,5 @@ public class QuickActionsDialogFragment extends DialogFragment {
System.exit(0);
}
}
@@ -56,12 +56,16 @@ public class SetupWizardDialogFragment extends DialogFragment {
int pad = (int)(24 * getResources().getDisplayMetrics().density);
root.setPadding(pad, pad, pad, pad);
root.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
// Use theme background
root.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.md_theme_surface));
titleView = new TextView(requireContext());
titleView.setText("Welcome! Let's set up PSX2");
titleView.setTextSize(22f);
titleView.setTextSize(26f);
titleView.setGravity(Gravity.CENTER_HORIZONTAL);
titleView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
titleView.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_theme_tertiaryFixedDim));
titleView.setTypeface(titleView.getTypeface(), android.graphics.Typeface.BOLD);
root.addView(titleView);
// Subtitle removed; using inline hint near the Done button instead.
@@ -144,7 +148,8 @@ public class SetupWizardDialogFragment extends DialogFragment {
hintView.setTextSize(14f);
hintView.setGravity(Gravity.CENTER_HORIZONTAL);
hintView.setTextAlignment(View.TEXT_ALIGNMENT_CENTER);
hintView.setAlpha(0.8f);
hintView.setTextColor(ContextCompat.getColor(requireContext(), R.color.brand_primary));
hintView.setAlpha(0.9f);
LinearLayout.LayoutParams hintLp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
hintLp.topMargin = (int)(8 * getResources().getDisplayMetrics().density);
hintView.setLayoutParams(hintLp);
@@ -199,11 +204,37 @@ public class SetupWizardDialogFragment extends DialogFragment {
btnGames.setIcon(step2 ? ContextCompat.getDrawable(requireContext(), R.drawable.check_circle_24px) : null);
btnBios.setIcon(step3 ? ContextCompat.getDrawable(requireContext(), R.drawable.check_circle_24px) : null);
// Add theme accent to completed buttons
int themeAccent = ContextCompat.getColor(requireContext(), R.color.md_theme_tertiary);
int defaultColor = ContextCompat.getColor(requireContext(), R.color.md_theme_onSurface);
btnData.setIconTint(step1 ? android.content.res.ColorStateList.valueOf(themeAccent) : null);
btnGames.setIconTint(step2 ? android.content.res.ColorStateList.valueOf(themeAccent) : null);
btnBios.setIconTint(step3 ? android.content.res.ColorStateList.valueOf(themeAccent) : null);
btnGames.setEnabled(step1);
btnBios.setEnabled(step1 && step2);
boolean doneEnabled = (step1 && step2 && step3);
btnDone.setEnabled(doneEnabled);
if (hintView != null) hintView.setVisibility(doneEnabled ? View.GONE : View.VISIBLE);
// Style the Done button when ready
if (doneEnabled) {
btnDone.setBackgroundTintList(android.content.res.ColorStateList.valueOf(themeAccent));
btnDone.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_theme_onTertiary));
if (hintView != null) {
hintView.setText("🎉 Ready to go! Tap Done to start.");
hintView.setTextColor(ContextCompat.getColor(requireContext(), R.color.md_theme_tertiaryFixedDim));
}
} else {
btnDone.setBackgroundTintList(null);
btnDone.setTextColor(defaultColor);
if (hintView != null) {
hintView.setText("Complete all steps to finish.");
hintView.setTextColor(ContextCompat.getColor(requireContext(), R.color.brand_primary));
}
}
if (hintView != null) hintView.setVisibility(View.VISIBLE);
}
}
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M360,640L440,640L440,320L360,320L360,640ZM520,640L600,640L600,320L520,320L520,640ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M380,660L660,480L380,300L380,660ZM480,880Q397,880 324,848.5Q251,817 197,763Q143,709 111.5,636Q80,563 80,480Q80,397 111.5,324Q143,251 197,197Q251,143 324,111.5Q397,80 480,80Q563,80 636,111.5Q709,143 763,197Q817,251 848.5,324Q880,397 880,480Q880,563 848.5,636Q817,709 763,763Q709,817 636,848.5Q563,880 480,880ZM480,800Q614,800 707,707Q800,614 800,480Q800,346 707,253Q614,160 480,160Q346,160 253,253Q160,346 160,480Q160,614 253,707Q346,800 480,800ZM480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Q480,480 480,480Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M200,648L200,312L440,480L200,648ZM520,640L520,320L600,320L600,640L520,640ZM680,640L680,320L760,320L760,640L680,640Z"/>
</vector>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Selected/Checked state -->
<item android:state_checked="true" android:color="@color/toggle_button_selected" />
<!-- Default/Unselected state -->
<item android:color="@color/toggle_button_unselected" />
</selector>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Selected/Checked state - neon purple text -->
<item android:state_checked="true" android:color="@color/md_theme_tertiary" />
<!-- Default/Unselected state - neon blue text -->
<item android:color="@color/brand_primary" />
</selector>
@@ -39,6 +39,29 @@
app:layout_constraintStart_toStartOf="parent"
android:layout_margin="12dp"/>
<!-- Pause/Play button in top right corner -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_pause_play"
style="@style/PSX2.ElevatedTransparentButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:insetLeft="0dp"
android:insetRight="0dp"
android:insetTop="0dp"
android:insetBottom="0dp"
android:gravity="center"
android:text=""
app:icon="@drawable/play_circle_24px"
app:iconTint="@color/brand_primary"
app:iconGravity="textStart"
app:iconSize="24dp"
app:iconPadding="0dp"
app:rippleColor="#80cdcdcd"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="12dp"
android:visibility="gone"/>
<!-- Visibility toggle removed -->
<!-- Top center menu row -->
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/dlg_drawer_layout"
@@ -63,16 +63,7 @@
app:iconPadding="8dp"
app:iconSize="24dp" />
</LinearLayout>
<!-- Letters row above covers -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_letters"
android:layout_width="match_parent"
android:layout_height="24dp"
android:layout_marginTop="0dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:clipToPadding="false"
android:overScrollMode="never"/>
<!-- Letters row removed -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_covers"
@@ -137,3 +128,4 @@
app:headerLayout="@layout/drawer_header_settings" />
</androidx.drawerlayout.widget.DrawerLayout>
@@ -39,6 +39,29 @@
app:layout_constraintStart_toStartOf="parent"
android:layout_margin="12dp"/>
<!-- Pause/Play button in top right corner -->
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_pause_play"
style="@style/PSX2.ElevatedTransparentButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:insetLeft="0dp"
android:insetRight="0dp"
android:insetTop="0dp"
android:insetBottom="0dp"
android:gravity="center"
android:text=""
app:icon="@drawable/play_circle_24px"
app:iconTint="@color/brand_primary"
app:iconGravity="textStart"
app:iconSize="24dp"
app:iconPadding="0dp"
app:rippleColor="#80cdcdcd"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_margin="12dp"
android:visibility="gone"/>
<!-- Visibility toggle removed -->
<LinearLayout
@@ -63,16 +63,7 @@
app:iconPadding="8dp"
app:iconSize="24dp" />
</LinearLayout>
<!-- Letters row above covers -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_letters"
android:layout_width="match_parent"
android:layout_height="56dp"
android:layout_marginTop="0dp"
android:paddingStart="24dp"
android:paddingEnd="24dp"
android:clipToPadding="false"
android:overScrollMode="never"/>
<!-- Letters row removed -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_covers"
@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp">
<ImageView
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:layout_marginBottom="16dp"
android:src="@drawable/stadia_controller_24px"
android:tint="?attr/colorPrimary" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Controller Quick Actions"
android:textSize="18sp"
android:textStyle="bold"
android:gravity="center"
android:layout_marginBottom="12dp"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="When using a controller, press Select + Start together to open the Quick Actions menu for easy access to settings and features."
android:textSize="14sp"
android:gravity="center"
android:layout_marginBottom="16dp"
android:lineSpacingExtra="2dp"
android:textColor="?android:attr/textColorSecondary" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="16dp"
android:background="?attr/colorSurfaceVariant"
android:padding="12dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SELECT"
android:textSize="12sp"
android:textStyle="bold"
android:background="?attr/colorPrimary"
android:textColor="?attr/colorOnPrimary"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:layout_marginEnd="8dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="+"
android:textSize="16sp"
android:textStyle="bold"
android:layout_marginEnd="8dp"
android:textColor="?android:attr/textColorPrimary" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="START"
android:textSize="12sp"
android:textStyle="bold"
android:background="?attr/colorPrimary"
android:textColor="?attr/colorOnPrimary"
android:paddingStart="8dp"
android:paddingEnd="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp" />
</LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/sw_dont_show_again"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Don't show this tip again"
android:textSize="14sp" />
</LinearLayout>
@@ -15,11 +15,13 @@
<!-- Game Title -->
<TextView
android:id="@+id/tv_game_title"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Game Title"
android:textStyle="bold"
android:textSize="18sp"
android:textColor="@android:color/white"
android:gravity="center"
android:layout_marginBottom="4dp"/>
<!-- Subtitle -->
@@ -28,7 +30,7 @@
android:layout_height="wrap_content"
android:text="Per-Game Settings (Long-press from game list)"
android:textSize="12sp"
android:textColor="#666666"
android:textColor="@color/md_theme_tertiaryFixedDim"
android:layout_marginBottom="4dp"/>
<!-- Game Serial/CRC -->
@@ -38,7 +40,7 @@
android:layout_height="wrap_content"
android:text="Serial: SLUS-12345 | CRC: 12345678"
android:textSize="12sp"
android:textColor="#888888"
android:textColor="@color/md_theme_tertiaryFixedDim"
android:layout_marginBottom="16dp"/>
<TextView
@@ -46,7 +48,7 @@
android:layout_height="wrap_content"
android:text="These settings will override global settings for this game only."
android:textSize="14sp"
android:textColor="#666666"
android:textColor="@color/md_theme_tertiaryFixedDim"
android:layout_marginBottom="8dp"/>
@@ -58,13 +60,29 @@
android:text="Graphics"
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/brand_primary"
android:paddingBottom="8dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Aspect Ratio"
android:textSize="14sp"
android:textColor="@color/brand_primary"
android:paddingBottom="4dp"/>
<Spinner
android:id="@+id/sp_aspect_ratio"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Blending Accuracy"
android:textSize="14sp"
android:textColor="@color/brand_primary"
android:paddingBottom="4dp"/>
<Spinner
@@ -78,6 +96,7 @@
android:layout_height="wrap_content"
android:text="Renderer"
android:textSize="14sp"
android:textColor="@color/brand_primary"
android:paddingBottom="4dp"/>
<Spinner
@@ -91,6 +110,7 @@
android:layout_height="wrap_content"
android:text="Resolution"
android:textSize="14sp"
android:textColor="@color/brand_primary"
android:paddingBottom="4dp"/>
<Spinner
@@ -106,6 +126,7 @@
android:text="Game Patches"
android:textStyle="bold"
android:textSize="16sp"
android:textColor="@color/brand_primary"
android:paddingBottom="8dp"/>
<com.google.android.material.materialswitch.MaterialSwitch
@@ -113,6 +134,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Widescreen Patches"
android:textColor="@android:color/white"
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
android:layout_marginBottom="8dp"/>
@@ -121,6 +143,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="No Interlacing Patches"
android:textColor="@android:color/white"
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
android:layout_marginBottom="8dp"/>
@@ -139,6 +162,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enable Cheats"
android:textColor="@android:color/white"
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
android:layout_marginBottom="16dp"/>
@@ -148,6 +172,7 @@
android:layout_height="wrap_content"
android:text="Texture Packs"
android:textSize="14sp"
android:textColor="@color/brand_primary"
android:paddingBottom="4dp"/>
<com.google.android.material.materialswitch.MaterialSwitch
@@ -155,6 +180,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Load Texture Replacements"
android:textColor="@android:color/white"
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
android:layout_marginBottom="8dp"/>
@@ -163,6 +189,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Async Texture Loading"
android:textColor="@android:color/white"
style="@style/Widget.Material3.CompoundButton.MaterialSwitch"
android:layout_marginBottom="16dp"/>
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -13,40 +18,41 @@
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="16dp"
android:textColor="?android:attr/textColorPrimary" />
android:textColor="@color/brand_primary" />
<!-- Row 1: Power + Reboot -->
<!-- Row 1: Pause/Play + Power + Reboot (icons only, centered) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginBottom="8dp">
<com.google.android.material.button.MaterialButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_quick_pause_play"
style="?attr/floatingActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="Pause/Play"
app:srcCompat="@drawable/play_pause_24px"
android:layout_marginEnd="12dp" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_quick_power"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
style="?attr/floatingActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Power"
android:textAllCaps="false"
android:drawableStart="@drawable/ic_power"
android:drawablePadding="8dp" />
android:contentDescription="Power"
app:srcCompat="@drawable/ic_power"
android:layout_marginEnd="12dp" />
<View
android:layout_width="8dp"
android:layout_height="0dp" />
<com.google.android.material.button.MaterialButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_quick_reboot"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
style="?attr/floatingActionButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Reboot"
android:textAllCaps="false"
android:drawableStart="@drawable/ic_reboot"
android:drawablePadding="8dp" />
android:contentDescription="Reboot"
app:srcCompat="@drawable/ic_reboot" />
</LinearLayout>
<!-- Row 2: Renderer toggle buttons -->
@@ -109,8 +115,10 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="GAMES"
android:textColor="@android:color/white"
android:textAllCaps="true"
android:drawableStart="@drawable/stadia_controller_24px"
android:drawableTint="@color/brand_primary"
android:drawablePadding="8dp" />
<View
@@ -124,8 +132,10 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SAVES"
android:textColor="@android:color/white"
android:textAllCaps="true"
android:drawableStart="@drawable/save_24px"
android:drawableTint="@color/brand_primary"
android:drawablePadding="8dp" />
</LinearLayout>
@@ -135,6 +145,7 @@
android:layout_height="wrap_content"
android:text="Aspect Ratio"
android:textStyle="bold"
android:textColor="@color/brand_primary"
android:layout_marginBottom="4dp"/>
<Spinner
@@ -149,6 +160,7 @@
android:layout_height="wrap_content"
android:text="Blending Accuracy"
android:textStyle="bold"
android:textColor="@color/brand_primary"
android:layout_marginBottom="4dp"/>
<Spinner
@@ -225,4 +237,7 @@
android:text="Cancel"
android:textAllCaps="false" />
</LinearLayout>
</LinearLayout>
</ScrollView>

Some files were not shown because too many files have changed in this diff Show More