Android N中的FileUriExposedException with Camera

时间:2016-08-31 06:15:33

标签: android

MainActivity

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Parcelable;
import android.provider.MediaStore;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.File;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mBtn;
    private Context context;
    private static final int SELECT_PICTURE_CAMARA = 101, SELECT_PICTURE = 201, CROP_IMAGE = 301;
    private Uri outputFileUri;
    String mCurrentPhotoPath;
    private Uri selectedImageUri;
    private File finalFile = null;
    private ImageView imageView;
    private PermissionUtil permissionUtil;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mBtn = (Button) findViewById(R.id.btn_img);
    imageView = (ImageView) findViewById(R.id.img_photo);
    permissionUtil = new PermissionUtil();
    mBtn.setOnClickListener(this);
    context = this;
}

@Override
public void onClick(View view) {
    selectImageOption();
}

private void selectImageOption() {
    final CharSequence[] items = {"Capture Photo", "Choose from Gallery", "Cancel"};

    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
    builder.setTitle("Add Photo!");
    builder.setItems(items, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int item) {
            if (items[item].equals("Capture Photo")) {                
                if (permissionUtil.checkMarshMellowPermission()) {
                    if (permissionUtil.verifyPermissions(MainActivity.this, permissionUtil.getCameraPermissions()))
                        onClickCamera();
                    else
                        ActivityCompat.requestPermissions(MainActivity.this, permissionUtil.getCameraPermissions(), SELECT_PICTURE_CAMARA);
                } else
                    onClickCamera();
            } else if (items[item].equals("Choose from Gallery")) {
                if (permissionUtil.checkMarshMellowPermission()) {
                    if (permissionUtil.verifyPermissions(MainActivity.this, permissionUtil.getGalleryPermissions()))
                        onClickGallery();
                    else
                        ActivityCompat.requestPermissions(MainActivity.this, permissionUtil.getGalleryPermissions(), SELECT_PICTURE);
                } else
                    onClickGallery();
            } else if (items[item].equals("Cancel")) {
                dialog.dismiss();
            }
        }
    });
    builder.show();
}

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_OK) {

        if (requestCode == SELECT_PICTURE) {

            selectedImageUri = data.getData();
            cropImage(selectedImageUri);

        } else if (requestCode == CROP_IMAGE) {
            /*if (data != null) {
                // get the returned data
                Bundle extras = data.getExtras();
                // get the cropped bitmap
                Bitmap selectedBitmap = extras.getParcelable("data");

                imageView.setImageBitmap(selectedBitmap);
            }*/

            Uri imageUri = Uri.parse(mCurrentPhotoPath);
            File file = new File(imageUri.getPath());
            try {
                InputStream ims = new FileInputStream(file);
                imageView.setImageBitmap(BitmapFactory.decodeStream(ims));
            } catch (FileNotFoundException e) {
                return;
            }

        } else if (requestCode == SELECT_PICTURE_CAMARA && resultCode == Activity.RESULT_OK) {
            cropImage(Uri.parse(mCurrentPhotoPath));

        }
    }
}

private void onClickCamera() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    if (takePictureIntent.resolveActivity(context.getPackageManager()) != null) {
        File photoFile = null;
        try {
            photoFile = createImageFile();
        } catch (IOException ex) {
        }
        if (photoFile != null) {

            Uri photoURI;
            if (Build.VERSION.SDK_INT >= 24) {
                photoURI = FileProvider.getUriForFile(MainActivity.this,
                        BuildConfig.APPLICATION_ID + ".provider", photoFile);
            } else {
                photoURI = Uri.fromFile(photoFile);
            }

            takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
            startActivityForResult(takePictureIntent, SELECT_PICTURE_CAMARA);

        }
    }
}

private void onClickGallery() {
    List<Intent> targets = new ArrayList<>();
    Intent intent = new Intent();
    intent.setType("image/*");
    intent.setAction(Intent.ACTION_PICK);
    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
    List<ResolveInfo> candidates = getApplicationContext().getPackageManager().queryIntentActivities(intent, 0);

    for (ResolveInfo candidate : candidates) {
        String packageName = candidate.activityInfo.packageName;
        if (!packageName.equals("com.google.android.apps.photos") && !packageName.equals("com.google.android.apps.plus") && !packageName.equals("com.android.documentsui")) {
            Intent iWantThis = new Intent();
            iWantThis.setType("image/*");
            iWantThis.setAction(Intent.ACTION_PICK);
            iWantThis.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
            iWantThis.setPackage(packageName);
            targets.add(iWantThis);
        }
    }
    if (targets.size() > 0) {
        Intent chooser = Intent.createChooser(targets.remove(0), "Select Picture");
        chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, targets.toArray(new Parcelable[targets.size()]));
        startActivityForResult(chooser, SELECT_PICTURE);
    } else {
        Intent intent1 = new Intent(Intent.ACTION_PICK);
        intent1.setType("image/*");
        startActivityForResult(Intent.createChooser(intent1, "Select Picture"), SELECT_PICTURE);
    }
}

