Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions android/app/src/main/java/cc/agentlabs/opencode/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package cc.agentlabs.opencode

import android.os.Build
import android.os.Bundle
import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat

import com.facebook.react.ReactActivity
import com.facebook.react.ReactActivityDelegate
Expand All @@ -17,6 +20,17 @@ class MainActivity : ReactActivity() {
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme);
super.onCreate(null)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
val root = findViewById<View>(android.R.id.content)
val bottom = root.paddingBottom
// Android 15+ no longer resizes edge-to-edge windows for the IME.
ViewCompat.setOnApplyWindowInsetsListener(root) { view, insets ->
view.setPadding(view.paddingLeft, view.paddingTop, view.paddingRight, bottom + insets.getInsets(WindowInsetsCompat.Type.ime()).bottom)
insets
}
ViewCompat.requestApplyInsets(root)
}
}

/**
Expand Down
1 change: 1 addition & 0 deletions app.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
},
"plugins": [
"expo-router",
"./plugins/withKeyboardInsets",
"expo-secure-store",
"expo-local-authentication",
[
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"android": "expo run:android",
"typecheck": "tsc --noEmit",
"check:versions": "node scripts/check-version-parity.mjs",
"test": "node --test 'src/**/*.test.ts' 'scripts/*.test.mjs'"
"test": "node --test 'src/**/*.test.ts' 'scripts/*.test.mjs' 'plugins/*.test.js'"
},
"dependencies": {
"@expo/vector-icons": "^15.0.3",
Expand Down
38 changes: 38 additions & 0 deletions plugins/withKeyboardInsets.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
const { withMainActivity } = require("@expo/config-plugins")

const imports = `import android.view.View
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
`

const listener = `
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
val root = findViewById<View>(android.R.id.content)
val bottom = root.paddingBottom
// Android 15+ no longer resizes edge-to-edge windows for the IME.
ViewCompat.setOnApplyWindowInsetsListener(root) { view, insets ->
view.setPadding(view.paddingLeft, view.paddingTop, view.paddingRight, bottom + insets.getInsets(WindowInsetsCompat.Type.ime()).bottom)
insets
}
ViewCompat.requestApplyInsets(root)
}
`

function addKeyboardInsets(contents) {
if (contents.includes("WindowInsetsCompat.Type.ime()")) return contents
if (!contents.includes("import android.os.Bundle\n") || !contents.includes(" super.onCreate(null)\n")) {
throw new Error("Unsupported MainActivity.kt template")
}
return contents
.replace("import android.os.Bundle\n", `import android.os.Bundle\n${imports}`)
.replace(" super.onCreate(null)\n", ` super.onCreate(null)\n${listener}`)
}

module.exports = (config) =>
withMainActivity(config, (config) => {
if (config.modResults.language !== "kt") throw new Error("Keyboard inset fix requires MainActivity.kt")
config.modResults.contents = addKeyboardInsets(config.modResults.contents)
return config
})

module.exports.addKeyboardInsets = addKeyboardInsets
10 changes: 10 additions & 0 deletions plugins/withKeyboardInsets.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const assert = require("node:assert/strict")
const test = require("node:test")
const { addKeyboardInsets } = require("./withKeyboardInsets")

test("adds Android IME insets listener once", () => {
const source = "import android.os.Bundle\nclass MainActivity {\n super.onCreate(null)\n}\n"
const result = addKeyboardInsets(source)
assert.match(result, /WindowInsetsCompat\.Type\.ime\(\)/)
assert.equal(addKeyboardInsets(result), result)
})