4

我正在研究将 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 应用程序中。或者在谷歌浏览器的 URL 输入栏中。

这对大多数应用程序来说是无害的 - 如果您在不支持图像输入的输入字段中选择 GIF(例如上面的示例,Gmail 和 Chrome),GBoard 会显示“类似 toast”的消息“此文本字段不支持'不支持从键盘插入 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);

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

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

我说的是另一种情况——我会重申——当用户尝试在我们的应用程序中将 GIF 插入“其他”EditText 并且 GBoard 使用 GIF 的 URI 和 ACTION_SEND 启动我们的“消息撰写”活动时。

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

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

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

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

删除 sharedUserId 并且不进行任何其他更改使得这些动画图像 URI 现在可以使用 cr.openInputStream 打开,无论是在输入连接回调中还是在 ACTION_SEND 中。

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

URI 看起来像这样,应用程序的包名称编码在一个参数中:

content://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)?

4

1 回答 1

1

SEND就像你说的,当视图不接受时,Gboard 正在发送一个意图Gifs。问题是,我测试过的其他键盘也是如此,所以这似乎不是 Gboard 的错误。

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

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

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;
}

该方法接收一个剪辑数据,该剪辑数据可以使用intent.getClipData(). 它获取当前键盘,检查提供者,将它们的权限与剪辑数据的 Uri 的权限匹配,如果剪辑数据来自键盘,则返回 false。

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

我希望这有帮助 :)

于 2018-08-23T14:45:13.077 回答