Participe do evento ⁠#Android11: apresentação de lançamento da versão Beta no dia 3 de junho.

Visibilidade do pacote no Android 11

O Android 11 muda a forma como os app podem consultar e interagir com outros apps instalados no mesmo dispositivo. Se o app for direcionado ao Android 11, talvez seja necessário adicionar um elemento <queries> ao arquivo de manifesto dele para que o sistema saiba quais outros apps exibir ao seu.

O elemento <queries> permite que você descreva com que outros apps seu app pode precisar interagir. É possível especificar apps pelo nome do pacote ou pela assinatura da intent em um único elemento <queries>.

Os métodos PackageManager que retornam resultados sobre outros apps, como queryIntentActivities(), são filtrados com base na declaração <queries> do app de chamada. Interações explícitas com outros apps, como startService(), também exigem que o app de destino corresponda a uma das declarações em <queries>.

Queremos receber seu feedback. Responda a esta breve pesquisa para nos contar como você está usando o recurso (link em inglês). Especificamente, conte-nos sobre os casos de uso afetados por esse recurso.

Consultar e interagir com pacotes específicos

Se você conhece o conjunto específico de apps que quer consultar ou com os quais quer interagir, como apps que se integram ao seu ou apps cujos serviços você usa, inclua os nomes dos pacotes deles em um conjunto de elementos <package> dentro da tag <queries>:

<manifest package="com.example.game">
    <queries>
        <package android:name="com.example.store" />
        <package android:name="com.example.services" />
    </queries>
    ...
</manifest>

Consultar e interagir com apps que receberam um filtro de intent

Seu app pode precisar consultar ou interagir com um conjunto de apps que atendem a uma finalidade específica, mas você pode não saber os nomes de pacotes específicos a serem incluídos. Nessa situação, você pode listar as assinaturas de filtro de intent na tag <queries>. Então, seu app pode descobrir apps que tenham tags <intent-filter> correspondentes.

O exemplo a seguir permite que seu app veja apps instalados compatíveis com o compartilhamento de imagens JPEG:

<manifest package="com.example.game">
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="image/jpeg" />
        </intent>
    </queries>
    ...
</manifest>

O elemento <intent> tem algumas restrições:

  • É necessário incluir exatamente um elemento <action>.
  • Não é possível usar os atributos path, pathPrefix, pathPattern ou port em um elemento <data>. O sistema se comporta como se o valor de cada atributo fosse o valor do caractere curinga genérico (*).
  • Não é possível usar o atributo mimeGroup de um elemento <data>.
  • Nos elementos <data> de um único elemento <intent>, é possível usar cada um dos seguintes atributos no máximo uma vez:

    • mimeType
    • scheme
    • host

    É possível distribuir esses atributos em vários elementos <data> ou usá-los em um único elemento <data>.

O elemento <intent> aceita o caractere curinga genérico (*) como o valor de alguns atributos:

  • O atributo name do elemento <action>.
  • O subtipo do atributo mimeType de um elemento <data> (image/*).
  • O tipo e subtipo do atributo mimeType de um elemento <data> (*/*).
  • O atributo scheme de um elemento <data>.
  • O atributo host de um elemento <data>.

A menos que especificado de outra forma na lista anterior, o sistema não é compatível com uma combinação de caracteres curinga e de texto, como prefix*.

Consultar e interagir com todos os apps

Em casos raros, o app pode precisar consultar ou interagir com todos os apps instalados em um dispositivo, independentemente dos componentes que eles contenham. Um exemplo é uma loja de apps, como o Google Play. Para permitir que o app veja todos os outros apps instalados, o Android 11 introduz a permissão QUERY_ALL_PACKAGES.

Observação: na grande maioria dos casos, é possível preencher o caso de uso do app declarando a tag <queries>.

Nas próximas versões da Visualização do desenvolvedor, procure o Google Play para fornecer diretrizes para apps que precisam dessa permissão.

Mensagens de registro para filtragem de pacotes

Para ver mais detalhes sobre como as mudanças na visibilidade do pacote afetam seu app, ative as mensagens de registro para filtragem de pacotes. Se estiver desenvolvendo um app de teste ou depurável no Android Studio, esse recurso estará ativado. Caso contrário, será possível executar o seguinte comando em uma janela de terminal para ativá-lo manualmente:

adb shell pm log-visibility --enable your-package-name

Em seguida, sempre que os pacotes forem filtrados dos valores de retorno de um objeto PackageManager, você verá uma mensagem semelhante à seguinte no Logcat:

I/AppsFilter: interaction: PackageSetting{7654321 \
  com.example.myapp/12345} -> PackageSetting{...} BLOCKED

Testar a mudança

