Crear y compartir un archivo desde el almacenamiento interno
Mi objetivo es crear un archivo XML en el almacenamiento interno y luego enviarlo a través de la intención de compartir.
Puedo crear un archivo XML usando este código
- Content_URI en el proveedor de contenido
- Cómo especificar el orden de clasificación de la consulta a un proveedor de contenido
- Compartir a través de Seekable Pipe o Stream con otra aplicación para Android?
- Android ContentProvider llama ráfagas de setNotificationUri () a CursorAdapter cuando se insertan muchas filas con una operación por lotes
- Proveedor de contenido de Android y relación de muchos a muchos DB
FileOutputStream outputStream = context.openFileOutput(fileName, Context.MODE_WORLD_READABLE); PrintStream printStream = new PrintStream(outputStream); String xml = this.writeXml(); // get XML here printStream.println(xml); printStream.close();
Estoy atrapado tratando de recuperar un Uri en el archivo de salida con el fin de compartirlo. Primero intenté tener acceso al archivo convirtiendo el archivo a un Uri
File outFile = context.getFileStreamPath(fileName); return Uri.fromFile(outFile);
Esto devuelve el archivo: ///data/data/com.my.package/files/myfile.xml pero no puedo adjuntarlo a un correo electrónico, cargar, etc.
Si compruebo manualmente la longitud del archivo, es correcto y muestra que hay un tamaño de archivo razonable.
A continuación, creé un proveedor de contenido y trató de hacer referencia al archivo y no es un identificador válido para el archivo. El ContentProvider
nunca parece ser llamado un punto cualquiera.
Uri uri = Uri.parse("content://" + CachedFileProvider.AUTHORITY + "/" + fileName); return uri;
Esto devuelve el contenido: //com.my.package.provider/myfile.xml pero compruebo el archivo y su longitud cero.
¿Cómo accedo a los archivos correctamente? ¿Necesito crear el archivo con el proveedor de contenido? ¿Si es así, cómo?
Actualizar
Aquí está el código que estoy usando para compartir. Si selecciono Gmail, se mostrará como un archivo adjunto, pero cuando lo envíe da un error No se pudo mostrar el archivo adjunto y el correo electrónico que llega no tiene ningún archivo adjunto.
public void onClick(View view) { Log.d(TAG, "onClick " + view.getId()); switch (view.getId()) { case R.id.share_cancel: setResult(RESULT_CANCELED, getIntent()); finish(); break; case R.id.share_share: MyXml xml = new MyXml(); Uri uri; try { uri = xml.writeXmlToFile(getApplicationContext(), "myfile.xml"); //uri is "file:///data/data/com.my.package/files/myfile.xml" Log.d(TAG, "Share URI: " + uri.toString() + " path: " + uri.getPath()); File f = new File(uri.getPath()); Log.d(TAG, "File length: " + f.length()); // shows a valid file size Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.putExtra(Intent.EXTRA_STREAM, uri); shareIntent.setType("text/plain"); startActivity(Intent.createChooser(shareIntent, "Share")); } catch (FileNotFoundException e) { e.printStackTrace(); } break; } }
Me di cuenta de que hay una Exception
lanzada aquí desde dentro de createChooser (…) , pero no puedo entender por qué se ha lanzado.
E / ActivityThread (572): Actividad com.android.internal.app.ChooserActivity ha filtrado IntentReceiver com.android.internal.app.ResolverActivity$1@4148d658 que fue registrado originalmente aquí. ¿Le falta una llamada para anular el registroReceiver ()?
He investigado este error y no puedo encontrar nada obvio. Estos dos enlaces sugieren que necesito anular el registro de un receptor.
- ChooserActivity ha filtrado IntentReceiver
- ¿Por qué Intent.createChooser () necesita un BroadcastReceiver y cómo implementarlo?
Tengo una configuración de receptor, pero es para un AlarmManager
que se establece en otro lugar y no requiere que la aplicación se registre o cancele el registro.
Código para openFile (…)
En caso de que sea necesario, aquí está el proveedor de contenido que he creado.
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { String fileLocation = getContext().getCacheDir() + "/" + uri.getLastPathSegment(); ParcelFileDescriptor pfd = ParcelFileDescriptor.open(new File(fileLocation), ParcelFileDescriptor.MODE_READ_ONLY); return pfd; }
- Negación de permisos de Android en Widget RemoteViewsFactory para contenido
- Declaración de proveedor de contenido
- ¿Es posible simular el cursor sqlite3 en Android cuando no existe db o tabla?
- Insertar una nueva foto de contacto para Android
- ¿Cómo usar el proveedor de contenido de SMS? ¿Dónde están los documentos?
- ¿Cómo crear un ContentProvider de Thread-safe?
- Eliminar varias filas de contentprovider
- ¿Cómo consultar al proveedor de contenido de Android MediaStore, evitando las imágenes huérfanas?
Es posible exponer un archivo almacenado en su directorio privado de aplicaciones a través de un ContentProvider. He aquí un ejemplo de código que hice mostrando cómo crear un proveedor de contenido que puede hacer esto.
Manifiesto
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.providertest" android:versionCode="1" android:versionName="1.0"> <uses-sdk android:minSdkVersion="11" android:targetSdkVersion="15" /> <application android:label="@string/app_name" android:icon="@drawable/ic_launcher" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name="MyProvider" android:authorities="com.example.prov" android:exported="true" /> </application> </manifest>
En su ContentProvider anule openFile para devolver el ParcelFileDescriptor
@Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { File cacheDir = getContext().getCacheDir(); File privateFile = new File(cacheDir, "file.xml"); return ParcelFileDescriptor.open(privateFile, ParcelFileDescriptor.MODE_READ_ONLY); }
Asegúrese de haber copiado el archivo xml en el directorio de caché
private void copyFileToInternal() { try { InputStream is = getAssets().open("file.xml"); File cacheDir = getCacheDir(); File outFile = new File(cacheDir, "file.xml"); OutputStream os = new FileOutputStream(outFile.getAbsolutePath()); byte[] buff = new byte[1024]; int len; while ((len = is.read(buff)) > 0) { os.write(buff, 0, len); } os.flush(); os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); // TODO: should close streams properly here } }
Ahora cualquier otra aplicación debe ser capaz de obtener un InputStream para su archivo privado utilizando el contenido uri (content: //com.example.prov/myfile.xml)
Para una prueba sencilla, llame al proveedor de contenido desde una aplicación independiente similar a la siguiente
private class MyTask extends AsyncTask<String, Integer, String> { @Override protected String doInBackground(String... params) { Uri uri = Uri.parse("content://com.example.prov/myfile.xml"); InputStream is = null; StringBuilder result = new StringBuilder(); try { is = getApplicationContext().getContentResolver().openInputStream(uri); BufferedReader r = new BufferedReader(new InputStreamReader(is)); String line; while ((line = r.readLine()) != null) { result.append(line); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (is != null) is.close(); } catch (IOException e) { } } return result.toString(); } @Override protected void onPostExecute(String result) { Toast.makeText(CallerActivity.this, result, Toast.LENGTH_LONG).show(); super.onPostExecute(result); } }
Así que la respuesta de Rob es correcta supongo pero lo hice un poco diferente. Por lo que entiendo, con la configuración en el proveedor:
android:exported="true"
Usted está dando acceso público a todos sus archivos ?! De todos modos, una manera de dar sólo acceso a algunos archivos es definir permisos de ruta de archivo de la siguiente manera:
<provider android:authorities="com.your.app.package" android:name="android.support.v4.content.FileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
Y luego en su directorio XML se define el archivo file_paths.xml como sigue:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path path="/" name="allfiles" /> <files-path path="tmp/" name="tmp" /> </paths>
Ahora, el "allfiles" da el mismo tipo de permiso público, supongo que como la opción android: exportado = "true", pero en realidad no quieres que supongo que para definir un subdirectorio es la siguiente línea. Entonces todo lo que tienes que hacer es almacenar los archivos que quieres compartir, allí en ese directorio.
A continuación, lo que tienes que hacer es, como también dice Rob, obtener un URI para este archivo. Así es como lo hice:
Uri contentUri = FileProvider.getUriForFile(context, "com.your.app.package", sharedFile);
Entonces, cuando tengo este URI, tuve que adjuntar a él permisos para que otra aplicación lo utilizara. Yo estaba usando o enviando este archivo URI a la aplicación de cámara. De todos modos esta es la forma en que obtuve la información del paquete de otra aplicación y concedió permisos para el URI:
PackageManager packageManager = getPackageManager(); List<ResolveInfo> list = packageManager.queryIntentActivities(cameraIntent, PackageManager.MATCH_DEFAULT_ONLY); if (list.size() < 1) { return; } String packageName = list.get(0).activityInfo.packageName; grantUriPermission(packageName, sharedFileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); ClipData clipData = ClipData.newRawUri("CAMFILE", sharedFileUri); cameraIntent.setClipData(clipData); cameraIntent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION); cameraIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivityForResult(cameraIntent, GET_FROM_CAMERA);
Dejé el código para la cámara, ya que no quería tomar algún otro ejemplo que no funcionó. Pero esta manera usted ve que usted puede atar permisos al URI sí mismo.
La cosa de la cámara es que puedo fijarla vía ClipData y después fijar también permisos. Supongo que en su caso sólo necesita FLAG_GRANT_READ_URI_PERMISSION al adjuntar un archivo a un correo electrónico.
Aquí está el enlace para ayudar en FileProvider como base de todos mis mensajes en la información que encontré allí. Tuve algunos problemas para encontrar un paquete de información para la aplicación de la cámara sin embargo.
Espero eso ayude.
Si necesitas permiso para que otras aplicaciones vean los archivos privados de tu aplicación (para compartir o de otra forma), podrías ahorrar algo de tiempo y usar FileProvider de la biblioteca v4 compat
- Cómo establecer emoji por unicode en android textview
- Cómo agregar la barra de desplazamiento a la disposición relativa?