Android Camera : data intent returns null

2018-12-31 20:54发布

问题:

I have an android application which contains multiple activities.

In one of them I\'m using a button which will call the device camera :

public void onClick(View view) {
    Intent photoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(photoIntent, IMAGE_CAPTURE);
}

In the same activity I call the OnActivityResult method for the image result :

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == IMAGE_CAPTURE) {
        if (resultCode == RESULT_OK) {
            Bitmap image = (Bitmap) data.getExtras().get(\"data\");
            ImageView imageview = (ImageView) findViewById(R.id.pic);
            imageview.setImageBitmap(image);
        } else if (resultCode == RESULT_CANCELED) {
            Toast.makeText(this, \"CANCELED \", Toast.LENGTH_LONG).show();
        }
    }
}

The problem is that the intent data is null and the OnActivityResult method turns directly to the (resultCode == RESULT_CANCELED) and the application returns to the previous avtivity.

How can I fix this issue and after calling the camera, the application returns to the current activity which contains an ImageView which will contains the picture taken?

Thanks

回答1:

The default Android camera application returns a non-null intent only when passing back a thumbnail in the returned Intent. If you pass EXTRA_OUTPUT with a URI to write to, it will return a null intent and the picture is in the URI that you passed in.

You can verify this by looking at the camera app\'s source code on GitHub:

  • https://github.com/android/platform_packages_apps_camera/blob/gingerbread-release/src/com/android/camera/Camera.java#L1186

    Bundle newExtras = new Bundle();
    if (mCropValue.equals(\"circle\")) {
        newExtras.putString(\"circleCrop\", \"true\");
    }
    if (mSaveUri != null) {
        newExtras.putParcelable(MediaStore.EXTRA_OUTPUT, mSaveUri);
    } else {
        newExtras.putBoolean(\"return-data\", true);
    }
    

I would guess that you\'re either passing in EXTRA_OUTPUT somehow, or the camera app on your phone works differently.



回答2:

I found an easy answer. it works!!

o

private void openCameraForResult(int requestCode){
    Intent photo = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    Uri uri  = Uri.parse(\"file:///sdcard/photo.jpg\");
    photo.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri);
    startActivityForResult(photo,requestCode);
}

if (requestCode == CAMERA_REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            File file = new File(Environment.getExternalStorageDirectory().getPath(), \"photo.jpg\");
            Uri uri = Uri.fromFile(file);
            Bitmap bitmap;
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
                bitmap = crupAndScale(bitmap, 300); // if you mind scaling
                pofileImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }
    }

if you would like to crop and scale this image

public static  Bitmap crupAndScale (Bitmap source,int scale){
    int factor = source.getHeight() <= source.getWidth() ? source.getHeight(): source.getWidth();
    int longer = source.getHeight() >= source.getWidth() ? source.getHeight(): source.getWidth();
    int x = source.getHeight() >= source.getWidth() ?0:(longer-factor)/2;
    int y = source.getHeight() <= source.getWidth() ?0:(longer-factor)/2;
    source = Bitmap.createBitmap(source, x, y, factor, factor);
    source = Bitmap.createScaledBitmap(source, scale, scale, false);
    return source;
}


回答3:

Simple working camera app avoiding the null intent problem

- all changed code included in this reply; close to android tutorial

I\'ve been spending plenty of time on this issue, so I decided to create an account and share my outcomes with you.

The official android tutorial \"Taking Photos Simply\" turned out to not quite hold what it promised. The code provided there did not work on my device: a Samsung Galaxy S4 Mini GT-I9195 running android version 4.4.2 / KitKat / API Level 19.

I figured out that the main problem was the following line in the method invoked when capturing the photo (dispatchTakePictureIntent in the tutorial):

takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);

It resulted in the intent subsequently catched by onActivityResult being null.

To solve this problem, I pulled much inspiration out of earlier replies here and some helpful posts on github (mostly this one by deepwinter - big thanks to him; you might want to check out his reply on a closely related post as well).

Following these pleasant pieces of advice, I chose the strategy of deleting the mentioned putExtra line and doing the corresponding thing of getting back the taken picture from the camera within the onActivityResult() method instead. The decisive lines of code to get back the bitmap associated with the picture are:

        Uri uri = intent.getData();
        Bitmap bitmap = null;
        try {
            bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }

I created an exemplary app which just has the ability to take a picture, save it on the SD card and display it. I think this might be helpful to people in the same situation as me when I stumbled on this issue, since the current help suggestions mostly refer to rather extensive github posts which do the thing in question but aren\'t too easy to oversee for newbies like me. With respect to the file system Android Studio creates per default when creating a new project, I just had to change three files for my purpose:

activity_main.xml :

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
xmlns:app=\"http://schemas.android.com/apk/res-auto\"
xmlns:tools=\"http://schemas.android.com/tools\"
android:layout_width=\"match_parent\"
android:layout_height=\"match_parent\"
android:orientation=\"vertical\"
tools:context=\"com.example.android.simpleworkingcameraapp.MainActivity\">

<Button
    android:layout_width=\"wrap_content\"
    android:layout_height=\"wrap_content\"
    android:onClick=\"takePicAndDisplayIt\"
    android:text=\"Take a pic and display it.\" />

<ImageView
    android:id=\"@+id/image1\"
    android:layout_width=\"match_parent\"
    android:layout_height=\"200dp\" />

</LinearLayout>

MainActivity.java :

package com.example.android.simpleworkingcameraapp;

import android.content.Intent;
import android.graphics.Bitmap;
import android.media.Image;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class MainActivity extends AppCompatActivity {

private ImageView image;
static final int REQUEST_TAKE_PHOTO = 1;
String mCurrentPhotoPath;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    image = (ImageView) findViewById(R.id.image1);
}

// copied from the android development pages; just added a Toast to show the storage location
private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat(\"yyyyMMdd_HHmm\").format(new Date());
    String imageFileName = \"JPEG_\" + timeStamp + \"_\";
    File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
            imageFileName,  /* prefix */
            \".jpg\",         /* suffix */
            storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = image.getAbsolutePath();
    Toast.makeText(this, mCurrentPhotoPath, Toast.LENGTH_LONG).show();
    return image;
}

public void takePicAndDisplayIt(View view) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (intent.resolveActivity(getPackageManager()) != null) {
        File file = null;
        try {
            file = createImageFile();
        } catch (IOException ex) {
            // Error occurred while creating the File
        }

        startActivityForResult(intent, REQUEST_TAKE_PHOTO);
    }
}

@Override
protected void onActivityResult(int requestCode, int resultcode, Intent intent) {
    if (requestCode == REQUEST_TAKE_PHOTO && resultcode == RESULT_OK) {
        Uri uri = intent.getData();
        Bitmap bitmap = null;
        try {
            bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
        } catch (IOException e) {
            e.printStackTrace();
        }
        image.setImageBitmap(bitmap);
    }
}
}

AndroidManifest.xml :

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"
package=\"com.example.android.simpleworkingcameraapp\">


<!--only added paragraph-->
<uses-feature
    android:name=\"android.hardware.camera\"
    android:required=\"true\" />
<uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />  <!-- only crucial line to add; for me it still worked without the other lines in this paragraph -->
<uses-permission android:name=\"android.permission.CAMERA\" />


<application
    android:allowBackup=\"true\"
    android:icon=\"@mipmap/ic_launcher\"
    android:label=\"@string/app_name\"
    android:roundIcon=\"@mipmap/ic_launcher_round\"
    android:supportsRtl=\"true\"
    android:theme=\"@style/AppTheme\">
    <activity android:name=\".MainActivity\">
        <intent-filter>
            <action android:name=\"android.intent.action.MAIN\" />

            <category android:name=\"android.intent.category.LAUNCHER\" />
        </intent-filter>
    </activity>
</application>

</manifest>

Note that the solution I found for the problem also led to a simplification of the android manifest file: the changes suggested by the android tutorial in terms of adding a provider are no longer needed since I am not making use of any in my java code. Hence, only few standard lines -mostly regarding permissions- had to be added to the manifest file.

It might additionally be valuable to point out that Android Studio\'s autoimport may not be capable of handling java.text.SimpleDateFormat and java.util.Date. I had to import both of them manually.



回答4:

Probably because you had something like this?

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);                        
Uri fileUri =  CommonUtilities.getTBCameraOutputMediaFileUri();                  
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);                        
startActivityForResult(takePictureIntent, 2);

