1
0
forked from GRIN/grim

Federation, note modal, save-to-device, onboarding fixes + UI polish

- Configurable name authority (Settings → Identity → Name authority): bare
  names resolve there, own-domain names show bare, foreign verified names show
  'name · domain' with a check — no '@' anywhere. Lets bob@otherdomain pay
  alice@goblin.st. Home domain derived from the configured server.
- Note entry is now a modal that floats above the soft keyboard (dimmed
  backdrop) instead of an inline editor the keyboard covered.
- Backup export SAVES to a chosen location (Android CREATE_DOCUMENT / desktop
  save dialog) instead of opening the share sheet.
- Onboarding status-bar icons are legible again (white on the dark surface,
  not black); identity step is less wordy and drops the '@' prefix; claiming a
  name during onboarding now republishes kind 0 so it's visible immediately.
- App-open name re-verify sweep (persisted, runs if >78s since last).
- Advanced 'Manage node connection' opens GRIM's native Connections UI.
- Manual slatepack paste: removed the QR icon. Pay screen: bolder, bigger ツ.
- Localized new strings across 6 locales.
This commit is contained in:
2ro
2026-06-16 03:22:08 -04:00
parent dfbd85c7b3
commit 11033b93fe
19 changed files with 504 additions and 118 deletions
@@ -69,6 +69,9 @@ public class MainActivity extends GameActivity {
private ActivityResultLauncher<Intent> mFilePickResult = null;
private ActivityResultLauncher<Intent> mOpenFilePermissionsResult = null;
private ActivityResultLauncher<Intent> mFileSaveResult = null;
// Source path (in the share cache) staged by Rust for the next saveFile().
private String mPendingSavePath = null;
@SuppressLint("UnspecifiedRegisterReceiverFlag")
@Override
@@ -145,6 +148,32 @@ public class MainActivity extends GameActivity {
}
});
// Register file SAVE result (Storage Access Framework CREATE_DOCUMENT):
// copy the staged source file into the user-chosen document.
mFileSaveResult = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
String src = mPendingSavePath;
mPendingSavePath = null;
if (result.getResultCode() == Activity.RESULT_OK && src != null) {
Intent data = result.getData();
if (data != null && data.getData() != null) {
Uri uri = data.getData();
try (InputStream is = new FileInputStream(new File(src));
OutputStream os = getContentResolver().openOutputStream(uri)) {
byte[] buffer = new byte[4096];
int length;
while (is != null && (length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
if (os != null) os.flush();
} catch (Exception e) {
Log.e("grim", e.toString());
}
}
}
});
// Listener for display insets (cutouts) to pass values into native code.
View content = getWindow().getDecorView().findViewById(android.R.id.content);
ViewCompat.setOnApplyWindowInsetsListener(content, (v, insets) -> {
@@ -510,6 +539,23 @@ public class MainActivity extends GameActivity {
startActivity(Intent.createChooser(intent, "Share data"));
}
// Called from native code to SAVE a staged file to a user-chosen location.
// Launches the Storage Access Framework create-document picker; the result
// handler copies the staged source file into the chosen document.
public void saveFile(String path, String name) {
mPendingSavePath = path;
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/octet-stream");
intent.putExtra(Intent.EXTRA_TITLE, name);
try {
mFileSaveResult.launch(intent);
} catch (android.content.ActivityNotFoundException ex) {
Log.e("grim", ex.toString());
mPendingSavePath = null;
}
}
// Called from native code to share plain text (e.g. a payment link) via the
// system share sheet.
public void shareText(String text) {