如何禁用 Google Assistant API(现在点击)

Posted

技术标签:

【中文标题】如何禁用 Google Assistant API(现在点击)【英文标题】:How to disable Google Assist API (Now on Tap) 【发布时间】:2016-01-07 17:32:00 【问题描述】:

Google 最近发布了带有 Now on Tap 的 android Marshmallow,它可以扫描应用内容并向用户提供附加信息。

不幸的是,对于我们的应用,信息看起来不太相关,而且 Google 忽略了我们在 onProvideContentAssist()onProvideAssistData() 中设置的数据。

These specs 看起来相当高级,并且还包含诸如“可以建议”和“附加信息”之类的词,所以看起来谷歌官方允许自己忽略数据应用开发者提供。

所以我们决定禁用 Now on Tap,但似乎不是很简单。 根据上面提供的文档,在这种情况下我们应该使用FLAG_SECURE。但随后用户无法截取屏幕截图,Google Now on Tap 开始通过以下面向用户的消息指责我们的应用:

没有结果 此应用已屏蔽 Now on Tap

但似乎 Opera 以某种方式温和地阻止了 Now on Tap 的私人标签。它为他们显示了更多的应用友好信息:

什么都没有

Opera 如何阻止 Now on Tap?

有没有人知道如何阻止 Google Assist API(Now on Tap)而不会因为我们的应用受到他们的指责?

【问题讨论】:

“看来谷歌官方允许自己忽略应用开发者提供的数据。”很可能,您只是完全错误地做这部分。 【参考方案1】:

View 类有一个方法setAssistBlocked:

