Как получить результат камеры как URI в папке данных?

Я создаю приложение, в котором я хочу захватить изображение, а затем я хочу отправить это изображение в электронном письме в виде вложения.

Я открываю камеру используя android.provider.MediaStore.ACTION_IMAGE_CAPTURE намеренное действие, и я передаю Uri файла в качестве параметра EXTRA_OUTPUT чтобы вернуть изображение в файл. Это работает отлично, и я могу получить захваченное изображение, если я использую external storage uri как EXTRA_OUTPUT но если я использую папку данных URI, она не работает, и камера не закрывается, и все ее кнопки не работают.

Вот мой код для получения результата в каталоге внешнего хранилища

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);

И этот код для получения изображения в папке данных

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);

Я знал, что папка данных недоступна третьему приложению, поэтому, возможно, это вызывает проблему, поэтому мне нужно создать одного поставщика контента для общего доступа к файлу.

Вот мой контент предоставит класс

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);

    }
}

Таким образом, чтобы использовать этот контент, я использую следующий код, чтобы передать URI для активности камеры

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);

Теперь, если я передаю URL-адрес, отличный от каталога внешнего хранилища, камера открывается, но не закрывается в эмуляторе, а в устройстве камера закрывается, но я не получаю результат.

Я объявил, что это содержание предоставить в файле манифеста

<provider
android:name=".contentproviders.MyContentProvider"
android:authorities="x.y.z" />

Также я дал разрешение на запись внешнего хранилища, а также для использования камеры.

Я могу захватить изображение, используя внешнее хранилище, но я хочу сохранить изображение в каталоге данных вместо внешнего хранилища, потому что, если внешнее хранилище недоступно, я хочу захватить изображение и отправить почту.

Если я создаю контент, то могу также поделиться своим изображением с приложением электронной почты.

Если мы не предоставим дополнительные функции с намерением камеры, они вернут изображение в виде байта [] в результате действия как дополнительные данные, но это делается для миниатюры, поэтому я не могу получить изображение с высоким разрешением, используя этот способ.,

6 ответов

Решение

Есть два способа решить эту проблему.

1. Сохраните растровое изображение, которое вы получили от метода onActivityResult.

Вы можете запустить камеру через намерение сделать снимок, используя код ниже

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

После захвата фото вы получите растровое изображение в методе onActivityResult

if (requestCode == CAMERA_REQUEST) {  
    Bitmap photo = (Bitmap) data.getExtras().get("data"); 
 }

Теперь вы можете просто сохранить это растровое изображение во внутреннем хранилище

Примечание: здесь растровый объект состоит из большого пальца, он не будет иметь изображения с полным разрешением

2. Сохраните растровое изображение непосредственно во внутреннем хранилище, используя контент-провайдер

Здесь мы создадим класс провайдера контента, чтобы разрешить разрешение локального каталога хранения на активность камеры

Пример провайдера, как показано ниже

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());
    }
}

После этого вы можете просто использовать URI для перехода к активности камеры с помощью приведенного ниже кода

Intent i = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
i.putExtra(MediaStore.EXTRA_OUTPUT, MyFileContentProvider.CONTENT_URI);
startActivityForResult(i, CAMERA_RESULT);

Если вы не хотите создавать своего собственного провайдера, вы можете использовать FileProvider из support-library-v4. Для получения подробной помощи вы можете заглянуть в этот пост

Лучшее решение, которое я нашел, это: FileProvider (нужен support-library-v4). Он использует внутреннее хранилище! https://developer.android.com/reference/android/support/v4/content/FileProvider.html

  1. Определите свой FileProvider в манифесте в элементе приложения:

    <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>
    
  2. Добавьте разрешения в манифесте корневого элемента:

    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" android:required="false" />
    
  3. Определите пути к изображениям, например, в res / xml/image_path.xml:

    <paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="captured_image" path="your/path/"/>
    </paths>
    
  4. Джава:

    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 намерение захвата:

    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);
    }

Намерение позвонить, чтобы сделать снимок,

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

Затем возьмите растровое изображение в ActivityResult

 if (requestCode == CAMERA_REQUEST) {   
            Bitmap photo = (Bitmap) data.getExtras().get("data");  
 }   

Затем запишите это во внутреннюю память, посмотрите это

// 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); 

Затем в следующий раз, чтобы прочитать этот файл,

Bitmap bitmap = BitmapFactory.decodeFile(file); 

Сначала сохраните полученное фото на внешнем накопителе и попробуйте -

@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();
   }  
} 

Следующая цель -

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);

Вы также можете сделать это без необходимости в поставщике контента, так как вам понадобится SD-карта, чтобы в любом случае открыть намерение захвата изображения с камеры. Конечно, вы можете взломать наличие SD-карты, но не с помощью захвата камеры намеренно... Итак, вы проверяете внешнее хранилище, но вам это нужно в данный момент.... К вашему сведению, вы также должны проверить библиотеку обрезки, например кадрирование изображения вместо использования native, поскольку оно не очень хорошо поддерживается на разных устройствах.

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");
        }

}

в вашем коде "возьмите картинку":

            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));
}

После изучения этой ошибки в течение некоторого времени я заметил, что действие, которое вызывало намерение камеры, перезапускается только тогда, когда в телефоне заканчивается память. поэтому, поскольку действие перезапускается, объект, содержащий путь или Uri к захваченному изображению, обновляется (обнуляется)

поэтому я бы посоветовал вам перехватить / обнаружить нулевой объект в onActivityResult и предложить пользователю освободить место на своем устройстве или перезагрузить телефон для быстрого временного исправления.

Другие вопросы по тегам