因为CurrentInputConnection总是返回null

发布于 2025-01-18 18:07:33 字数 3116 浏览 3 评论 0原文

我正在尝试为Android构建IME(输入方法编辑器)。我知道我必须创建一个 inputMethodservice ,有权访问

然后我知道我必须做这样的事情:

val focusedTextField = currentInputConnection ?: return

问题在于我始终将其变为零。我的理论是,文本编辑器(当前集中的文本字段)并不认为我的应用程序是IME,或者“可能没有意识到”它正在集中。 因此,也许我必须提供更多信息。我已经检查了我声明服务并提供元数据的清单,一切似乎都是正确的。 res/xml/method.xml文件正确。

这是清单文件。有人告诉我,自Android 11以来,我们必须要求使用服务的位置许可,

<service
    android:name=".IMEService"
    android:label="Amazing Keyboard"
    android:foregroundServiceType="location"
    android:permission="android.permission.BIND_INPUT_METHOD"
    android:exported="true">
        <intent-filter>
            <action android:name="android.view.InputMethod" />
        </intent-filter>
        <meta-data
            android:name="android.view.im"
            android:resource="@xml/method" />
</service>

这是我正在做的方法文件

<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsActivity="com.example.amazingkeyboard.MainActivity">
    <subtype
        android:name = "my app"
        android:label="English (U.S)"
        android:imeSubtypeLocale="en_US"
        android:imeSubtypeMode="keyboard"/>
</input-method>

,我正在使用JetPack撰写,但这不是问题,因为我已经尝试返回XML视图,我始终有错误,

class IMEService : InputMethodService(), LifecycleOwner, 
      ViewModelStoreOwner, SavedStateRegistryOwner {
  fun sendText(text : CharSequence, newCursorPosition : Int) {
    val focusedTextField = currentInputConnection ?: return //always returns null 
    focusedTextField.commitText(text, newCursorPosition) 
  } 
  ...
}

调用方法的地方

