Как отключить Google Assist API (Now on Tap)

Google недавно выпустила Android Marshmallow с функцией Now on Tap, которая может сканировать содержимое приложения и предоставлять пользователю дополнительную информацию.

К сожалению, для нашего приложения информация выглядит не очень актуальной, и Google игнорирует данные, которые мы установили внутри onProvideContentAssist() и onProvideAssistData().

Эти спецификации выглядят довольно высокоуровневыми и также содержат такие слова, как " может предложить" и "дополнительную информацию", поэтому Google официально позволяет себе игнорировать данные, которые предоставляют разработчики приложений.

Поэтому мы решили отключить 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), не обвиняя наше приложение с их стороны?


person goRGon    schedule 09.10.2015    source источник
comment
похоже, что Google официально позволяет себе игнорировать данные, которые предоставляют разработчики приложений. Скорее всего, вы просто делаете эту часть совершенно неправильно.   -  person BooleanCheese    schedule 09.10.2015
comment
@Saeed У Google есть даже пример кода в документах, которые я упомянул: developer.android. com/training/articles/, данные которого полностью игнорируются onTap (он же Google Assist) внутри нашего приложения.   -  person goRGon    schedule 09.10.2015


Ответы (3)


Класс View имеет метод setAssistBlocked:

Определяет, включен ли вспомогательный сбор данных из этого представления и его дочерних элементов (то есть будут ли вызываться {@link #onProvideStructure} и {@link #onProvideVirtualStructure}). Значение по умолчанию — false, разрешая обычный сбор помощи. Если установить для этого параметра значение 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);
person Mattia Maestrini    schedule 10.10.2015
comment
Привет! Мы резко осуждаем рекомендации по использованию частных API платформы. Они скрыты по причине. Скрытые API не поддерживаются, то есть вы не знаете, что с ними может случиться — может быть, в будущем они исчезнут (к счастью, вы просто молча подавитесь этим), может быть, реализация изменится так, что вызов, который вы делаете сейчас обрушивает на мир зомби-апокалипсис. Ты не знаешь будущего, поэтому, пожалуйста, не делай этого. - person hackbod; 16.10.2015
comment
Кроме того, в этом случае нет причины для этого, потому что вы можете использовать это: developer.android.com/reference/android/view/ - person hackbod; 16.10.2015
comment
Итак, если нет причин делать это, почему Chrome делает точно то же самое? Объясните пожалуйста, а то мне очень интересно... - person Mattia Maestrini; 16.10.2015
comment
Кстати, включение Google Assist по умолчанию для всех смартфонов Android M и сокрытие хорошего способа его отключения не выглядит дружелюбно для разработчиков приложений, не так ли? - person goRGon; 23.10.2015

На другое возможное решение, которое мне в конечном итоге нужно будет опробовать, намекает один комментарий от hackbod:

Далее, в данном случае нет смысла это делать, потому что можно использовать вот это: https://developer.android.com/reference/android/view/View.html#dispatchProvideStructure(android.view.ViewStructure)

Комментарий к коду для dispatchProvideStructure() гласит: «Отправить создание ViewStructure вниз по иерархии». (выделено мной).

Итак, создайте подкласс NoAssistFrameLayout для FrameLayout, переопределите 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
  }
}
person CommonsWare    schedule 15.10.2015
comment
Мне любопытно, как выглядит всплывающее окно On Tap после использования его в качестве корневого представления. Не могли бы вы описать, что появилось, или выложить случайно скриншот? - person Jeff; 21.10.2015
comment
@JeffAlexander: я, конечно же, отключил Google Now. Я тестировал NoAssistFrameLayout с помощью моей собственной реализации помощника, которая просто регистрирует то, что выставляется активностью. Я протестировал NoAssistFrameLayout для одной вкладки ViewPager в этом примере проекта и все заработало, как и ожидалось. - person CommonsWare; 21.10.2015
comment
На случай, если кто-то захочет узнать, как это будет выглядеть, я протестировал Activity с корневым представлением, которое представляло собой FrameLayout, внутри которого был TextView с текстом названия фильма. On Tap показывал информацию об этом фильме. Затем я переключился на NoAssistFrameLayout. Я ничего не получил. - person Jeff; 21.10.2015
comment
Похоже, что этот подход довольно сложно использовать в реальном приложении. Основная проблема заключается в том, что он блокирует только 1 уровень иерархии представлений из доступа к Google Assist. Например. он работает для непосредственных дочерних элементов NoAssistFrameLayout, но если я использую NoAssistFrameLayout в качестве родителя для фрагмента, то Google Assist сможет успешно получить доступ к данным фрагмента. - person goRGon; 23.10.2015
comment
@goRGon: Извините, я не вижу этих результатов. Я только что протестировал исправленную версию этого примера проекта, где Я обернул NoAssistFrameLayout содержимое res/layout/main.xml (LinearLayout держал MaterialTabs+ViewPager). ViewPager содержит 3 вкладки, каждая из которых заполнена фрагментом. Единственный материал, который появляется из помощника, используя этот помощник по ведению журнала вместо Now On Tap — это панель действий. У вас есть воспроизводимый тестовый пример? - person CommonsWare; 23.10.2015
comment
@CommonsWare Может быть, это зависит от данных, которые у вас есть внутри этого фрагмента? Или, может быть, Google Assist достаточно умен, чтобы распознавать некоторые изображения в нашем приложении. Но дело в том, что когда я помещаю всю страницу в NoAssistFrameLayout, Google Assist каким-то образом все равно крадет некоторые данные из встроенных фрагментов. И использование setAssistBlocked отлично работает во всем приложении, просто вызывая его внутри нашего общего базового класса активности. Очень грустно, что Google решил скрыть этот метод от разработчиков приложений. - person goRGon; 23.10.2015
comment
@goRGon: Или, может быть, Google Assist достаточно умен, чтобы распознавать некоторые изображения в нашем приложении — это возможно, поскольку NoAssistFrameLayout не блокирует снимок экрана. Кроме того, без воспроизводимого тестового примера я не могу вам помочь. - person CommonsWare; 23.10.2015
comment
Спасибо, я доволен NoAssistFrameLayout. Похоже, это лучшее решение, особенно после комментария @Mattia Maestrini, что его использует даже Google Chrome... - person goRGon; 23.10.2015
comment
Нет, Chrome не использует метод NoAssistFrameLayout, он использует setAssistBlocked, как вы можете видеть здесь - person Mattia Maestrini; 24.10.2015
comment
Хорошо, наконец я обнаружил, что решение NoAssistFrameLayout тоже работает. Но чтобы это работало во всем приложении, нам нужно поместить NoAssist*Layout во все макеты нашего приложения. - person goRGon; 24.10.2015

Opera делает это, вероятно, потому, что они не обновили свой браузер для предоставления вспомогательных данных (это требует особого взаимодействия с приложениями, которые не используют обычную иерархию представлений, например браузеры).

Однако, если ваша проблема заключается в том, что функция Now on Tap не возвращает релевантные данные, решение состоит в том, чтобы не блокировать получаемые данные — все, что нужно сделать, — это гарантировать, что данные, которые оно возвращает для вашего приложения, никогда не улучшатся. , потому что он никогда не видит данных, для которых мог бы предложить более подходящие варианты. Его блокировка, безусловно, не приведет к тому, что он будет использовать другие данные, это просто сделает его глупым для вашего приложения.

Вы не должны пытаться сделать Now on Tap глупым для своего приложения.

person hackbod    schedule 15.10.2015
comment
Как я уже упоминал в своем вопросе, Opera делает это только для своих личных вкладок, но Google Assist по-прежнему работает для их общедоступных вкладок. Так что не похоже, что они не обновили свой браузер, чтобы сообщать вспомогательные данные. Я понимаю вашу точку зрения, и я думаю, что должно быть много приложений, таких как Google Assist, и данные, которые ему важны, но мой вопрос был о его отключении. - person goRGon; 23.10.2015