mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-15 23:40:29 +02:00
Merge pull request #22511 from hrydgard/android-inputbox-fixes
Claude code review: Android inputbox fixes
This commit is contained in:
11 files changed
+247
-90
No files matched your search
@@ -23,6 +23,11 @@ public class ImageResultProxyActivity extends AppCompatActivity {
|
||||
|
||||
private String copyAndDownscaleToCache(Uri selectedImage) {
|
||||
Log.i(TAG, "Selected image: " + selectedImage);
|
||||
if (selectedImage == null) {
|
||||
// Some pickers only return a ClipData.
|
||||
Log.e(TAG, "No image URI in the result");
|
||||
return null;
|
||||
}
|
||||
File cacheDir = getExternalCacheDir();
|
||||
if (cacheDir == null) {
|
||||
Log.e(TAG, "External cache directory not available");
|
||||
@@ -104,7 +109,9 @@ public class ImageResultProxyActivity extends AppCompatActivity {
|
||||
String returnPath = tempFile.getAbsolutePath();
|
||||
Log.i(TAG, "Return path: " + returnPath);
|
||||
return returnPath;
|
||||
} catch (IOException e) {
|
||||
} catch (IOException | RuntimeException | OutOfMemoryError e) {
|
||||
// We're in the app's process, so anything uncaught here (SecurityException from a provider, a huge bitmap)
|
||||
// takes the whole app down.
|
||||
Log.e(TAG, "Error processing image: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
|
||||
@@ -125,8 +125,8 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
private final ArrayList<InputDeviceState> inputPlayers = new ArrayList<>();
|
||||
|
||||
private PowerSaveModeReceiver mPowerSaveModeReceiver = null;
|
||||
// The open input box, if any.
|
||||
private AlertDialog inputDialog = null;
|
||||
// The open input boxes. There can be two, if a game's OSK pops up over a UI one.
|
||||
private final ArrayList<AlertDialog> inputDialogs = new ArrayList<>();
|
||||
private NativeSurfaceManager surfaceManager = null;
|
||||
private static LocationHelper mLocationHelper;
|
||||
private static InfraredHelper mInfraredHelper;
|
||||
@@ -999,10 +999,10 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
mPowerSaveModeReceiver = null;
|
||||
}
|
||||
// Otherwise its window leaks, and its request is never answered. Dismissing answers it as cancelled.
|
||||
if (inputDialog != null) {
|
||||
inputDialog.dismiss();
|
||||
inputDialog = null;
|
||||
for (AlertDialog dlg : new ArrayList<>(inputDialogs)) {
|
||||
dlg.dismiss();
|
||||
}
|
||||
inputDialogs.clear();
|
||||
|
||||
// After finish(), Android is free to deliver onDestroy late, after it has already created and resumed a
|
||||
// new activity if the user was quick to relaunch. That one found initialized still set and adopted the
|
||||
@@ -1480,7 +1480,7 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
}
|
||||
|
||||
// The return value is sent to C++ via requestID.
|
||||
public void inputBox(final int requestId, final String title, String defaultText, String defaultAction) {
|
||||
public void inputBox(final int requestId, final String title, String defaultText, boolean passwordMasking, String defaultAction) {
|
||||
final FrameLayout fl = new FrameLayout(this);
|
||||
final EditText input = new EditText(this);
|
||||
input.setGravity(Gravity.CENTER);
|
||||
@@ -1489,7 +1489,11 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
editBoxLayout.setMargins(2, 20, 2, 20);
|
||||
fl.addView(input, editBoxLayout);
|
||||
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
if (passwordMasking) {
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||
} else {
|
||||
input.setInputType(InputType.TYPE_CLASS_TEXT);
|
||||
}
|
||||
input.setImeOptions(EditorInfo.IME_ACTION_DONE);
|
||||
input.setText(defaultText);
|
||||
input.setFocusableInTouchMode(true);
|
||||
@@ -1497,22 +1501,25 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
input.selectAll();
|
||||
//input.setSelection(input.getText().length());
|
||||
|
||||
// Answered exactly once: OK and Done are followed by a dismiss.
|
||||
final boolean[] answered = { false };
|
||||
AlertDialog.Builder builder = createDialogBuilder()
|
||||
.setView(fl)
|
||||
.setTitle(title)
|
||||
.setPositiveButton(defaultAction, (d, which) -> {
|
||||
Log.i(TAG, "input box successful");
|
||||
answered[0] = true;
|
||||
NativeApp.sendRequestResult(requestId, true, input.getText().toString(), 0);
|
||||
// Dismiss happens automatically.
|
||||
})
|
||||
.setNegativeButton("Cancel", (d, which) -> d.cancel());
|
||||
builder.setOnDismissListener( d -> {
|
||||
Log.i(TAG, "input box dismissed");
|
||||
// This will be ignored if we already sent a success.
|
||||
NativeApp.sendRequestResult(requestId, false, "", 0);
|
||||
if (inputDialog == d) {
|
||||
inputDialog = null;
|
||||
if (!answered[0]) {
|
||||
answered[0] = true;
|
||||
NativeApp.sendRequestResult(requestId, false, "", 0);
|
||||
}
|
||||
inputDialogs.remove(d);
|
||||
// Also comes after onDestroy dismissed it.
|
||||
if (!isDestroyed()) {
|
||||
updateSystemUiVisibility();
|
||||
@@ -1523,7 +1530,10 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
input.setOnEditorActionListener((v, actionId, event) -> {
|
||||
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
Log.i(TAG, "input box successful via Keyboard Done");
|
||||
NativeApp.sendRequestResult(requestId, true, input.getText().toString(), 0);
|
||||
if (!answered[0]) {
|
||||
answered[0] = true;
|
||||
NativeApp.sendRequestResult(requestId, true, input.getText().toString(), 0);
|
||||
}
|
||||
|
||||
// We must dismiss the dialog manually here
|
||||
dlg.dismiss();
|
||||
@@ -1538,10 +1548,15 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
}
|
||||
try {
|
||||
dlg.show();
|
||||
inputDialog = dlg;
|
||||
inputDialogs.add(dlg);
|
||||
input.requestFocus();
|
||||
} catch (Exception e) {
|
||||
NativeApp.reportException(e, "AlertDialog");
|
||||
// The dismiss listener won't run, and the native side (maybe a game's OSK) waits for an answer.
|
||||
if (!answered[0]) {
|
||||
answered[0] = true;
|
||||
NativeApp.sendRequestResult(requestId, false, "", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1694,12 +1709,13 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
} else if (command.equals("inputbox")) {
|
||||
String title = "Input";
|
||||
String defString = "";
|
||||
String[] param = params.split(":@:", 3);
|
||||
String[] param = params.split(":@:", 4);
|
||||
int requestID = Integer.parseInt(param[0]);
|
||||
if (param.length > 1 && !param[1].isEmpty())
|
||||
title = param[1];
|
||||
if (param.length > 2)
|
||||
defString = param[2];
|
||||
boolean passwordMasking = param.length > 1 && param[1].equals("1");
|
||||
if (param.length > 2 && !param[2].isEmpty())
|
||||
title = param[2];
|
||||
if (param.length > 3)
|
||||
defString = param[3];
|
||||
// Workaround for issue #13363 to fix Split/Second game start - it requires text input
|
||||
// but we don't support it on VR devices.
|
||||
if (isVRDevice()) {
|
||||
@@ -1707,8 +1723,9 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
NativeApp.sendRequestResult(requestID, false, defString, 0);
|
||||
return true;
|
||||
}
|
||||
Log.i(TAG, "Launching inputbox: #" + requestID + " " + title + " " + defString);
|
||||
inputBox(requestID, title, defString, "OK");
|
||||
// Don't log defString, it can be a password.
|
||||
Log.i(TAG, "Launching inputbox: #" + requestID + " " + title);
|
||||
inputBox(requestID, title, defString, passwordMasking, "OK");
|
||||
return true;
|
||||
} else if (command.equals("vibrate")) {
|
||||
int milliseconds = -1;
|
||||
@@ -2009,6 +2026,19 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
}
|
||||
}
|
||||
|
||||
// For commands that carry a native request ID (always first in the parameter), answers it as failed, since the
|
||||
// native side waits for an answer.
|
||||
private static void failRequestCommand(String command, String parameter) {
|
||||
if (!command.equals("inputbox") && !command.equals("browse_image") && !command.startsWith("browse_file") && !command.equals("browse_folder")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
NativeApp.sendRequestResult(Integer.parseInt(parameter.split(":@:", 2)[0]), false, "", 0);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Bad request ID for command '" + command + "'");
|
||||
}
|
||||
}
|
||||
|
||||
// called by the C++ code through JNI. Dispatch anything we can't directly handle
|
||||
// on the gfx thread to the UI thread.
|
||||
@Keep
|
||||
@@ -2018,18 +2048,13 @@ public class PpssppActivity extends AppCompatActivity implements SensorEventList
|
||||
// Posted before this activity was destroyed, but runs after.
|
||||
if (isDestroyed()) {
|
||||
Log.w(TAG, "Dropping command '" + command + "' for a destroyed activity");
|
||||
if (command.equals("inputbox")) {
|
||||
// The native side waits for an answer.
|
||||
try {
|
||||
NativeApp.sendRequestResult(Integer.parseInt(parameter.split(":@:", 2)[0]), false, "", 0);
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(TAG, "Bad inputbox parameter: " + parameter);
|
||||
}
|
||||
}
|
||||
failRequestCommand(command, parameter);
|
||||
return;
|
||||
}
|
||||
if (!processCommand(command, parameter)) {
|
||||
Log.e(TAG, "processCommand failed: cmd: '" + command + "' param: '" + parameter + "'");
|
||||
// Not logging the parameter, an inputbox one can contain a password.
|
||||
Log.e(TAG, "processCommand failed: cmd: '" + command + "'");
|
||||
failRequestCommand(command, parameter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user