fix(android): fix edge-to-edge (#12453)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Added the ability to retrieve the system navigation bar height on Android devices.

- **Bug Fixes**
  - Removed duplicate internal code to improve stability.

- **Chores**
  - Removed the dependency on the edge-to-edge support package and related configuration and code.
  - Updated configuration to adjust margins for edge-to-edge layouts.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
aki-chang-dev
2025-05-22 09:57:50 +00:00
parent 6d662b8a54
commit d91e64b46b
10 changed files with 91 additions and 83 deletions

View File

@@ -12,7 +12,6 @@ dependencies {
implementation project(':capacitor-app') implementation project(':capacitor-app')
implementation project(':capacitor-keyboard') implementation project(':capacitor-keyboard')
implementation project(':capacitor-status-bar') implementation project(':capacitor-status-bar')
implementation project(':capawesome-capacitor-android-edge-to-edge-support')
implementation project(':capgo-inappbrowser') implementation project(':capgo-inappbrowser')
} }

View File

@@ -1,10 +1,13 @@
package app.affine.pro package app.affine.pro
import android.content.res.ColorStateList import android.content.res.ColorStateList
import android.os.Bundle
import android.view.Gravity import android.view.Gravity
import android.view.View import android.view.View
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateMargins import androidx.core.view.updateMargins
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.affine.pro.ai.AIActivity import app.affine.pro.ai.AIActivity
@@ -64,6 +67,16 @@ class MainActivity : BridgeActivity(), AIButtonPlugin.Callback, AffineThemePlugi
} }
} }
private var naviHeight = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { v, insets ->
naviHeight = insets.getInsets(WindowInsetsCompat.Type.navigationBars()).bottom
ViewCompat.onApplyWindowInsets(v, insets)
}
}
override fun load() { override fun load() {
super.load() super.load()
AuthInitializer.initialize(bridge) AuthInitializer.initialize(bridge)
@@ -96,6 +109,10 @@ class MainActivity : BridgeActivity(), AIButtonPlugin.Callback, AffineThemePlugi
} }
} }
override fun getSystemNaviBarHeight(): Int {
return naviHeight
}
override fun onClick(v: View) { override fun onClick(v: View) {
lifecycleScope.launch { lifecycleScope.launch {
webService.update(bridge) webService.update(bridge)

View File

@@ -1,5 +1,6 @@
package app.affine.pro.plugin package app.affine.pro.plugin
import com.getcapacitor.JSObject
import com.getcapacitor.Plugin import com.getcapacitor.Plugin
import com.getcapacitor.PluginCall import com.getcapacitor.PluginCall
import com.getcapacitor.PluginMethod import com.getcapacitor.PluginMethod
@@ -11,6 +12,7 @@ class AffineThemePlugin : Plugin() {
interface Callback { interface Callback {
fun onThemeChanged(darkMode: Boolean) fun onThemeChanged(darkMode: Boolean)
fun getSystemNaviBarHeight(): Int
} }
@PluginMethod @PluginMethod
@@ -20,4 +22,10 @@ class AffineThemePlugin : Plugin() {
(bridge.activity as? Callback)?.onThemeChanged(darkMode) (bridge.activity as? Callback)?.onThemeChanged(darkMode)
call.resolve() call.resolve()
} }
@PluginMethod
fun getSystemNaviBarHeight(call: PluginCall) {
val height = (bridge.activity as? Callback)?.getSystemNaviBarHeight() ?: 0
call.resolve(JSObject().put("height", height))
}
} }

View File

@@ -1303,6 +1303,70 @@ inline fun <T : Disposable?, R> T.use(block: (T) -> R) =
* @suppress * @suppress
* */ * */
object NoPointer object NoPointer
/**
* The cleaner interface for Object finalization code to run.
* This is the entry point to any implementation that we're using.
*
* The cleaner registers objects and returns cleanables, so now we are
* defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the
* different implmentations available at compile time.
*
* @suppress
*/
interface UniffiCleaner {
interface Cleanable {
fun clean()
}
fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable
companion object
}
// The fallback Jna cleaner, which is available for both Android, and the JVM.
private class UniffiJnaCleaner : UniffiCleaner {
private val cleaner = com.sun.jna.internal.Cleaner.getCleaner()
override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
UniffiJnaCleanable(cleaner.register(value, cleanUpTask))
}
private class UniffiJnaCleanable(
private val cleanable: com.sun.jna.internal.Cleaner.Cleanable,
) : UniffiCleaner.Cleanable {
override fun clean() = cleanable.clean()
}
// We decide at uniffi binding generation time whether we were
// using Android or not.
// There are further runtime checks to chose the correct implementation
// of the cleaner.
private fun UniffiCleaner.Companion.create(): UniffiCleaner =
try {
// For safety's sake: if the library hasn't been run in android_cleaner = true
// mode, but is being run on Android, then we still need to think about
// Android API versions.
// So we check if java.lang.ref.Cleaner is there, and use that…
java.lang.Class.forName("java.lang.ref.Cleaner")
JavaLangRefCleaner()
} catch (e: ClassNotFoundException) {
// … otherwise, fallback to the JNA cleaner.
UniffiJnaCleaner()
}
private class JavaLangRefCleaner : UniffiCleaner {
val cleaner = java.lang.ref.Cleaner.create()
override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
JavaLangRefCleanable(cleaner.register(value, cleanUpTask))
}
private class JavaLangRefCleanable(
val cleanable: java.lang.ref.Cleaner.Cleanable
) : UniffiCleaner.Cleanable {
override fun clean() = cleanable.clean()
}
/** /**
* @suppress * @suppress
@@ -1529,70 +1593,6 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
// //
/**
* The cleaner interface for Object finalization code to run.
* This is the entry point to any implementation that we're using.
*
* The cleaner registers objects and returns cleanables, so now we are
* defining a `UniffiCleaner` with a `UniffiClenaer.Cleanable` to abstract the
* different implmentations available at compile time.
*
* @suppress
*/
interface UniffiCleaner {
interface Cleanable {
fun clean()
}
fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable
companion object
}
// The fallback Jna cleaner, which is available for both Android, and the JVM.
private class UniffiJnaCleaner : UniffiCleaner {
private val cleaner = com.sun.jna.internal.Cleaner.getCleaner()
override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
UniffiJnaCleanable(cleaner.register(value, cleanUpTask))
}
private class UniffiJnaCleanable(
private val cleanable: com.sun.jna.internal.Cleaner.Cleanable,
) : UniffiCleaner.Cleanable {
override fun clean() = cleanable.clean()
}
// We decide at uniffi binding generation time whether we were
// using Android or not.
// There are further runtime checks to chose the correct implementation
// of the cleaner.
private fun UniffiCleaner.Companion.create(): UniffiCleaner =
try {
// For safety's sake: if the library hasn't been run in android_cleaner = true
// mode, but is being run on Android, then we still need to think about
// Android API versions.
// So we check if java.lang.ref.Cleaner is there, and use that…
java.lang.Class.forName("java.lang.ref.Cleaner")
JavaLangRefCleaner()
} catch (e: ClassNotFoundException) {
// … otherwise, fallback to the JNA cleaner.
UniffiJnaCleaner()
}
private class JavaLangRefCleaner : UniffiCleaner {
val cleaner = java.lang.ref.Cleaner.create()
override fun register(value: Any, cleanUpTask: Runnable): UniffiCleaner.Cleanable =
JavaLangRefCleanable(cleaner.register(value, cleanUpTask))
}
private class JavaLangRefCleanable(
val cleanable: java.lang.ref.Cleaner.Cleanable
) : UniffiCleaner.Cleanable {
override fun clean() = cleanable.clean()
}
public interface DocStoragePoolInterface { public interface DocStoragePoolInterface {
suspend fun `clearClocks`(`universalId`: kotlin.String) suspend fun `clearClocks`(`universalId`: kotlin.String)

View File

@@ -11,8 +11,5 @@ project(':capacitor-keyboard').projectDir = new File('../../../../../node_module
include ':capacitor-status-bar' include ':capacitor-status-bar'
project(':capacitor-status-bar').projectDir = new File('../../../../../node_modules/@capacitor/status-bar/android') project(':capacitor-status-bar').projectDir = new File('../../../../../node_modules/@capacitor/status-bar/android')
include ':capawesome-capacitor-android-edge-to-edge-support'
project(':capawesome-capacitor-android-edge-to-edge-support').projectDir = new File('../../../../../node_modules/@capawesome/capacitor-android-edge-to-edge-support/android')
include ':capgo-inappbrowser' include ':capgo-inappbrowser'
project(':capgo-inappbrowser').projectDir = new File('../../../../../node_modules/@capgo/inappbrowser/android') project(':capgo-inappbrowser').projectDir = new File('../../../../../node_modules/@capgo/inappbrowser/android')

View File

@@ -25,6 +25,7 @@ const config: CapacitorConfig & AppConfig = {
keystoreAliasPassword: process.env.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD, keystoreAliasPassword: process.env.AFFINE_ANDROID_KEYSTORE_ALIAS_PASSWORD,
releaseType: 'AAB', releaseType: 'AAB',
}, },
adjustMarginsForEdgeToEdge: 'force',
}, },
server: { server: {
cleartext: true, cleartext: true,

View File

@@ -23,7 +23,6 @@
"@capacitor/core": "^7.0.0", "@capacitor/core": "^7.0.0",
"@capacitor/keyboard": "^7.0.0", "@capacitor/keyboard": "^7.0.0",
"@capacitor/status-bar": "^7.0.0", "@capacitor/status-bar": "^7.0.0",
"@capawesome/capacitor-android-edge-to-edge-support": "^7.0.0",
"@capgo/inappbrowser": "^7.1.0", "@capgo/inappbrowser": "^7.1.0",
"@sentry/react": "^9.2.0", "@sentry/react": "^9.2.0",
"@toeverything/infra": "workspace:*", "@toeverything/infra": "workspace:*",

View File

@@ -40,7 +40,6 @@ import {
import { App as CapacitorApp } from '@capacitor/app'; import { App as CapacitorApp } from '@capacitor/app';
import { Keyboard } from '@capacitor/keyboard'; import { Keyboard } from '@capacitor/keyboard';
import { StatusBar, Style } from '@capacitor/status-bar'; import { StatusBar, Style } from '@capacitor/status-bar';
import { EdgeToEdge } from '@capawesome/capacitor-android-edge-to-edge-support';
import { InAppBrowser } from '@capgo/inappbrowser'; import { InAppBrowser } from '@capgo/inappbrowser';
import { Framework, FrameworkRoot, getCurrentStore } from '@toeverything/infra'; import { Framework, FrameworkRoot, getCurrentStore } from '@toeverything/infra';
import { OpClient } from '@toeverything/infra/op'; import { OpClient } from '@toeverything/infra/op';
@@ -329,9 +328,6 @@ const ThemeProvider = () => {
? Style.Light ? Style.Light
: Style.Default, : Style.Default,
}).catch(console.error); }).catch(console.error);
EdgeToEdge.setBackgroundColor({
color: resolvedTheme === 'dark' ? '#000000' : '#F5F5F5',
}).catch(console.error);
AffineTheme.onThemeChanged({ AffineTheme.onThemeChanged({
darkMode: resolvedTheme === 'dark', darkMode: resolvedTheme === 'dark',
}).catch(console.error); }).catch(console.error);

View File

@@ -1,3 +1,4 @@
export interface AffineThemePlugin { export interface AffineThemePlugin {
onThemeChanged(options: { darkMode: boolean }): Promise<void>; onThemeChanged(options: { darkMode: boolean }): Promise<void>;
getSystemNaviBarHeight(): Promise<{ height: number }>;
} }

View File

@@ -260,7 +260,6 @@ __metadata:
"@capacitor/core": "npm:^7.0.0" "@capacitor/core": "npm:^7.0.0"
"@capacitor/keyboard": "npm:^7.0.0" "@capacitor/keyboard": "npm:^7.0.0"
"@capacitor/status-bar": "npm:^7.0.0" "@capacitor/status-bar": "npm:^7.0.0"
"@capawesome/capacitor-android-edge-to-edge-support": "npm:^7.0.0"
"@capgo/inappbrowser": "npm:^7.1.0" "@capgo/inappbrowser": "npm:^7.1.0"
"@sentry/react": "npm:^9.2.0" "@sentry/react": "npm:^9.2.0"
"@toeverything/infra": "workspace:*" "@toeverything/infra": "workspace:*"
@@ -4527,15 +4526,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@capawesome/capacitor-android-edge-to-edge-support@npm:^7.0.0":
version: 7.2.1
resolution: "@capawesome/capacitor-android-edge-to-edge-support@npm:7.2.1"
peerDependencies:
"@capacitor/core": ">=7.0.0"
checksum: 10/6d84de3a7e8bc6dcd11809b00a295cc7269963e695b73ca036fbc7a60688cdbf8835a6e811709aeeb0cf15c8c1ac10f0ff597dee673a7597864aadafb879ca2e
languageName: node
linkType: hard
"@capgo/inappbrowser@npm:^7.1.0": "@capgo/inappbrowser@npm:^7.1.0":
version: 7.9.3 version: 7.9.3
resolution: "@capgo/inappbrowser@npm:7.9.3" resolution: "@capgo/inappbrowser@npm:7.9.3"