private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";

    File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    File image = File.createTempFile(
            imageFileName,  /* prefix */
            ".jpg",         /* suffix */
            storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    if (Build.VERSION.SDK_INT >= 24) {
        mCurrentPhotoPath = String.valueOf(FileProvider.getUriForFile(MainActivity.this,
                BuildConfig.APPLICATION_ID + ".provider", image));
    } else {
        mCurrentPhotoPath = String.valueOf(Uri.fromFile(image));
    }

    return image;
}

private void cropImage(Uri selectedImageUri) {
    Intent cropIntent = new Intent("com.android.camera.action.CROP");

    cropIntent.setDataAndType(selectedImageUri, "image/*");

    cropIntent.putExtra("crop", "true");
    cropIntent.putExtra("aspectX", 1);
    cropIntent.putExtra("aspectY", 1.5);
    cropIntent.putExtra("return-data", true);

    outputFileUri = Uri.fromFile(createCropFile());

    cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
    startActivityForResult(cropIntent, CROP_IMAGE);
}

private File createCropFile() {
    File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
    //  path = path + (timeStamp + "1jpg");
    File file = null;
    try {
        file = File.createTempFile(timeStamp, ".jpg", storageDir);
    } catch (IOException e) {
        e.printStackTrace();
    }

    mCurrentPhotoPath = String.valueOf(Uri.fromFile(file));
    return file;
}
}

PermissionUtil.java

package com.example.shwetachauhan.imagecropasoebi;

import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.v4.app.ActivityCompat;

public class PermissionUtil {
    private String[] galleryPermissions = {
        "android.permission.WRITE_EXTERNAL_STORAGE",
        "android.permission.READ_EXTERNAL_STORAGE"
};

private String[] cameraPermissions = {
        "android.permission.CAMERA",
        "android.permission.WRITE_EXTERNAL_STORAGE",
        "android.permission.READ_EXTERNAL_STORAGE"
};

public String[] getGalleryPermissions(){
    return galleryPermissions;
}

public String[] getCameraPermissions() {
    return cameraPermissions;
}

public boolean verifyPermissions(int[] grantResults) {
    if(grantResults.length < 1){
        return false;
    }

    for (int result : grantResults) {
        if (result != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

public boolean verifyPermissions(Context context, String[] grantResults) {
    for (String result : grantResults) {
        if (ActivityCompat.checkSelfPermission(context, result) != PackageManager.PERMISSION_GRANTED) {
            return false;
        }
    }
    return true;
}

public boolean checkMarshMellowPermission(){
    return(Build.VERSION.SDK_INT> Build.VERSION_CODES.LOLLIPOP_MR1);
}

public boolean checkJellyBean(){
    return(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN);
}
}
  • 此代码适用于来自相机或图库的裁剪图片
  • 此代码适用于所有Android操作系统,但当我尝试在Android N设备上运行时,它会在我打开相机时崩溃。它在带有Gallery的Android N中运行良好。

5 个答案:

答案 0 :(得分:44)

我在 Android N设备中也遇到了同样的问题。但我解决了。

以下是我可以解决问题的代码:

 public void launchCamera() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        intent.putExtra(MediaStore.EXTRA_OUTPUT, getPhotoFileUri());
    } else {
        File file = new File(getPhotoFileUri().getPath());
        Uri photoUri = FileProvider.getUriForFile(getApplicationContext(), getApplicationContext().getPackageName() + ".provider", file);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
    }
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    if (intent.resolveActivity(getApplicationContext().getPackageManager()) != null) {
        startActivityForResult(intent, REQUEST_CAMERA);
    }
}

在此之后,您需要在res中创建 XML文件夹,并且在该文件夹中,您需要创建一个标记为 provider_paths.xml

的xml

provider_paths.xml

中的代码
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
</paths>

稍后在清单中,您需要在应用程序代码中添加以下内容,并确保 compileSdkVersion&gt; = 24

<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>

以下是两个参考链接,可以为您提供更好的理解指导。

<强>链接:

link1 - 来自中等合作

link2 - 来自inthecheesefactory

答案 1 :(得分:7)

如果你没有触及系统中的其他应用程序,只需添加

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

Application.onCreate()中。它会忽略URI暴露,你很高兴。

答案 2 :(得分:4)

从Android N开始,android改变了你提供文件URI的方式。使用file:// uri是被禁止的并且会抛出它。使用FileProvider来克服这个问题。

  

传递file://包域外的URI可能会使接收者无法访问路径。因此,尝试传递file:// URI会触发FileUriExposedException。共享私有文件内容的推荐方法是使用FileProvider。

可以找到更多信息here

答案 3 :(得分:4)

您可以使用StrictMode,将以下代码添加到您的Activity onCreate()

    StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());

答案 4 :(得分:-5)

更改你的buildToolsVersion

 buildToolsVersion "25.0.1"

 buildToolsVersion "23.0.1"