控制是否从这个视图和它的辅助数据收集 children 已启用(即,是否 @link #onProvideStructure 和 @link #onProvideVirtualStructure 将被调用)。默认值 为假,允许正常辅助收集。将此设置为 false 将禁用辅助收集。

@param enabled 设置为true以禁用辅助数据收集, 或 false(默认值)以允许它。

可惜这个方法是用@hide注解的,所以我们看不到也不能直接使用。

但是我们可以通过一个小的反射调用来使用它:

private void setAssistBlocked(View view, boolean blocked) 
        try 
            Method setAssistBlockedMethod = View.class.getMethod("setAssistBlocked", boolean.class);
            setAssistBlockedMethod.invoke(view, blocked);
         catch (NoSuchMethodException e) 
            e.printStackTrace();
         catch (InvocationTargetException e) 
            e.printStackTrace();
         catch (IllegalAccessException e) 
            e.printStackTrace();
        
    

要阻止活动中的所有内容,您可以使用:

final View rootContent = findViewById(android.R.id.content);
setAssistBlocked(rootContent, true);

【讨论】:

您好!我们对使用平台私有 API 的建议深表不满。他们被隐藏是有原因的。不支持隐藏的 API,这意味着您不知道它们会发生什么——也许将来它们会消失(幸运的是,您只会默默地对此感到窒息),也许实现会发生变化,因此您现在正在拨打向世界释放僵尸启示录。你不知道未来,所以请不要这样做。 进一步,在这种情况下没有理由这样做,因为你可以使用这个:developer.android.com/reference/android/view/… 那么如果没有理由这样做,为什么 chrome 会完全做到same thing?请解释一下,因为我真的很好奇... 顺便说一句,默认情况下为所有 Android M 智能手机开启 Google Assist 并隐藏禁用它的好方法对应用程序开发人员来说并不友好,不是吗?【参考方案2】:

hackbod's one comment 暗示了我最终需要尝试的另一种可能的解决方案:

此外,在这种情况下没有理由这样做,因为你可以使用这个:https://developer.android.com/reference/android/view/View.html#dispatchProvideStructure(android.view.ViewStructure)

dispatchProvideStructure() 的代码注释指出:“ViewStructure 的调度创建在层次结构中。” (强调我的)。

因此,创建FrameLayoutNoAssistFrameLayout 子类,将dispatchProvideStructure() 覆盖为无操作,并将其用作活动布局的根容器。这将阻止 ViewStructure 自动填充活动主体中的任何内容。

但是,它不会阻止任何可能从您的主要内容区域之外的东西推断出来的东西,例如您的操作栏,因为它会在NoAssistFrameLayout 的视图层次结构之外。 可能其中没有太多会影响隐私的内容,但这是该技术的一个限制。


好的,我试过了,它似乎确实有效:

/***
 Copyright (c) 2015 CommonsWare, LLC
 Licensed under the Apache License, Version 2.0 (the "License"); you may not
 use this file except in compliance with the License. You may obtain a copy
 of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
 by applicable law or agreed to in writing, software distributed under the
 License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
 OF ANY KIND, either express or implied. See the License for the specific
 language governing permissions and limitations under the License.

 From _The Busy Coder's Guide to Android Development_
 https://commonsware.com/Android
 */

package com.commonsware.android.assist.no;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.ViewStructure;
import android.widget.FrameLayout;

public class NoAssistFrameLayout extends FrameLayout 
  public NoAssistFrameLayout(Context context) 
    super(context);
  

  public NoAssistFrameLayout(Context context,
                             AttributeSet attrs) 
    super(context, attrs);
  

  public NoAssistFrameLayout(Context context,
                             AttributeSet attrs,
                             int defStyleAttr) 
    super(context, attrs, defStyleAttr);
  

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  public NoAssistFrameLayout(Context context,
                             AttributeSet attrs,
                             int defStyleAttr,
                             int defStyleRes) 
    super(context, attrs, defStyleAttr, defStyleRes);
  

  @Override
  public void dispatchProvideStructure(ViewStructure structure) 
    // no, thanks
  

【讨论】:

我很好奇使用它作为根视图后“On Tap”弹出窗口是什么样子的。您能描述一下出现的内容或偶然发布的屏幕截图吗? @JeffAlexander:我理所当然地禁用了 Google Now。我一直在用my own assistant implementation 测试NoAssistFrameLayout,它只记录活动暴露的内容。我在this sample project 中为ViewPager 的单个选项卡测试了NoAssistFrameLayout,一切都按预期工作。 如果有人想知道它会是什么样子,我测试了一个 Activity,它的根视图是 FrameLayout,其中有一个 TextView,其文本是电影标题。 On Tap 显示了有关该电影的信息。然后我切换到 NoAssistFrameLayout,我得到了“NoAssistFrameLayout”。 似乎这种方法很难在真正的应用程序中使用。主要问题是它仅阻止 Google Assist 访问的 1 级视图层次结构。例如。它适用于 NoAssistFrameLayout 的直接子级,但如果我使用 NoAssistFrameLayout 作为 Fragment 的父级,那么 Google Assist 可以成功访问 Fragment 的数据。 不,Chrome 不使用NoAssistFrameLayout 技术,它使用setAssistBlocked,如您所见here【参考方案3】:

Opera 这样做可能是因为他们没有更新浏览器来报告辅助数据(这需要与不使用正常视图层次结构的应用程序进行特殊合作,例如浏览器)。

但是,如果您的问题是 Now on Tap 没有返回相关数据,解决方案不是阻止它获取的数据 - 所做的只是确保它为您返回的数据应用程序永远不会改进,因为它永远不会看到可以提供更好建议的数据。阻止它当然不会导致它使用其他数据,它只会让你的应用程序变得愚蠢。

您不应该试图让 Now on Tap 变得愚蠢。

【讨论】:

正如我在问题中提到的那样,Opera 仅针对他们的私人标签执行此操作,但 Google Assist 仍然适用于他们的公共标签。因此,他们似乎没有更新浏览器来报告辅助数据。我理解您的观点,我认为应该有很多应用程序都喜欢 Google Assist 以及与之相关的数据,但我的问题是关于禁用它。

以上是关于如何禁用 Google Assistant API(现在点击)的主要内容,如果未能解决你的问题,请参考以下文章

Google Assistant 的银行操作是不是必须使用交易 API?

让我的应用与 Google Home 中的 Google Assistant 对话

如何以编程方式启动 Google Assistant?

如何使用 Google Assist API 实现助手

如何从 Google Assistant 发出局域网 HTTP 请求?

Google Assistant 与 IFTTT 的集成如何运作?