Fix runtime diagnostics, rotation stability, and manpage search positioning

- Expand the missing-native-Git message with the selected ABI, 32/64-bit ABI lists,
  Android version, device model/platform details, expected binary path, and a request
  to report the information so device support can be added.
- Use application-scoped DataStore delegates for game progress and pane layout
  preferences to avoid duplicate DataStore instances during activity recreation, such
  as screen orientation changes.
- Center manpage search matches in the visible scroll viewport when possible and apply
  IME padding so keyboard-covered space is accounted for.
- Support mobile-friendly handling for interactive add/stage commands (`git add -i`,
  `git stage -i`, and `git add --interactive`) by routing them through GitHug's
  staging semantics instead of rejecting them or launching Git's terminal UI.
- Apply IME padding to editor dialogs so controls remain reachable while the keyboard
  is displayed.
This commit is contained in:
Joe Tretter
2026-05-09 14:22:07 -05:00
parent edec66bfe6
commit 0acf8c9ab0
10 changed files with 80 additions and 213 deletions

View File

@@ -58,12 +58,23 @@ class GitRepositoryRuntime private constructor(
fun isNativeGitAvailable(): Boolean = nativeGitBinary() != null
fun unavailableMessage(): String {
val selectedAbi = Build.SUPPORTED_ABIS.firstOrNull() ?: "unknown"
val nativeLibraryDir = context?.applicationInfo?.nativeLibraryDir ?: "unknown"
return buildString {
append("GitHug Android cannot start because this build does not include a native Git binary for this device ABI.")
append("\n\nSupported device ABIs: ")
append("\n\nCurrent device ABI: ")
append(selectedAbi)
append("\nSupported device ABIs: ")
append(Build.SUPPORTED_ABIS.joinToString(", ").ifBlank { "unknown" })
append("\nExpected binary: nativeLibraryDir/libgit.so")
append("\n\nInstall a build that bundles the cross-compiled Git binary for this device.")
append("\nSupported 64-bit ABIs: ")
append(Build.SUPPORTED_64_BIT_ABIS.joinToString(", ").ifBlank { "none" })
append("\nSupported 32-bit ABIs: ")
append(Build.SUPPORTED_32_BIT_ABIS.joinToString(", ").ifBlank { "none" })
append("\nPlatform: Android ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT})")
append("\nDevice: ${Build.MANUFACTURER} ${Build.MODEL} (${Build.DEVICE}; ${Build.HARDWARE})")
append("\nExpected binary: $nativeLibraryDir/libgit.so")
append("\n\nPlease report this information to the developer so support can be added for this device/platform.")
append("\nInstall a build that bundles the cross-compiled Git binary for this device.")
}
}
@@ -124,8 +135,6 @@ class GitRepositoryRuntime private constructor(
expandShellPathspecs(currentRepo, shellTokens)
}.normalizeGitStageAlias()
rejectUnsupportedInteractiveGitCommand(currentRepo, expandedTokens)?.let { return it }
executeSyntheticGitCommand(currentRepo, command, expandedTokens)?.let { return it }
val result = when (expandedTokens.first()) {
@@ -538,7 +547,7 @@ class GitRepositoryRuntime private constructor(
) to listOf("Cloned ${tokens[2]} into $target")
}
val shouldUseSandboxSemantics = when (gitCommand) {
"add" -> tokens.any { it == "-p" || it == "--patch" }
"add" -> tokens.any { it == "-p" || it == "--patch" || it == "-i" || it == "--interactive" }
"rebase" -> "-i" in tokens || "--onto" in tokens
"merge" -> "--squash" in tokens || tokens.lastOrNull() == "mybranch" || tokens.lastOrNull() == "feature"
"revert", "stash" -> true
@@ -561,19 +570,6 @@ class GitRepositoryRuntime private constructor(
}
}
private fun rejectUnsupportedInteractiveGitCommand(
currentRepo: RepoState,
tokens: List<String>,
): Pair<RepoState, List<String>>? {
if (tokens.firstOrNull() != "git") return null
val subcommand = tokens.getOrNull(1) ?: return null
val interactive = tokens.drop(2).any { it == "-i" || it == "--interactive" }
if (subcommand == "add" && interactive) {
return currentRepo to listOf("Interactive staging is not supported in the mobile terminal. Use git add <path> or git add -p <path>.")
}
return null
}
private fun executeEcho(workingDir: File, currentRepo: RepoState, tokens: List<String>): Pair<RepoState, List<String>> {
val redirectIndex = tokens.indexOfFirst { it == ">" || it == ">>" }
if (redirectIndex == -1 || redirectIndex == tokens.lastIndex) {