mirror of
https://github.com/izzy2lost/PSX2.git
synced 2026-07-05 15:18:36 -07:00
lots of ui menu changes
This commit is contained in:
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user