However you must not put the extra output into the intent, because then the data goes into the URI instead of the data variable. For that reason, you have to take the two lines in the middle out, so that you have

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(takePictureIntent, 2);

That´s what caused the problem for me, hope that helped.



回答5:

I´ve had experienced this problem, the intent is not null but the information sent via this intent is not received in onActionActivit()

\"enter

This is a better solution using getContentResolver() :

    private Uri imageUri;
    private ImageView myImageView;
    private Bitmap thumbnail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

      ...
      ...    
      ...
      myImageview = (ImageView) findViewById(R.id.pic); 

      values = new ContentValues();
      values.put(MediaStore.Images.Media.TITLE, \"MyPicture\");
      values.put(MediaStore.Images.Media.DESCRIPTION, \"Photo taken on \" + System.currentTimeMillis());
      imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
      Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
      intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
      startActivityForResult(intent, PICTURE_RESULT);

  }

the onActivityResult() get a bitmap stored by getContentResolver() :

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_TAKE_PHOTO && resultCode == RESULT_OK) {

            Bitmap bitmap;
            try {
                bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), imageUri);
                myImageView.setImageBitmap(bitmap);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

\"introducir \"introducir

Check my example in github:

https://github.com/Jorgesys/TakePicture



回答6:

The following code works for me:

Intent cameraIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
startActivityForResult(cameraIntent, 2);

And here is the result:

protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent)
{ 
    super.onActivityResult(requestCode, resultCode, imageReturnedIntent);

    if(resultCode == RESULT_OK)
    {
        Uri selectedImage = imageReturnedIntent.getData();
        ImageView photo = (ImageView) findViewById(R.id.add_contact_label_photo);
        Bitmap mBitmap = null;
        try
        {
            mBitmap = Media.getBitmap(this.getContentResolver(), selectedImage);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}


回答7:

To Access the Camera and take pictures and set ImageView on Android

You have to use Uri file = Uri.fromFile(getOutputMediaFile()); for marshmallow.

Use below link to get path

https://androidkennel.org/android-camera-access-tutorial/



回答8:

Kotlin code that works for me:

 private fun takePhotoFromCamera() {
          val intent = Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE)
        startActivityForResult(intent, PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA)
      }

And get Result :

 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
 if (requestCode == PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA) {
         if (resultCode == Activity.RESULT_OK) {
           val photo: Bitmap? =  MediaStore.Images.Media.getBitmap(this.contentResolver, Uri.parse( data!!.dataString)   )
            // Do something here : set image to an ImageView or save it ..   
              imgV_pic.imageBitmap = photo 
        } else if (resultCode == Activity.RESULT_CANCELED) {
            Log.i(TAG, \"Camera  , RESULT_CANCELED \")
        }

    }

}

and don\'t forget to declare request code:

companion object {
 const val PERMISSIONS_REQUEST_TAKE_PICTURE_CAMERA = 300
  }


回答9:

When we capture the image from Camera in Android then Uri or data.getdata() becomes null. We have two solutions to resolve this issue.

  1. Retrieve the Uri path from the Bitmap Image
  2. Retrieve the Uri path from cursor.

This is how to retrieve the Uri from the Bitmap Image. First capture image through Intent that will be the same for both methods:

// Capture Image
captureImg.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        if (intent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(intent, reqcode);
        }
    }
});

Now implement OnActivityResult, which will be the same for both methods:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode==reqcode && resultCode==RESULT_OK)
    {
        Bitmap photo = (Bitmap) data.getExtras().get(\"data\");
        ImageView.setImageBitmap(photo);

        // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
        Uri tempUri = getImageUri(getApplicationContext(), photo);

        // Show Uri path based on Image
        Toast.makeText(LiveImage.this,\"Here \"+ tempUri, Toast.LENGTH_LONG).show();

        // Show Uri path based on Cursor Content Resolver
        Toast.makeText(this, \"Real path for URI : \"+getRealPathFromURI(tempUri), Toast.LENGTH_SHORT).show();
    }
    else
    {
        Toast.makeText(this, \"Failed To Capture Image\", Toast.LENGTH_SHORT).show();
    }
}

Now create all above methods to create the Uri from Image and Cursor methods:

Uri path from Bitmap Image:

private Uri getImageUri(Context applicationContext, Bitmap photo) {
    ByteArrayOutputStream bytes = new ByteArrayOutputStream();
    photo.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
    String path = MediaStore.Images.Media.insertImage(LiveImage.this.getContentResolver(), photo, \"Title\", null);
    return Uri.parse(path);
}

Uri from Real path of saved image:

public String getRealPathFromURI(Uri uri) {
    Cursor cursor = getContentResolver().query(uri, null, null, null, null);
    cursor.moveToFirst();
    int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
    return cursor.getString(idx);
}