diff --git a/android-llmservice/app/src/main/kotlin/net/ladenthin/android/llmservice/MainActivity.kt b/android-llmservice/app/src/main/kotlin/net/ladenthin/android/llmservice/MainActivity.kt index 84add02f..d49d84b3 100644 --- a/android-llmservice/app/src/main/kotlin/net/ladenthin/android/llmservice/MainActivity.kt +++ b/android-llmservice/app/src/main/kotlin/net/ladenthin/android/llmservice/MainActivity.kt @@ -11,6 +11,7 @@ import android.provider.OpenableColumns import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity @@ -25,6 +26,7 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -84,6 +86,10 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + // Edge-to-edge is forced on Android 15 (targetSdk 35); enable it explicitly on every version + // so window-inset handling is consistent. The bottom log strip then pads itself above the + // system navigation bar (see LogStrip) instead of being drawn behind it. + enableEdgeToEdge() // Test / automation hook: preload a model straight from an absolute path, bypassing the // interactive SAF picker (a separate system process the UI test can't drive). The shipping @@ -233,8 +239,11 @@ private fun LogStrip(viewModel: ChatViewModel, onOpen: () -> Unit) { val log by viewModel.log.collectAsStateWithLifecycle() val last = log.lastOrNull() ?: stringResource(R.string.log_empty) Surface(tonalElevation = 2.dp) { + // navigationBarsPadding keeps the strip's content above the system nav bar (back / home / + // recents) under edge-to-edge; the Surface background still fills to the screen edge behind it. Row( modifier = Modifier.fillMaxWidth().clickable(onClick = onOpen).testTag("logStrip") + .navigationBarsPadding() .padding(horizontal = 12.dp, vertical = 6.dp), verticalAlignment = Alignment.CenterVertically, ) { diff --git a/android-llmservice/requirements.md b/android-llmservice/requirements.md index 5edf3b19..ae6edca8 100644 --- a/android-llmservice/requirements.md +++ b/android-llmservice/requirements.md @@ -94,7 +94,7 @@ by hand only (no automated test); `build` = enforced at build/resource-compile t | ID | Requirement | Source | Verified by | |---|---|---|---| -| R7.1 | An **always-visible one-line log strip** sits at the very bottom (🧾, small font, may wrap to 2 display lines) showing the newest log line, or an empty-state string. | `MainActivity.LogStrip` | manual | +| R7.1 | An **always-visible one-line log strip** sits at the very bottom (🧾, small font, may wrap to 2 display lines) showing the newest log line, or an empty-state string. Under edge-to-edge (`enableEdgeToEdge`) its content is padded **above the system navigation bar** (`navigationBarsPadding`) so it is never covered by the back/home/recents bar. | `MainActivity.onCreate`; `MainActivity.LogStrip` | manual | | R7.2 | Tapping the strip opens a **full-screen log viewer** with **Copy all**, **Save as…** (writes a `.txt` via SAF `CreateDocument`), and **Clear**; ✕ (top-right) closes it. | `MainActivity.LogDialog` | manual | | R7.3 | The log is a **rolling buffer capped at 500 lines**, each entry prefixed with a local wall-clock time. | `ChatViewModel.log` (`MAX_LOG_LINES`) | manual | | R7.4 | Logged events include: model loading/ready/load-failure, generation start (with the effective sampling knobs), reply-complete (char count), generation-stopped, generation-failure, regenerate, chat-cleared, settings-reset, and session save/load. | `ChatViewModel` (`log(...)` call sites) | manual |