Soddisfare casi d'uso comuni con una visibilità dei pacchetti limitata

Questo documento presenta diversi casi d'uso comuni in cui un'app interagisce con altre app. Ogni sezione fornisce indicazioni su come implementare la funzionalità dell'app con visibilità limitata dei pacchetti, che devi prendere in considerazione se la tua app ha come target Android 11 (livello API 30) o versioni successive.

Quando un'app che ha come target Android 11 o versioni successive utilizza un intent per avviare un'attività in un'altra app, l'approccio più semplice è richiamare l'intent e gestire l'eccezione ActivityNotFoundException se non è disponibile alcuna app.

Se una parte della tua app dipende dalla possibilità di eseguire la chiamata a startActivity(), ad esempio per mostrare una UI, aggiungi un elemento all'elemento <queries> del manifest della tua app. In genere, si tratta di un elemento <intent>.

Apri URL

Questa sezione descrive vari modi per aprire gli URL in un'app che ha come target Android 11 o versioni successive.

Aprire URL in un browser o in un'altra app

Per aprire un URL, utilizza un intent che contenga l'azione ACTION_VIEW, come descritto nella guida al caricamento di un URL web. Dopo aver chiamato startActivity() utilizzando questo intent, si verifica una delle seguenti situazioni:

  • L'URL si apre in un'app browser web.
  • L'URL si apre in un'app che lo supporta come link diretto.
  • Viene visualizzata una finestra di dialogo di scelta che consente all'utente di scegliere l'app che apre l'URL.
  • Si verifica un ActivityNotFoundException perché sul dispositivo non è installata un'app in grado di aprire l'URL. È una cosa insolita.

    Ti consigliamo di fare in modo che la tua app rilevi e gestisca l'errore ActivityNotFoundException se si verifica.

Poiché il metodo startActivity() non richiede la visibilità del pacchetto per avviare l'attività di un'altra applicazione, non devi aggiungere un elemento <queries> al manifest dell'app né apportare modifiche a un elemento <queries> esistente. Questo vale sia per gli intent impliciti che per quelli espliciti che aprono un URL.

Controllare se è disponibile un browser

In alcuni casi, l'app potrebbe voler verificare che sul dispositivo sia disponibile almeno un browser o che un browser specifico sia il browser predefinito prima di tentare di aprire un URL. In questi casi, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

<!-- 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 chiami queryIntentActivities() e passi un intent web come argomento, l'elenco restituito include in alcuni casi le app browser disponibili. L'elenco non include le app browser se l'utente ha configurato l'URL in modo che si apra per impostazione predefinita in un'app non browser.

Aprire gli URL nelle schede personalizzate

Le schede personalizzate consentono a un'app di personalizzare l'aspetto del browser. Puoi aprire un URL in una scheda personalizzata senza dover aggiungere o modificare l'elemento <queries> nel manifest dell'app.

Tuttavia, ti consigliamo di verificare se il dispositivo ha un browser che supporta le schede personalizzate o di selezionare un browser specifico da avviare con le schede personalizzate utilizzando CustomTabsClient.getPackageName(). In questi casi, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

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

Consentire alle app non browser di gestire gli URL

Anche se la tua app può aprire URL utilizzando le schede personalizzate, ti consigliamo di consentire a un'app non browser di aprire un URL, se possibile. Per fornire questa funzionalità nella tua app, prova a chiamare startActivity() utilizzando un intent che imposta il flag FLAG_ACTIVITY_REQUIRE_NON_BROWSER dell'intent. Se il sistema genera un ActivityNotFoundException, la tua app può aprire l'URL in una scheda personalizzata.

Se un intent include questo flag, una chiamata a startActivity() causa la generazione di un ActivityNotFoundException quando si verifica una delle seguenti condizioni:

  • La chiamata avrebbe avviato direttamente un'app browser.
  • La chiamata avrebbe mostrato all'utente una finestra di dialogo di disambiguazione in cui le uniche opzioni sono le app del browser.

Il seguente snippet di codice mostra come aggiornare la logica per utilizzare il flag di 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);
}

Evitare una finestra di dialogo di disambiguazione

Se vuoi evitare di mostrare la finestra di dialogo di disambiguazione che gli utenti potrebbero vedere quando aprono un URL e preferisci gestire l'URL autonomamente in queste situazioni, puoi utilizzare un intent che imposta il flag dell'intent FLAG_ACTIVITY_REQUIRE_DEFAULT.

Se un intent include questo flag, una chiamata a startActivity() causa la generazione di un ActivityNotFoundException quando la chiamata avrebbe mostrato una finestra di dialogo di disambiguazione all'utente.

Se un intent include sia questo flag sia il flag FLAG_ACTIVITY_REQUIRE_NON_BROWSER dell'intent, una chiamata a startActivity() genera un ActivityNotFoundException quando si verifica una delle seguenti condizioni:

  • La chiamata avrebbe avviato direttamente l'app del browser.
  • La chiamata avrebbe mostrato all'utente una finestra di dialogo di disambiguazione.

