Atender a casos de uso comuns, com visibilidade de pacotes limitada

Este documento apresenta vários casos de uso comuns em que um app interage com outros. Cada seção oferece orientações sobre como realizar a função do app com visibilidade limitada de pacotes. Você precisa pensar nisso caso seu app seja destinado ao Android 11 (API de nível 30) ou mais recente.

Quando um app destinado ao Android 11 ou versões mais recentes usar uma intent para iniciar uma atividade em outro, a abordagem mais direta é invocar a intent e processar a exceção ActivityNotFoundException, se nenhum app estiver disponível.

Se parte do app depender de saber se a chamada para startActivity() pode ser bem-sucedida, como mostrar uma IU, adicione um elemento ao <queries> do manifesto do app. Normalmente, é um elemento <intent>.

Abrir URLs

Esta seção descreve várias maneiras de abrir URLs em um app destinado ao Android 11 ou versões mais recentes.

Abrir URLs em um navegador ou outro app

Para abrir um URL, use uma intent que contenha a ação da intent ACTION_VIEW, conforme descrito no guia sobre como carregar um URL da Web. Depois de chamar startActivity() usando essa intent, acontece uma das seguintes ações:

  • O URL é aberto em um app de navegador da Web.
  • O URL é aberto em um app compatível com o URL, como um link direto.
  • Uma caixa de diálogo de desambiguação é mostrada, permitindo que o usuário escolha qual app abre o URL.
  • Uma ActivityNotFoundException ocorre porque não há nenhum app instalado no dispositivo que possa abrir o URL. Isso é incomum.

    Recomenda-se que o app detecte e processe o ActivityNotFoundException se ele ocorrer.

Como o método startActivity() não exige visibilidade do pacote para iniciar a atividade de outro aplicativo, não é necessário adicionar um elemento <queries> ao manifesto do app, nem fazer mudanças em um elemento <queries>. Isso é válido para intents implícitas e explícitas que abrem um URL.

Conferir se um navegador está disponível

Em alguns casos, o app pode verificar se há pelo menos um navegador disponível no dispositivo ou se um navegador específico é o navegador padrão antes de tentar abrir um URL. Nesses casos, inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <category android:name="android.intent.category.BROWSABLE" />
  <data android:scheme="https" />
</intent>

Quando você chama queryIntentActivities() e transmite uma intent da Web como argumento, a lista retornada inclui os apps de navegação disponíveis em alguns casos. A lista não incluirá apps de navegação se o usuário tiver configurado o URL para ser aberto em um app que não seja de navegador por padrão.

Abrir URLs em guias personalizadas

As guias personalizadas permitem que um app personalize a aparência do navegador. É possível abrir um URL em uma guia personalizada sem precisar adicionar ou mudar o elemento <queries> no manifesto do app.

No entanto, convém conferir se o dispositivo tem um navegador que ofereça suporte a guias personalizadas ou selecionar um navegador específico para iniciar com guias personalizadas usando CustomTabsClient.getPackageName(). Nesses casos, inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>

Permitir que apps que não sejam navegadores processem URLs

Mesmo que seu app possa abrir URLs usando guias personalizadas, é recomendável permitir que um app sem navegador abra um URL, se possível. Para fornecer esse recurso no app, tente uma chamada para startActivity() usando uma intent que defina a sinalização de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER. Se o sistema gerar um ActivityNotFoundException, o app poderá abrir o URL em uma guia personalizada.

Se uma intent incluir essa sinalização, uma chamada para startActivity() fará com que um ActivityNotFoundException seja lançado quando uma das seguintes condições ocorrer:

  • A chamada teria iniciado um app de navegador diretamente.
  • A chamada teria mostrado ao usuário uma caixa de diálogo de desambiguação em que as únicas opções são apps de navegador.

O snippet de código a seguir mostra como atualizar sua lógica para usar a sinalização de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER:

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.
    // So you can open the URL directly in your app, for example in a
    // Custom Tab.
    openInCustomTabs(url)
}

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.
    // So you can open the URL directly in your app, for example in a
    // Custom Tab.
    openInCustomTabs(url);
}

Evitar uma caixa de diálogo de desambiguação

Se você quiser evitar a exibição da caixa de diálogo de desambiguação que os usuários podem ver ao abrir um URL e preferir lidar com o URL nessas situações, use uma intent que defina a sinalização de intent FLAG_ACTIVITY_REQUIRE_DEFAULT.

Se uma intent incluir essa sinalização, uma chamada para startActivity() gerará um ActivityNotFoundException quando a chamada mostraria uma caixa de diálogo de desambiguação ao usuário.

Se uma intent incluir essa sinalização e a sinalização de intent FLAG_ACTIVITY_REQUIRE_NON_BROWSER, uma chamada para startActivity() fará com que um ActivityNotFoundException seja lançado quando uma das seguintes condições ocorrer:

  • A chamada teria iniciado o app do navegador diretamente.
  • A chamada teria mostrado uma caixa de diálogo de desambiguação para o usuário.

O snippet de código a seguir mostra como usar as sinalizações FLAG_ACTIVITY_REQUIRE_NON_BROWSER e FLAG_ACTIVITY_REQUIRE_DEFAULT juntas:

Kotlin

val url = URL_TO_LOAD
try {
    // 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_ACTIVITY_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 this URL.
    openInCustomTabs(url)
}

Java

String url = URL_TO_LOAD;
try {
    // 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_ACTIVITY_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 this URL.
    openInCustomTabs(url);
}

Abrir um arquivo

