Google 键盘 - 插入图像“发送”意图怪异

Kos*_*yev 5 android gboard

我正在研究将 Gboard 图像支持添加到我们的应用程序中。

我有以下三个具体问题。

已在https://developer.android.com/guide/topics/text/image-keyboard.html上查看官方文档

不过,有些奇怪的事情正在发生。

当用户点击 EditText 时,编辑器会发送在 EditorInfo.contentMimeTypes 中接受的 MIME 内容类型列表。

IME 读取支持的类型列表,并在软键盘中显示编辑器可接受的内容。

我读到此内容还意味着,如果应用程序设置 EditorInfo.contentMimeType,则键盘将无法启用用于插入图像的 UI。

但实际情况并非如此。

首先,我看到的是 GBoard 显示了几乎所有 EditText 的插入图像 UI,大概是基于其 inputType。

我尝试创建一个 EditText 子类,重写 onCreateInputConnection 并确保 EditorInfo.contentMimeType -> 插入图像 UI 确实显示。

在不需要的 EditText 上插入图像

我们的应用程序中有数十个 EditText 输入字段(可能更多),位于不同的屏幕上。对于绝大多数人来说,插入动画 GIF 没有任何意义。

问题#1 - 如何才能阻止这种情况(如果可以的话)?

顺便说一下,这很容易在几乎任何应用程序、几乎任何输入字段中进行检查。例如,在 Gmail 应用程序的搜索栏中。或者在 Google Chrome 中的 URL 输入栏中。

这对于大多数应用程序来说是无害的 - 如果您在不支持图像输入的输入字段中选择 GIF(例如上面的示例,Gmail 和 Chrome),GBoard 会显示一条“类似于吐司”的消息,指出“此文本字段不支持图像输入”。不支持从键盘插入 GIF”。

好的但是:

作为后备方案,当 GBoard 无法通过 InputConneciton 发送选定的 GIF 时 -> 它会尝试启动仅限于应用程序包的 ACTION_SEND 意图,并使用表示图像的 URL。

我们的应用程序(电子邮件应用程序)在其 ACTION_SEND 清单中确实有一个意图过滤器,使用户能够通过电子邮件共享“内容”。像图库图像、文件管理器中的文件之类的东西,任何东西。

因此,GBoard 最终启动了带有 ACTION_SEND 和图像 URL 的“电子邮件撰写屏幕”。

这会导致另外两个问题:

首先,它会让用户感到困惑

假设他/她尝试将 GIF 插入应用程序中的“某些”EditText 中。

然后,由于 ACTION_SEND - “哇,发生了什么”,他/她将被带到应用程序的“消息撰写”屏幕。

问题#2 - 如何才能阻止这种情况(如果可以的话)?

其次,据我所知,无法使用任何方法打开图像的 URI。

我试过了:

cr.openFileDescriptor(uri, "r");
cr.openAssetFileDescriptor(uri, "r");
cr.openInputStream(uri);
cr.query(uri, null, null, null, null);
Run Code Online (Sandbox Code Playgroud)

所有这些都会因“无效 URI”等变体而失败。

我确实明白,通过为我们想要支持 GIF 的一个或几个输入字段实现一个 InputConnectionWrapper,我们可以直接从 inputContentInfo 请求权限,如文档中所述。

我正在谈论一种不同的情况 - 我会重申 - 当用户尝试将 GIF 插入我们应用程序中的“其他”EditText 时,GBoard 使用 GIF 的 URI 和 ACTION_SEND 启动我们的“消息撰写”活动。

目前,我们的代码尝试以与其他任何方式相同的方式处理此 URI(例如,当用户将 Google Photos 中的照片分享到我们的应用程序中时),但打开此 URI 失败,因此用户不仅会意外地出现在撰写屏幕上,但随后也出现错误,表明我们无法打开/复制“附件”。

问题 #3 - 使用 ACTION_SEND 发送的 GBoard 图像 URI 是否可以使用“标准”ContentResolver 方法打开?

4 月 12 日,更新“问题#3”(无法打开直播)。

我们的应用程序在其清单中使用sharedUserId。

删除sharedUserId并且不进行其他更改,这样这些动画图像URI现在可以在输入连接回调和ACTION_SEND中使用cr.openInputStream正常打开。

虽然可能有理由不使用sharedUserId,但我们的应用程序自2012年左右就开始使用,并且不可能仅因为此功能而删除它(因为这会阻止更新)。

URI 如下所示,应用程序的包名称编码在参数中:

内容://com.google.android.inputmethod.latin.inputcontent/inputContent?fileName=%2Fdata%2Fuser_de%2F0%2Fcom.google.android.inputmethod.latin%2Ffiles%2Fgif20152912710254894520&packageName=org.kman.AquaMail&mimeType=image%2Fgif

所以问题#3现在被替换为

问题 #4 - 我们如何向 Google 报告此错误(sharedUserId 导致图像 URI 无法打开)?

Ped*_*ira 1

SEND正如您所说,当视图不接受时,Gboard 会发送意图Gifs。问题是,我测试的其他键盘也发生了同样的情况,所以这似乎不是 Gboard 的错误。

所以我想出的解决方案是忽略当前键盘的 SEND 意图。

当我在接收器活动中收到意图时,我会检查数据是否来自键盘,为此我使用以下方法,它也可能对您有所帮助:

private boolean isClipDataAuthorityValid(@NonNull ClipData clipData) {
    if (clipData.getItemCount() == 0) {
        return true;
    }

    Uri uri = clipData.getItemAt(0).getUri();

    if (uri == null) {
        return true;
    }

    String authority = uri.getAuthority();
    if (TextUtils.isEmpty(authority)) {
        return true;
    }

    String defaultInputMethod = Settings.Secure.getString(getContentResolver(), "default_input_method");
    if (TextUtils.isEmpty(defaultInputMethod)) {
        return true;
    }

    String keyboardPackage = defaultInputMethod.split("/")[0];
    try {
        ProviderInfo[] providers = getPackageManager().getPackageInfo(keyboardPackage, PackageManager.GET_PROVIDERS).providers;
        if (providers == null || providers.length == 0) {
            return true;
        }

        //Check if the authority of the given clipdata's uri matches any of the keyboards's provider authority
        for (ProviderInfo provider : providers) {
            if (TextUtils.equals(authority, provider.authority)) {
                return false;
            }
        }

    } catch (PackageManager.NameNotFoundException e) {
        //Do nothing
    }

    return true;
}
Run Code Online (Sandbox Code Playgroud)

该方法接收一个剪辑数据,可以使用 轻松从意图中提取该剪辑数据intent.getClipData()。它获取当前键盘,检查其提供程序,将其权限与剪辑数据的 Uri 权限进行匹配,如果剪辑数据来自键盘,则返回 false。

目前,我无法找到另一个通用解决方案,因为我测试的键盘显示了不同的结果。

我希望这有帮助 :)