Para testar se essa mudança de comportamento entrou em vigor no seu app, siga as seguintes etapas:

  1. Instale o Android Studio 3.6.1 ou uma versão mais recente.
  2. Instale a versão mais recente do Gradle compatível com o Android Studio.
  3. Defina a targetSdkVersion do app como 'R'.
  4. Não inclua o elemento <queries> no arquivo de manifesto do app.
  5. Chame getInstalledApplications() ou getInstalledPackages(). Os dois métodos retornarão uma lista filtrada.
  6. Veja quais recursos do app não estão funcionando.
  7. Introduza as entradas <queries> adequadas para corrigir esses recursos.

Casos de uso que não são afetados pela mudança

A lista a seguir inclui vários exemplos de casos de uso que não exigem uma declaração <queries>:

  • O app de destino é o próprio app.
  • Use uma intent implícita para iniciar uma atividade. Seu app pode restringir a forma como ele usa intents implícitas para interagir com outros apps.
  • O app interage com determinados pacotes do sistema, como o provedor de mídia, que implementam funcionalidades essenciais do Android.
  • Outro app espera um resultado do seu. Essa situação se aplica quando seu app é um provedor de conteúdo, quando o outro app invoca o seu chamando startActivityForResult() e quando seu app é um serviço que outro app tenta iniciar ou ao qual ele tenta se conectar.

Por exemplo, se outro app fizer uma solicitação a um provedor de conteúdo no seu app, o sistema permitirá que seu app veja o outro.

Adicionar restrições aos inícios de atividade

O Android 11 adiciona várias sinalizações que permitem especificar quando uma chamada para startActivity() do seu app deve resultar em um ActivityNotFoundException, em vez de fazer com que outro app complete a intent. Ao usar essas sinalizações, não é necessário chamar resolveActivity() ou queryIntentActivities().

Essas sinalizações são especialmente úteis para intents da Web. Quando seu app quiser criar um link direto para um app instalado e não houver um disponível, ele poderá carregar o URL em uma guia personalizada ou em um navegador no app para processar a intent.

Iniciar uma intent da Web em um app que não seja um navegador

Quando a sinalização de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER é incluída, a intent é iniciada apenas nos seguintes casos:

Caso contrário, um ActivityNotFoundException é gerado, e espera-se que seu app processe a intent propriamente dita.

Kotlin

try {
    val intent = Intent(ACTION_VIEW, Uri.parse(url)).apply {
        // The URL should either launch directly in a non-browser app (if it's
        // the default), or in the disambiguation dialog.
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_REQUIRE_NON_BROWSER
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // Only browser apps are available, or a browser is the default.
    tryLoadInCustomTabs(url) // Or use an in-app browser.
}

Java

try {
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    // The URL should either launch directly in a non-browser app (if it's the
    // default), or in the disambiguation dialog.
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_REQUIRE_NON_BROWSER);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // Only browser apps are available, or a browser is the default.
    tryLoadInCustomTabs(url); // Or use an in-app browser.
}

Exigir apenas uma atividade correspondente

Quando a sinalização de intent FLAG_ACTIVITY_REQUIRE_DEFAULT é incluída, a intent do app é invocada quando apenas um app no dispositivo pode gerenciá-la ou quando um app é o gerenciador padrão dessa intent. Essa sinalização reduz o atrito ao não mostrar a caixa de diálogo de desambiguação e, em vez disso, permitir que o app escolha como o conteúdo é exibido para o usuário.

Kotlin


val url = url-to-load
try {
    // In order for this intent to be invoked, the system must directly launch a
    // non-browser app.
    val intent = Intent(ACTION_VIEW, Uri.parse(url).apply {
        addCategory(CATEGORY_BROWSABLE)
        flags = FLAG_ACTIVITY_NEW_TASK or FLAG_REQUIRE_NON_BROWSER or
                FLAG_ACTIVITY_REQUIRE_DEFAULT
    }
    startActivity(intent)
} catch (e: ActivityNotFoundException) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling URLs.
    tryLoadInCustomTabs(url)
}

Java

String url = url-to-load;
try {
    // In order for this intent to be invoked, the system must directly launch a
    // non-browser app.
    Intent intent = new Intent(ACTION_VIEW, Uri.parse(url));
    intent.addCategory(CATEGORY_BROWSABLE);
    intent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_REQUIRE_NON_BROWSER |
            FLAG_ACTIVITY_REQUIRE_DEFAULT);
    startActivity(intent);
} catch (ActivityNotFoundException e) {
    // This code executes in one of the following cases:
    // 1. Only browser apps can handle the intent.
    // 2. The user has set a browser app as the default app.
    // 3. The user hasn't set any app as the default for handling URLs.
    tryLoadInCustomTabs(url);
}