Dopo aver configurato l'app per la condivisione di file utilizzando gli URI dei contenuti, puoi rispondere a messaggi di altre app richieste per questi file. Un modo per rispondere a queste richieste è selezionare i file a riga di comando dell'app server che altre applicazioni possono richiamare. Questo approccio consente a un cliente per consentire agli utenti di selezionare un file dall'app server e ricevere poi i l'URI contenuto.
Questa lezione mostra come creare una selezione di file Activity
nella tua app
che risponde alle richieste dei file.
Ricevi richieste di file
Per ricevere richieste di file dalle app client e rispondere con un URI dei contenuti, l'app deve
seleziona Activity
. Le app client avviano questa operazione
Activity
chiamando startActivityForResult()
con un Intent
contenente l'azione
ACTION_PICK
. Quando l'app client chiama
startActivityForResult()
, la tua app può
restituiscono un risultato all'app client, sotto forma di URI dei contenuti per il file selezionato dall'utente.
Per scoprire come implementare una richiesta per un file in un'app client, consulta la lezione Richiedi un file condiviso.
Crea un'attività di selezione file
Per configurare la selezione di file Activity
, inizia specificando la
Activity
nel manifest, insieme a un filtro per intent
che corrisponde all'azione ACTION_PICK
e
categorie CATEGORY_DEFAULT
e
CATEGORY_OPENABLE
. Aggiungi anche filtri di tipo MIME
per i file che la tua app pubblica su altre app. Il seguente snippet mostra come specificare
nuovo Activity
e filtro per intent:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> ... <application> ... <activity android:name=".FileSelectActivity" android:label="@File Selector" > <intent-filter> <action android:name="android.intent.action.PICK"/> <category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.OPENABLE"/> <data android:mimeType="text/plain"/> <data android:mimeType="image/*"/> </intent-filter> </activity>
Definisci l'attività di selezione dei file nel codice
Poi definisci una sottoclasse Activity
che mostri i file disponibili
alla directory files/images/
dell'app nella memoria interna e consente all'utente di selezionare
del file desiderato. Il seguente snippet mostra come definirlo
Activity
e rispondi alla selezione dell'utente:
Kotlin
class MainActivity : Activity() { // The path to the root of this app's internal storage private lateinit var privateRootDir: File // The path to the "images" subdirectory private lateinit var imagesDir: File // Array of files in the images subdirectory private lateinit var imageFiles: Array<File> // Array of filenames corresponding to imageFiles private lateinit var imageFilenames: Array<String> // Initialize the Activity override fun onCreate(savedInstanceState: Bundle?) { ... // Set up an Intent to send back to apps that request a file resultIntent = Intent("com.example.myapp.ACTION_RETURN_FILE") // Get the files/ subdirectory of internal storage privateRootDir = filesDir // Get the files/images subdirectory; imagesDir = File(privateRootDir, "images") // Get the files in the images subdirectory imageFiles = imagesDir.listFiles() // Set the Activity's result to null to begin with setResult(Activity.RESULT_CANCELED, null) /* * Display the file names in the ListView fileListView. * Back the ListView with the array imageFilenames, which * you can create by iterating through imageFiles and * calling File.getAbsolutePath() for each File */ ... } ... }
Java
public class MainActivity extends Activity { // The path to the root of this app's internal storage private File privateRootDir; // The path to the "images" subdirectory private File imagesDir; // Array of files in the images subdirectory File[] imageFiles; // Array of filenames corresponding to imageFiles String[] imageFilenames; // Initialize the Activity @Override protected void onCreate(Bundle savedInstanceState) { ... // Set up an Intent to send back to apps that request a file resultIntent = new Intent("com.example.myapp.ACTION_RETURN_FILE"); // Get the files/ subdirectory of internal storage privateRootDir = getFilesDir(); // Get the files/images subdirectory; imagesDir = new File(privateRootDir, "images"); // Get the files in the images subdirectory imageFiles = imagesDir.listFiles(); // Set the Activity's result to null to begin with setResult(Activity.RESULT_CANCELED, null); /* * Display the file names in the ListView fileListView. * Back the ListView with the array imageFilenames, which * you can create by iterating through imageFiles and * calling File.getAbsolutePath() for each File */ ... } ... }
Rispondere a una selezione di file
Una volta che un utente seleziona un file condiviso, l'applicazione deve determinare quale file è stato selezionato e
generiamo un URI dei contenuti per il file. Poiché Activity
mostra
elenco dei file disponibili in un ListView
, quando l'utente fa clic sul nome di un file
il sistema chiama il metodo onItemClick()
, in cui è possibile ottenere il file selezionato.
Quando utilizzi un intent per inviare l'URI di un file da un'app all'altra,
devi fare attenzione a ottenere un URI che
possono leggere. Questa operazione viene eseguita sui dispositivi con Android 6.0 (livello API 23) e versioni successive.
richiede un'offerta speciale
per le modifiche al modello di autorizzazioni in quella versione di Android, in particolare
di READ_EXTERNAL_STORAGE
diventare un
autorizzazione pericolosa, che potrebbe mancare all'app ricevente.
Tenendo presenti queste considerazioni, ti consigliamo di evitare di utilizzare
Uri.fromFile()
, che
presenta diversi svantaggi. Questo metodo:
- Non consente la condivisione di file tra profili.
- È necessario che l'app abbia
WRITE_EXTERNAL_STORAGE
sui dispositivi con Android 4.4 (livello API 19) o versioni precedenti. - È necessario che le app che ricevono i seguenti
READ_EXTERNAL_STORAGE
, che avrà esito negativo su destinazioni di condivisione importanti, come Gmail, che non hanno questa autorizzazione.
Anziché utilizzare Uri.fromFile()
,
puoi utilizzare le autorizzazioni URI per concedere altre app
l'accesso a URI specifici. Sebbene le autorizzazioni URI non funzionino sugli URI file://
generate da Uri.fromFile()
,
per gli URI associati ai fornitori di contenuti. La
L'API FileProvider
può
nella creazione di questi URI. Questo approccio funziona anche con i file che non sono
nella memoria esterna, ma nella memoria locale dell'app che invia l'intent.
In onItemClick()
, ricevi un
File
oggetto per il nome del file selezionato e passalo come argomento a
getUriForFile()
, insieme ai
dell'autorità che hai specificato
Elemento <provider>
per FileProvider
.
L'URI dei contenuti risultante contiene l'autorità, un segmento di percorso corrispondente
(come specificato nei metadati XML) e il nome del file, compreso il relativo
. In che modo FileProvider
mappa le directory al percorso
i segmenti basati su metadati XML sono descritti nella sezione
Specifica le directory condivisibili.
Il seguente snippet mostra come rilevare il file selezionato e ottenere un URI dei contenuti per il file:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> /* * Get a File for the selected file name. * Assume that the file names are in the * imageFilename array. */ val requestFile = File(imageFilenames[position]) /* * Most file-related method calls need to be in * try-catch blocks. */ // Use the FileProvider to get a content URI val fileUri: Uri? = try { FileProvider.getUriForFile( this@MainActivity, "com.example.myapp.fileprovider", requestFile) } catch (e: IllegalArgumentException) { Log.e("File Selector", "The selected file can't be shared: $requestFile") null } ... } ... }
Java
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override /* * When a filename in the ListView is clicked, get its * content URI and send it to the requesting app */ public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { /* * Get a File for the selected file name. * Assume that the file names are in the * imageFilename array. */ File requestFile = new File(imageFilename[position]); /* * Most file-related method calls need to be in * try-catch blocks. */ // Use the FileProvider to get a content URI try { fileUri = FileProvider.getUriForFile( MainActivity.this, "com.example.myapp.fileprovider", requestFile); } catch (IllegalArgumentException e) { Log.e("File Selector", "The selected file can't be shared: " + requestFile.toString()); } ... } }); ... }
Ricorda che puoi generare URI dei contenuti solo per i file che risiedono in una directory
specificato nel file di metadati che contiene l'elemento <paths>
,
descritto nella sezione Specificare le directory condivisibili. Se chiami
getUriForFile()
per un
File
in un percorso che non hai specificato, riceverai un
IllegalArgumentException
.
Concedi le autorizzazioni per il file
Ora che disponi di un URI dei contenuti per il file che vuoi condividere con un'altra app, devi
consentire all'app client di accedere al file. Per consentire l'accesso, concedi le autorizzazioni all'app client
aggiungendo l'URI dei contenuti a un Intent
e impostando i flag di autorizzazione
Intent
. Le autorizzazioni concesse sono temporanee e scadono
automaticamente al termine dell'elenco di attività dell'app ricevente.
Il seguente snippet di codice mostra come impostare l'autorizzazione di lettura per il file:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> ... if (fileUri != null) { // Grant temporary read permission to the content URI resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) ... } ... } ... }
Java
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks in the ListView fileListView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { ... if (fileUri != null) { // Grant temporary read permission to the content URI resultIntent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION); } ... } ... }); ... }
Attenzione: la chiamata a setFlags()
è l'unica soluzione
per concedere in modo sicuro l'accesso ai tuoi file usando le autorizzazioni di accesso temporanee. Evita di chiamare
Metodo Context.grantUriPermission()
per un
l'URI dei contenuti del file, poiché questo metodo concede l'accesso che puoi revocare solo
chiamata al numero Context.revokeUriPermission()
.
Non usare Uri.fromFile()
. Obbliga la ricezione di app
per avere l'autorizzazione READ_EXTERNAL_STORAGE
,
non funzionano se la condivisione con più utenti e nelle versioni
di Android precedente alla 4.4 (livello API 19), sarebbero necessarie la tua
per avere WRITE_EXTERNAL_STORAGE
.
E i target di condivisione molto importanti, come l'app Gmail, non hanno
READ_EXTERNAL_STORAGE
, causando
la chiamata non andrà a buon fine.
Puoi invece utilizzare le autorizzazioni URI per concedere ad altre app l'accesso a URI specifici.
Sebbene le autorizzazioni URI non funzionino sugli URI file:// generati
Uri.fromFile()
, sì
lavorare su URI associati ai Fornitori di contenuti. Anziché implementare il tuo solo per questo,
puoi e dovresti usare FileProvider
come spiegato in Condivisione di file.
Condividi il file con l'app che ha inviato la richiesta
Per condividere il file con l'app che lo ha richiesto, trasmetti il Intent
contenente l'URI dei contenuti e le autorizzazioni per setResult()
. Una volta completata la definizione del campo Activity
che hai appena definito,
invia l'elemento Intent
contenente l'URI dei contenuti all'app client.
Il seguente snippet di codice mostra come eseguire questa operazione:
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> ... if (fileUri != null) { ... // Put the Uri and MIME type in the result Intent resultIntent.setDataAndType(fileUri, contentResolver.getType(fileUri)) // Set the result setResult(Activity.RESULT_OK, resultIntent) } else { resultIntent.setDataAndType(null, "") setResult(RESULT_CANCELED, resultIntent) } } }
Java
protected void onCreate(Bundle savedInstanceState) { ... // Define a listener that responds to clicks on a file in the ListView fileListView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long rowId) { ... if (fileUri != null) { ... // Put the Uri and MIME type in the result Intent resultIntent.setDataAndType( fileUri, getContentResolver().getType(fileUri)); // Set the result MainActivity.this.setResult(Activity.RESULT_OK, resultIntent); } else { resultIntent.setDataAndType(null, ""); MainActivity.this.setResult(RESULT_CANCELED, resultIntent); } } });
Fornisci agli utenti un modo per tornare immediatamente all'app client dopo aver scelto un file.
Un modo per farlo è inserire un segno di spunta o il pulsante Fine. Associa un metodo a
utilizzando il metodo
Attributo android:onClick
. Nel metodo, richiama
finish()
. Ad esempio:
Kotlin
fun onDoneClick(v: View) { // Associate a method with the Done button finish() }
Java
public void onDoneClick(View v) { // Associate a method with the Done button finish(); }
Per ulteriori informazioni correlate, consulta:
- Progettazione degli URI dei contenuti
- Implementazione delle autorizzazioni del fornitore di contenuti
- Autorizzazioni
- Filtri per intent e intent