val connection = IMEService()
@Composable fun TestKey(modifier: Modifier = Modifier) {
  Key( 
    modifier = modifier .clickable { 
      connection.sendText(unicodeToString(0x1F436), 1)
}...

这是删除验证时 。正如我上面说的那样,问题是我总是变得无效。显然,如果我进行验证,就不会出现错误,但是我也无法发送(因为我总是有空),

// val focusedTextField = currentInputConnection ?: return
val focusedTextField = currentInputConnection

我有这个错误。

java.lang.NullPointerException:
Attempt to invoke interface method 'boolean android.view.inputmethod.InputConnection.commitText(java.lang.CharSequence, int)' on a null object reference
at com.chrrissoft.amazingkeyboard.IMEService.sendText(IMEService.kt:21)
at com.chrrissoft.amazingkeyboard.composables.GeneralKeysKt$TestKey$1.invoke(generalKeys.kt:32)
at com.chrrissoft.amazingkeyboard.composables.GeneralKeysKt$TestKey$1.invoke(generalKeys.kt:31)

在这里是一个完整的项目,以防您想对其进行审查。

I am trying to build an IME (input method editor) for Android. I know that I have to create a class that extends InputMethodService, to have access to the getCurrentInputConnection method. My understanding is that this returns me to the currently focused text field or null if there isn't.

Then I know I have to do something like this:

val focusedTextField = currentInputConnection ?: return

The problem is that I always get null as a result. My theory is that the Text Editor (currently focused Text Field) doesn't recognize my app as an IME or maybe "doesn't realize" that it is being focused.
So maybe I have to provide more information. I already checked the manifest where I declare the service and provide the metadata and everything seems to be correct. The res/xml/method.xml file is correct.

this is the manifest file. I have been told that since android 11 we have to ask for location permission to use services

<service
    android:name=".IMEService"
    android:label="Amazing Keyboard"
    android:foregroundServiceType="location"
    android:permission="android.permission.BIND_INPUT_METHOD"
    android:exported="true">
        <intent-filter>
            <action android:name="android.view.InputMethod" />
        </intent-filter>
        <meta-data
            android:name="android.view.im"
            android:resource="@xml/method" />
</service>

This is the method file

<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsActivity="com.example.amazingkeyboard.MainActivity">
    <subtype
        android:name = "my app"
        android:label="English (U.S)"
        android:imeSubtypeLocale="en_US"
        android:imeSubtypeMode="keyboard"/>
</input-method>

This is what I am doing, I am using jetpack compose, but that is not the problem, because I already tried to return an xml view and I always have the error

class IMEService : InputMethodService(), LifecycleOwner, 
      ViewModelStoreOwner, SavedStateRegistryOwner {
  fun sendText(text : CharSequence, newCursorPosition : Int) {
    val focusedTextField = currentInputConnection ?: return //always returns null 
    focusedTextField.commitText(text, newCursorPosition) 
  } 
  ...
}

This is where I call the method

val connection = IMEService()
@Composable fun TestKey(modifier: Modifier = Modifier) {
  Key( 
    modifier = modifier .clickable { 
      connection.sendText(unicodeToString(0x1F436), 1)
}...

when I remove the validation. As I said above, the problem is that I always get null. Obviously if I do the validation, there will be no error, but I can't send either (because I always have null)

// val focusedTextField = currentInputConnection ?: return
val focusedTextField = currentInputConnection

I have this error.

java.lang.NullPointerException:
Attempt to invoke interface method 'boolean android.view.inputmethod.InputConnection.commitText(java.lang.CharSequence, int)' on a null object reference
at com.chrrissoft.amazingkeyboard.IMEService.sendText(IMEService.kt:21)
at com.chrrissoft.amazingkeyboard.composables.GeneralKeysKt$TestKey$1.invoke(generalKeys.kt:32)
at com.chrrissoft.amazingkeyboard.composables.GeneralKeysKt$TestKey$1.invoke(generalKeys.kt:31)

Here is the complete project, in case you want to review it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

指尖微凉心微凉 2025-01-25 18:07:33

您在以下代码的第二行上获取nullpoInterException

val focusedTextField = currentInputConnection
focusedTextField.commitText(text, newCursorPosition) 

因为当前活动inputConnection不绑定到输入方法,这就是为什么CurrentInputConnection 为空。

有一个InputConnection中,当新客户端绑定到输入方法时,它被称为。在此通话中,您知道CurrentInputConnection返回有效的对象。
因此,在使用CurrentInputConnection客户端应绑定到输入方法。

要使用iMeService在其类范围之外的公共方法,您需要具有界限的实例。在GitHub上挖掘您的来源似乎很容易通过将imeservice传递到testkey()函数来解决。整个代码将看起来像这样:

@Composable
fun TestKey(modifier: Modifier = Modifier, connection: IMEService) {
    Key(
        modifier = modifier
            //...
            .clickable {
                connection.sendText(unicodeToString(0x1F383), 1)
            }
    ) {
        Icon(/*...*/)
    }
}

@Composable
fun EmojiLayout(navController: NavHostController, connection: IMEService) {

    val (currentPage, onPageChange) = remember {
        mutableStateOf(EmoticonsAndEmotionsPage)
    }

    Column(modifier = Modifier.fillMaxSize()) {
        EmojiPage(
            //...
        )
        Row(
            //...
        ) {
            //...
            TestKey(Modifier.weight(1f), connection)
        }
    }
}

@Composable
fun KeyboardScreen(connection: IMEService) {

    AmazingKeyboardTheme() {
        Column(
            //...
        ) {
            val navController = rememberNavController()
            NavHost(navController = navController, startDestination = "qwertyLayout") {
                //...
                composable("emojiLayout") { EmojiLayout(navController, connection) }
            }
        }
    }
}

class AndroidKeyboardView(context: Context) : FrameLayout(context) {

    constructor(service: IMEService) : this(service as Context) {
        inflate(service, R.layout.keyboard_view, this)
        findViewById<ComposeView>(R.id.compose_view).setContent {
            KeyboardScreen(connection = service)
        }
    }
}

imeservice类保持不变。

You are getting NullPointerException on the second line of the following code:

val focusedTextField = currentInputConnection
focusedTextField.commitText(text, newCursorPosition) 

because the currently active InputConnection isn't bound to the input method, and that's why currentInputConnection is null.

There is a onBindInput method in InputConnection, which is called when a new client has bound to the input method. Upon this call you know that currentInputConnection return valid object.
So before using currentInputConnection client should be bound to the input method.

To use IMEService's public methods outside of its class scope, you need to have an instance of the bound service. Digging into your sources on GitHub it seems the problem is easy to solve by passing IMEService to the TestKey() function. The whole code will look something like this:

@Composable
fun TestKey(modifier: Modifier = Modifier, connection: IMEService) {
    Key(
        modifier = modifier
            //...
            .clickable {
                connection.sendText(unicodeToString(0x1F383), 1)
            }
    ) {
        Icon(/*...*/)
    }
}

@Composable
fun EmojiLayout(navController: NavHostController, connection: IMEService) {

    val (currentPage, onPageChange) = remember {
        mutableStateOf(EmoticonsAndEmotionsPage)
    }

    Column(modifier = Modifier.fillMaxSize()) {
        EmojiPage(
            //...
        )
        Row(
            //...
        ) {
            //...
            TestKey(Modifier.weight(1f), connection)
        }
    }
}

@Composable
fun KeyboardScreen(connection: IMEService) {

    AmazingKeyboardTheme() {
        Column(
            //...
        ) {
            val navController = rememberNavController()
            NavHost(navController = navController, startDestination = "qwertyLayout") {
                //...
                composable("emojiLayout") { EmojiLayout(navController, connection) }
            }
        }
    }
}

class AndroidKeyboardView(context: Context) : FrameLayout(context) {

    constructor(service: IMEService) : this(service as Context) {
        inflate(service, R.layout.keyboard_view, this)
        findViewById<ComposeView>(R.id.compose_view).setContent {
            KeyboardScreen(connection = service)
        }
    }
}

IMEService class stays the same.

GRAY°灰色天空 2025-01-25 18:07:33

我假设您遵循此文档并进行了所有必要的设置,包括将 IMEService 添加到 Android Manifest。

val connection = IMEService()

不幸的是,您不能像这样使用 Service ,您无法自己创建 Service 类的实例并依赖它能够正常工作。服务是由Android系统创建的。如果您想使用 currentInputConnection 属性,您应该在 IMEService 中使用它,或者您需要以某种方式将它的实例传递到您想要使用它的地方,或者绑定到它(如果你想拥有的实例) IMEService 服务。另请参阅 这些有关如何绑定到 服务

I assume you followed this documentation and made all the necessary setups, including adding IMEService to the Android Manifest.

val connection = IMEService()

Unfortunately you can't use a Service like that, you can't create an instance of Service class by yourself and rely that it will work correctly. Services are created by Android system. If you want to use currentInputConnection property you should use it from within the IMEService, or you need somehow to pass an instance of it to the place where you want to use it, or to bind to it if you want to have an instance of IMEService service. Please refer also to these answers regarding how to bind to a Service.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文