web-dev-qa-db-fra.com

Intention unique de laisser l'utilisateur prendre une photo OR choisir l'image dans la galerie dans Android

Je développe une application pour Android 2.1 vers le haut. Je veux permettre à mes utilisateurs de sélectionner une photo de profil dans mon application (je n'utilise pas le cadre de contacts).

La solution idéale serait de déclencher une intention qui permet à l'utilisateur de sélectionner une image dans la galerie, mais si une image appropriée n'est pas disponible, utilisez l'appareil photo pour prendre une photo (ou vice-versa, c'est-à-dire permettre à l'utilisateur de prendre une photo mais si ils savent qu'ils ont déjà une image appropriée, laissez-les tomber dans la galerie et choisissez cette image).

Actuellement je peux faire l'un ou l'autre mais pas les deux.

Si je passe directement en mode caméra à l'aide de MediaStore.ACTION_IMAGE_CAPTURE, il n'y a pas d'option pour passer dans la galerie.

Si je vais directement à la galerie en utilisant Intent.ACTION_PICK, je peux choisir une image, mais si je clique sur le bouton de la caméra (dans le coin supérieur droit de la galerie), une nouvelle intention de caméra est déclenchée. Ainsi, aucune photo prise n'est renvoyée directement à mon application. (Bien sûr, vous pouvez appuyer sur le bouton de retour pour revenir dans la galerie et sélectionner l'image à partir de là, mais c'est une étape supplémentaire inutile et n'est pas du tout intuitive).

Alors, y a-t-il un moyen de combiner les deux ou vais-je devoir proposer un menu pour faire l'un ou l'autre depuis mon application? On dirait que ce serait un cas d'utilisation courant ... je manque sûrement quelque chose?

66
Damian

UPDATE : L'autre réponse, en utilisant EXTRA_INITIAL_INTENTS, est meilleur à ce stade. Au moment où j'ai écrit ma réponse, EXTRA_INITIAL_INTENTS n'existait pas encore, car il a été ajouté au niveau 5 de l'API.

Alors, y a-t-il un moyen de combiner les deux ou vais-je devoir proposer un menu pour faire l'un ou l'autre depuis mon application?

Écrivez votre propre galerie qui a les caractéristiques que vous désirez.

Je pense qu'un menu serait plus simple.

On dirait que ce serait un cas d'utilisation courant ... je manque sûrement quelque chose?

Le développeur à côté de vous pensera que la galerie devrait vous permettre de choisir parmi la galerie locale ou bien de sauter sur Flickr pour faire une sélection à partir de là. Un autre développeur pensera que l'appareil photo devrait non seulement permettre de "prendre une photo" via l'appareil photo, mais de "prendre une photo" en choisissant quelque chose dans la galerie, en inversant les choses de la façon dont vous l'envisagez. Pourtant, un autre développeur pensera que la galerie devrait permettre de choisir parmi la galerie locale, ou Flickr, ou la caméra, ou une webcam connectée au réseau. Encore un autre développeur pensera que la galerie est stupide et les utilisateurs devraient simplement choisir des fichiers via un explorateur de fichiers. Etc.

Tout cela dans un environnement (téléphones mobiles) où le flash pour le système d'exploitation est à une prime.

Par conséquent, à mon humble avis, il n'est pas complètement choquant que le noyau Android a choisi de fournir des blocs de construction à assembler comme bon vous semble, plutôt que d'essayer de s'adapter à tous les modèles possibles.

13
CommonsWare

Vous pouvez essayer de faire quelque chose comme ça:

// ...
// Within your enclosing Class
// ...
private static final int SELECT_PICTURE = 1;

// ... 

Intent pickIntent = new Intent();
pickIntent.setType("image/*");
pickIntent.setAction(Intent.ACTION_GET_CONTENT);

Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

String pickTitle = "Select or take a new Picture"; // Or get from strings.xml
Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
chooserIntent.putExtra
(
  Intent.EXTRA_INITIAL_INTENTS, 
  new Intent[] { takePhotoIntent }
);

startActivityForResult(chooserIntent, SELECT_PICTURE);

Pour voir comment gérer le résultat de l'activité, veuillez vous référer à cette question


Remarque: un point critique est de savoir si l'appareil photo ou la galerie a été utilisé. Cela est illustré dans cet exemple de code: https://stackoverflow.com/a/12347567/294884

119
Macarse

Vous pouvez procéder de cette manière dans votre Activité:

private static final int REQUEST_CODE_PICTURE= 1;

    /**
     * Click on View to change photo. Sets into View of your layout, Android:onClick="clickOnPhoto"
     * @param view View
     */
    public void clickOnPhoto(View view) {
        Intent pickIntent = new Intent();
        pickIntent.setType("image/*");
        pickIntent.setAction(Intent.ACTION_GET_CONTENT);
        Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        String pickTitle = "Take or select a photo";
        Intent chooserIntent = Intent.createChooser(pickIntent, pickTitle);
        chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] { takePhotoIntent });
        startActivityForResult(chooserIntent, REQUEST_CODE_PICTURE);
    }

Ensuite, ajoutez toujours dans votre activité la méthode onActivityResult:

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE_PICTURE && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                return;
            }
            try {
                InputStream inputStream = getContentResolver().openInputStream(data.getData());
                Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                imgPhoto.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
10
Dario Bruzzese

Ma réponse est presque identique à la solution @Macarse mais j'ajoute également une intention supplémentaire d'afficher les applications de la galerie (Ex: Google Photos) et est écrite en Kotlin:

val REQUEST_CODE_GET_IMAGE = 101

private fun addProfileImage() {
    val pickImageFileIntent = Intent()
    pickImageFileIntent.type = "image/*"
    pickImageFileIntent.action = Intent.ACTION_GET_CONTENT

    val pickGalleryImageIntent = Intent(Intent.ACTION_PICK, Android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI)

    val captureCameraImageIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    val pickTitle = "Capture from camera or Select from gallery the Profile photo"
    val chooserIntent = Intent.createChooser(pickImageFileIntent, pickTitle)
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(captureCameraImageIntent, pickGalleryImageIntent))
    startActivityForResult(chooserIntent, REQUEST_CODE_GET_IMAGE)
}

Extraire l'image de l'intention de résultat:

private var imageTempFile: File? = null
private var imageMimeType: String? = null

private fun extractImage(intent: Intent?) {
    val imageUri = intent?.data
    imageUri?.let {
        Glide.with(this)
                .load(imageUri)
                .into(profileImageCiv)
        imageTempFile = MediaUtils.copyContentFromUriToCacheFile(this, imageUri, Settings.DIRECTORY_CACHE_TEMP_PROFILE_IMAGE)
        imageMimeType = MediaUtils.getMimeType(this, imageUri)
    } ?: run {
        intent?.extras?.get("data")?.let { bitmap ->    // Bitmap was returned as raw bitmap
            Glide.with(this)
                    .load(bitmap)
                    .into(profileImageCiv)
            imageTempFile = MediaUtils.writeBitmapToCacheFile(this, bitmap as Bitmap, Settings.DIRECTORY_CACHE_TEMP_PROFILE_IMAGE)
            imageMimeType = "image/jpeg"    // The bitmap was compressed as JPEG format. The bitmap itself doesn't have any format associated to it
        } ?: run {
            imageTempFile = null
            imageMimeType = null
            Log.e("Intent data is null.")
            Log.d("Error during photo selection")
        }
    }
}
2
vovahost