谷歌键盘 - 插入图像“发送”意图怪异
Posted
技术标签:
【中文标题】谷歌键盘 - 插入图像“发送”意图怪异【英文标题】:Google Keyboard - insert image "send" intent weirdness 【发布时间】:2018-09-21 19:08:22 【问题描述】:我正在研究将 Gboard 图片支持添加到我们的应用中。
我有以下三个具体问题/问题。
在https://developer.android.com/guide/topics/text/image-keyboard.html看过官方文档
但是发生了一些奇怪的事情。
当用户点击 EditText 时,编辑器会发送一个 MIME 列表 它在 EditorInfo.contentMimeTypes 中接受的内容类型。
IME 读取支持的类型列表并在 编辑器可以接受的软键盘。
我在读这篇文章还意味着如果一个应用没有设置 EditorInfo.contentMimeType,那么键盘将不会启用 UI 来插入图像。
但实际情况并非如此。
首先,我看到的是 GBoard 显示了几乎所有 EditText 的插入图像 UI,大概基于其 inputType。
我尝试创建一个 EditText 子类,覆盖 onCreateInputConnection 并确保 EditorInfo.contentMimeType -> 插入图像 UI 确实显示。
我们的应用中有几十个 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=图片%2Fgif
所以问题 #3 现在被替换为
问题 #4 - 我们如何向 Google 报告此错误(sharedUserId 导致无法打开图像 URI)?
【问题讨论】:
我遇到了同样的问题。有什么解决办法吗? 不。不知道是否有办法向 Google 报告此问题或引起他们对这篇文章的注意。所以这里没有新信息。 【参考方案1】:如您所说,当视图不接受 Gifs
时,Gboard 会发送 SEND
意图。问题是,我测试过的其他键盘也是如此,所以这似乎不是 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。
目前,我无法找到另一种通用解决方案,因为我测试的键盘显示不同的结果。
我希望这会有所帮助:)
【讨论】:
以上是关于谷歌键盘 - 插入图像“发送”意图怪异的主要内容,如果未能解决你的问题,请参考以下文章
如果 setIconifiedByDefault 为 false,则从另一个意图返回时键盘弹出