Comment obtenir le résultat de la caméra en tant qu'uri dans le dossier de données?
je crée une application dans laquelle je veux capturer une image et puis je veux envoyer cette image dans le courriel en pièce jointe.
j'ouvre une caméra en utilisant l'action android.provider.MediaStore.ACTION_IMAGE_CAPTURE
et je passe L'Uri du fichier comme paramètre EXTRA_OUTPUT
pour ramener l'image au fichier. Cela fonctionne parfaitement, et je suis en mesure d'obtenir l'image capturée si j'utilise le external storage uri
comme un EXTRA_OUTPUT
mais si j'utilise le dossier data uri, elle n'est pas de travail et la caméra ne se ferme pas et tous les boutons ne fonctionnent pas.
Voici mon code pour obtenir le résultat dans le répertoire de stockage externe
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = Environment.getExternalStorageDirectory();
out = new File(out, imagename);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
Et ce code est pour obtenir l'image dans le dossier de données
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
File out = getFilesDir();
out = new File(out, MyPharmacyOptions.PRESCRIPTION_IMAGE_NAME);
i.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(out));
startActivityForResult(i, CAMERA_RESULT);
je savais que le dossier de données n'est pas accessible à la troisième application donc cela peut causer un problème donc j'ai créé un fournisseur de contenu pour partager le fichier.
Voici mon contenu fournir la classe
public class MyContentProvider extends ContentProvider {
private static final String Tag = RingtonContentProvider.class.getName();
public static final Uri CONTENT_URI = Uri
.parse("content://x.y.z/");
private static final HashMap<String, String> MIME_TYPES = new HashMap<String, String>();
static {
MIME_TYPES.put(".mp3", "audio/mp3");
MIME_TYPES.put(".wav", "audio/mp3");
MIME_TYPES.put(".jpg", "image/jpeg");
}
@Override
public boolean onCreate() {
return true;
}
@Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), uri.getPath());
if (f.exists()) {
return (ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY));
}
throw new FileNotFoundException(uri.getPath());
}
@Override
public Cursor query(Uri url, String[] projection, String selection,
String[] selectionArgs, String sort) {
throw new RuntimeException("Operation not supported");
}
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
File file = new File(getContext().getFilesDir(), uri.getPath());
if(file.exists()) file.delete();
try {
file.createNewFile();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return Uri.fromFile(file);
}
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs) {
throw new RuntimeException("Operation not supported");
}
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
File f = new File(getContext().getFilesDir(), "image1.jpg");
if(f.exists()) f.delete();
f = new File(getContext().getFilesDir(), "image2.jpg");
if(f.exists()) f.delete();
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
}
}
donc pour utiliser ce contenu fournir que j'utilise le code suivant pour passer l'uri à l'activité de caméra
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
Uri uri = MyContentProvider.CONTENT_URI;
uri = Uri.withAppendedPath(uri, imagename);
getContentResolver().insert(uri, null);
getContentResolver().notifyChange(RingtonContentProvider.CONTENT_URI, null);
Log.d(Tag, uri.toString());
i.putExtra(MediaStore.EXTRA_OUTPUT, uri);
startActivityForResult(i, CAMERA_RESULT);
maintenant si je passe l'url autre que le répertoire de stockage externe la caméra s'ouvre mais ne se ferme pas dans l'émulateur mais dans l'appareil la caméra va se fermer mais je ne reçois pas le résultat.
j'ai déclaré ce contenu fournir dans le manifeste
<provider
android:name=".contentproviders.MyContentProvider"
android:authorities="x.y.z" />
Aussi, j'ai donné l'autorisation écrire le stockage externe et aussi pour utilisation de la caméra .
je suis capable de capturer l'image en utilisant le stockage externe, mais je veux stocker l'image dans le répertoire de données au lieu du stockage externe parce que si le stockage externe n'est pas disponible, je veux capturer l'image et je veux envoyer du courrier.
si je crée le contenu provide puis je peux également partager mon image à l'application de courrier électronique.
si nous ne fournissons pas les extras avec l'intention de caméra il retournera l'image comme un octet[] dans le résultat de l'activité comme un extra de données, mais c'est pour le but de la vignette donc je ne peux pas obtenir l'image de haute résolution en utilisant cette façon.
6 réponses
Il y a deux façons de résoudre ce problème.
1. Enregistrer bitmap que vous avez reçu de la méthode onActivityResult
vous pouvez démarrer l'appareil photo par intention de capturer la photo en utilisant le code ci-dessous
Intent cameraIntent=new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
après la photo de capture, vous obtiendrez bitmap dans la méthode onActivityResult
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
maintenant vous pouvez simplement sauvegarder ce bitmap dans le stockage interne
Note: ici l'objet bitmap se compose d'une image de pouce, il n'aura pas une image de pleine résolution
2. Enregistrer bitmap directement dans le stockage interne en utilisant le fournisseur de contenu
ici, nous allons créer la classe de fournisseur de contenu pour permettre l'Autorisation du répertoire de stockage local à l'activité de caméra
exemple de fournisseur D'échantillon selon ci-dessous
public class MyFileContentProvider extends ContentProvider {
public static final Uri CONTENT_URI = Uri.parse
("content://com.example.camerademo/");
private static final HashMap<String, String> MIME_TYPES =
new HashMap<String, String>();
static {
MIME_TYPES.put(".jpg", "image/jpeg");
MIME_TYPES.put(".jpeg", "image/jpeg");
}
@Override
public boolean onCreate() {
try {
File mFile = new File(getContext().getFilesDir(), "newImage.jpg");
if(!mFile.exists()) {
mFile.createNewFile();
}
getContext().getContentResolver().notifyChange(CONTENT_URI, null);
return (true);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
@Override
public String getType(Uri uri) {
String path = uri.toString();
for (String extension : MIME_TYPES.keySet()) {
if (path.endsWith(extension)) {
return (MIME_TYPES.get(extension));
}
}
return (null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
throws FileNotFoundException {
File f = new File(getContext().getFilesDir(), "newImage.jpg");
if (f.exists()) {
return (ParcelFileDescriptor.open(f,
ParcelFileDescriptor.MODE_READ_WRITE));
}
throw new FileNotFoundException(uri.getPath());
}
}
après que vous pouvez simplement utiliser L'URI pour passer à l'activité de caméra en utilisant le code ci-dessous
Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);
Si vous ne voulez pas créer votre propre fournisseur, vous pouvez utiliser FileProvider de soutien-bibliothèque-v4. Pour une aide détaillée, vous pouvez regarder dans ce post
la meilleure solution que j'ai trouvée est: FileProvider (needs support-library-v4) Il utilise le stockage interne! https://developer.android.com/reference/android/support/v4/content/FileProvider.html
-
Définissez votre FileProvider dans le Manifeste de l'élément de la demande:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="your.package.name.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/image_path" /> </provider>
-
ajouter les permissions dans l'élément racine manifeste:
<uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" android:required="false" />
-
Définissez vos chemins d'image dans par exemple res/xml/image_path.xml:
<paths xmlns:android="http://schemas.android.com/apk/res/android"> <files-path name="captured_image" path="your/path/"/> </paths>
-
Java:
private static final int IMAGE_REQUEST_CODE = 1; // your authority, must be the same as in your manifest file private static final String CAPTURE_IMAGE_FILE_PROVIDER = "your.package.name.fileprovider";
4.1 intention de capture:
File path = new File(activity.getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File image = new File(path, "image.jpg");
Uri imageUri = FileProvider.getUriForFile(activity, CAPTURE_IMAGE_FILE_PROVIDER, image);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(intent, IMAGE_REQUEST_CODE);
4.2 onActivityResult ():
@Override
public void onActivityResult(int requestCode, int resultCode, Intent intent) {
if (requestCode == IMAGE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
File path = new File(getFilesDir(), "your/path");
if (!path.exists()) path.mkdirs();
File imageFile = new File(path, "image.jpg");
// use imageFile to open your image
}
}
super.onActivityResult(requestCode, resultCode, intent);
}
intention d'appeler pour capturer la photo,
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
puis prendre Bitmap dans ActivityResult
if (requestCode == CAMERA_REQUEST) {
Bitmap photo = (Bitmap) data.getExtras().get("data");
}
alors écrivez cela dans la mémoire interne, voir ce
// The openfileOutput() method creates a file on the phone/internal storage in the context of your application
final FileOutputStream fos = openFileOutput("my_new_image.jpg", Context.MODE_PRIVATE);
// Use the compress method on the BitMap object to write image to the OutputStream
bm.compress(CompressFormat.JPEG, 90, fos);
puis la prochaine fois pour lire ce fichier,
Bitmap bitmap = BitmapFactory.decodeFile(file);
d'abord save est venu photo dans votre stockage externe et essayer -
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
this.imageView = (ImageView)this.findViewById(R.id.imageView1);
Button photoButton = (Button) this.findViewById(R.id.button1);
photoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, CAMERA_REQUEST);
}
});
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == CAMERA_REQUEST) {
Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray(); // convert camera photo to byte array
// save it in your external storage.
FileOutputStream fo = new FileOutputStream(new File(Environment.getExternalStorageDirectory() + "/_camera.png"));
fo.write(byteArray);
fo.flush();
fo.close();
}
}
à Côté de la cible
File cameraFile = new File(Environment.getExternalStorageDirectory() + "/_camera.png");
startActivityForResult(Intent.createChooser(new Intent(Intent.ACTION_SEND)
.setType("image/jpg")
.putExtra(Intent.EXTRA_SUBJECT, "Subject")
.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(cameraFile))
.putExtra(Intent.EXTRA_TEXT, textBody), "Send your message using"), Constant.EMAIL);
vous pouvez également le faire sans avoir besoin d'un fournisseur de contenu puisque vous aurez besoin de la carte sd pour ouvrir l'objectif de capture d'image de la caméra de toute façon. Vous pouvez bien sûr contourner la présence de la carte sd mais pas avec la caméra de capture d'intention....Vous vérifiez donc le stockage externe, mais vous en avez besoin à ce stade.... FYI vous devriez également vérifier une bibliothèque de récolte comme crop-image au lieu d'utiliser natif, car il n'est pas bien supporté à travers les appareils.
File mediaStorageDir;
String photoFileName = "photo.jpg";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// page = getArguments().getInt("someInt", 0);
// title = getArguments().getString("someTitle");
// Get safe storage directory for photos
mediaStorageDir = new File(
Environment .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
APP_TAG);
// Create the storage directory if it does not exist
if (!mediaStorageDir.exists() && !mediaStorageDir.mkdirs()) {
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.isDirectory());
Log.d(APP_TAG, "Directory exists: " + mediaStorageDir.getPath());
Log.d(APP_TAG,
"Directory exists: "
+ Environment.getExternalStorageState());
Log.d(APP_TAG, "failed to create directory");
}
}
in votre code 'take the pic':
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri(photoFileName));
...
public Uri getPhotoFileUri(String fileName) {
return Uri.fromFile(new File(mediaStorageDir.getPath() + File.separator
+ fileName));
}
après avoir fait des recherches sur ce bug pendant un certain temps, j'ai remarqué que l'activité qui a appelé l'intention de l'appareil photo n'est relancée que lorsque le téléphone est en panne de mémoire. ainsi, parce que l'activité est redémarrée, l'objet tenant le chemin ou L'Uri vers l'image capturée est rafraîchi (nulled)
donc je vous suggérerais d'attraper / détecter l'objet nul dans le résultat onActivityResult, et d'inviter l'utilisateur à libérer de l'espace sur leur appareil ou redémarrer leur téléphone pour un rapide temporaire fixer.