Se o app processa arquivos ou anexos, como para verificar se um dispositivo pode abrir um determinado arquivo, geralmente é mais fácil tentar iniciar uma atividade que possa processar o arquivo. Para fazer isso, use uma intent que inclua a ação de intent ACTION_VIEW e o URI que representa o arquivo específico. Se nenhum app estiver disponível no dispositivo, seu app poderá capturar a ActivityNotFoundException. Na sua lógica de processamento de exceções, você pode mostrar um erro ou tentar processar o arquivo por conta própria.

Se o app precisar saber com antecedência se outro app pode abrir um determinado arquivo, inclua o elemento <intent> no snippet de código a seguir como parte do elemento <queries> no manifesto. Inclua o tipo de arquivo se você já souber qual é no momento da compilação.

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <!-- If you don't know the MIME type in advance, set "mimeType" to "*/*". -->
  <data android:mimeType="application/pdf" />
</intent>

Em seguida, verifique se um app está disponível chamando resolveActivity() com a intent.

Conceder acesso ao URI

Observação: é obrigatório declarar permissões de acesso ao URI para apps destinados ao Android 11 (API de nível 30) ou versões mais recentes, conforme descrito nesta seção. Essa ação é recomendada para todos os apps, seja qual for a versão do SDK de destino e quer eles exportem ou não os provedores de conteúdo.

Para que apps destinados ao Android 11 ou versões mais recentes acessem o URI de conteúdo, a intent do app precisa declarar permissões de acesso do URI configurando uma ou ambas as seguintes sinalizações de intent: FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION.

No Android 11 e versões mais recentes, as permissões de acesso do URI oferecem os seguintes recursos ao app que recebe a intent:

  • Ler ou gravar os dados que o URI de conteúdo representa, dependendo das permissões de URI fornecidas.
  • Ganhar visibilidade do app que contém o provedor de conteúdo correspondente à autoridade de URI. O app que com o provedor de conteúdo pode ser diferente do app que envia a intent.

O snippet de código a seguir demonstra como adicionar uma sinalização de intent de permissões de URI para que outro app destinado ao Android 11 ou versões mais recentes possa visualizar os dados no URI de conteúdo:

Kotlin

val shareIntent = Intent(Intent.ACTION_VIEW).apply {
    flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
    data = CONTENT_URI_TO_SHARE_WITH_OTHER_APP
}

Java

Intent shareIntent = new Intent(Intent.ACTION_VIEW);
shareIntent.setFlags(FLAG_GRANT_READ_URI_PERMISSION);
shareIntent.setData(CONTENT_URI_TO_SHARE_WITH_OTHER_APP);

Conexão com serviços

Caso seu app precise interagir com um serviço que não esteja visível automaticamente, é possível declarar a ação de intent adequada em um elemento <queries>. Nas seções a seguir, confira exemplos usando serviços acessados com frequência.

Conexão com mecanismo de conversão de texto em voz

Se o app interagir com um mecanismo de conversão de texto em voz (TTS, na sigla em inglês), inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.TTS_SERVICE" />
</intent>

Conexão com serviço de reconhecimento de fala

Se o app interagir com um serviço de reconhecimento de fala, inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.speech.RecognitionService" />
</intent>

Conexão com serviços do navegador de mídia

Se o app for um app de navegador de mídia do cliente, inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.media.browse.MediaBrowserService" />
</intent>

Oferecer funcionalidades personalizáveis

Se o app precisar executar ações ou mostrar informações personalizáveis com base nas interações com outros apps, você poderá representar esse comportamento personalizado usando assinaturas de filtro de intent como parte do elemento <queries> no manifesto. As seções a seguir oferecem uma orientação mais detalhada para vários cenários comuns.

Consultar apps de SMS

Se o app precisar de informações sobre o conjunto de apps de SMS instalados em um dispositivo, por exemplo, para conferir qual app é o gerenciador de SMS padrão dele, inclua o seguinte elemento <intent> como parte do elemento <queries> no seu manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.SENDTO"/>
  <data android:scheme="smsto" android:host="*" />
</intent>

Criar uma planilha personalizada

Sempre que possível, use um compartilhamento de arquivos fornecido pelo sistema. Como alternativa, inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.SEND" />
  <!-- Replace with the MIME type that your app works with, if needed. -->
  <data android:mimeType="image/jpeg" />
</intent>

O processo de criação da planilha na lógica do app, como a chamada para queryIntentActivities(), permanece inalterado em comparação com as versões anteriores ao Android 11.

Mostrar ações personalizadas de seleção de texto

Quando os usuários selecionam texto no seu app, uma barra de ferramentas de seleção de texto mostra o conjunto de operações que podem ser feitas no texto selecionado. Se essa barra de ferramentas mostrar ações personalizadas de outros apps, inclua o seguinte elemento <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<intent>
  <action android:name="android.intent.action.PROCESS_TEXT" />
  <data android:mimeType="text/plain" />
</intent>

Mostrar linhas de dados personalizadas para um contato

Os apps podem adicionar linhas de dados personalizadas ao Provedor de contatos. Para que um app de contatos mostre esses dados personalizados, ele precisa fazer o seguinte:

  1. Ler o arquivo contacts.xml dos outros apps.
  2. Carregar um ícone correspondente ao tipo MIME personalizado.

Se o app for um app de contatos, inclua os seguintes elementos <intent> como parte do elemento <queries> no manifesto:

<!-- Place inside the <queries> element. -->
<!-- Lets the app read the contacts.xml file from other apps. -->
<intent>
  <action android:name="android.accounts.AccountAuthenticator" />
</intent>
<!-- Lets the app load an icon corresponding to the custom MIME type. -->
<intent>
  <action android:name="android.intent.action.VIEW" />
  <data android:scheme="content" android:host="com.android.contacts"
        android:mimeType="vnd.android.cursor.item/*" />
</intent>