Il seguente snippet di codice mostra come utilizzare insieme i flag FLAG_ACTIVITY_REQUIRE_NON_BROWSER e FLAG_ACTIVITY_REQUIRE_DEFAULT:

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);
}

Apri un file

Se la tua app gestisce file o allegati, ad esempio controllando se un dispositivo può aprire un determinato file, di solito è più facile provare ad avviare un'attività in grado di gestire il file. Per farlo, utilizza un intent che includa l'azione dell'intent ACTION_VIEW e l'URI che rappresenta il file specifico. Se non è disponibile alcuna app sul dispositivo, la tua app può intercettare l'intent ActivityNotFoundException. Nella logica di gestione delle eccezioni, puoi mostrare un errore o provare a gestire il file autonomamente.

Se la tua app deve sapere in anticipo se un'altra app può aprire un determinato file, includi l'elemento <intent> nel seguente snippet di codice come parte dell'elemento <queries> nel manifest. Includi il tipo di file se lo conosci già in fase di compilazione.

<!-- 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>

A questo punto, puoi verificare se un'app è disponibile chiamando resolveActivity() con il tuo intento.

Concedere l'accesso all'URI

Nota:la dichiarazione delle autorizzazioni di accesso agli URI descritta in questa sezione è obbligatoria per le app destinate ad Android 11 (livello API 30) o versioni successive e consigliata per tutte le app, indipendentemente dalla versione dell'SDK di destinazione e dal fatto che esportino i propri content provider.

Per le app che hanno come target Android 11 o versioni successive per accedere all'URI dei contenuti, l'intent dell'app deve dichiarare le autorizzazioni di accesso all'URI impostando uno o entrambi i seguenti flag di intent: FLAG_GRANT_READ_URI_PERMISSION e FLAG_GRANT_WRITE_URI_PERMISSION.

Su Android 11 e versioni successive, le autorizzazioni di accesso agli URI offrono le seguenti funzionalità all'app che riceve l'intent:

  • Leggere o scrivere i dati rappresentati dall'URI contenuto, a seconda delle autorizzazioni URI specificate.
  • Ottieni visibilità sull'app contenente il fornitore di contenuti che corrisponde all'autorità URI. L'app che contiene il fornitore di contenuti potrebbe essere diversa dall'app che invia l'intent.

Il seguente snippet di codice mostra come aggiungere un flag di intent per le autorizzazioni URI in modo che un'altra app che ha come target Android 11 o versioni successive possa visualizzare i dati nell'URI dei contenuti:

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);

Connettersi ai servizi

Se la tua app deve interagire con un servizio non visibile automaticamente, puoi dichiarare l'azione intent appropriata all'interno di un elemento <queries>. Le sezioni seguenti forniscono esempi che utilizzano servizi a cui si accede di frequente.

Connettersi a un motore di sintesi vocale

Se la tua app interagisce con un motore di sintesi vocale, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

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

Connettersi a un servizio di riconoscimento vocale

Se la tua app interagisce con un servizio di riconoscimento vocale, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

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

Connessione ai servizi di esplorazione dei contenuti multimediali

Se la tua app è un'app di navigazione multimediale client, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

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

Fornire funzionalità personalizzate

Se la tua app deve eseguire azioni personalizzabili o mostrare informazioni personalizzabili in base alle sue interazioni con altre app, puoi rappresentare questo comportamento personalizzato utilizzando le firme del filtro per intent come parte dell'elemento <queries> nel manifest. Le sezioni seguenti forniscono indicazioni dettagliate per diversi scenari comuni.

Query per app di messaggistica

Se la tua app ha bisogno di informazioni sull'insieme di app di messaggistica installate su un dispositivo, ad esempio per controllare quale app è il gestore SMS predefinito del dispositivo, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

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

Creare un foglio di condivisione personalizzato

Se possibile, utilizza un foglio di condivisione fornito dal sistema. In alternativa, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

<!-- 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>

Il processo di creazione del foglio di condivisione nella logica della tua app, ad esempio la chiamata a queryIntentActivities(), rimane invariato rispetto alle versioni di Android precedenti ad Android 11.

Mostrare le azioni di selezione del testo personalizzate

Quando gli utenti selezionano il testo nella tua app, una barra degli strumenti di selezione del testo mostra l'insieme delle possibili operazioni da eseguire sul testo selezionato. Se questa barra degli strumenti mostra azioni personalizzate di altre app, includi il seguente elemento <intent> come parte dell'elemento <queries> nel manifest:

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

Mostrare le righe di dati personalizzati per un contatto

Le app possono aggiungere righe di dati personalizzati al provider di contatti. Affinché un'app di contatti mostri questi dati personalizzati, deve essere in grado di eseguire le seguenti operazioni:

  1. Leggi il file contacts.xml delle altre app.
  2. Carica un'icona corrispondente al tipo MIME personalizzato.

Se la tua app è un'app di contatti, includi i seguenti elementi <intent> come parte dell'elemento <queries> nel manifest:

<!-- 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>