code

데이터 폴더의 URI로 카메라 결과를 얻는 방법은 무엇입니까?

codestyles 2020. 11. 18. 09:07
반응형

데이터 폴더의 URI로 카메라 결과를 얻는 방법은 무엇입니까?


이미지를 캡처하려는 응용 프로그램을 만든 다음 해당 이미지를 이메일에 첨부 파일로 보내려고합니다.

android.provider.MediaStore.ACTION_IMAGE_CAPTURE의도 작업을 사용하여 카메라를 열고 파일의 Uri를 매개 변수 EXTRA_OUTPUT로 전달하여 이미지를 파일로 다시 가져옵니다. 이 완벽하게 작동하고 나는를 사용하는 경우 내가 캡처 한 이미지를 얻을 수 있어요 external storage uriA와 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" />

또한 외부 저장소쓰기 권한 카메라 사용 권한을 부여했습니다 .

외부 저장 장치를 사용하여 이미지를 캡처 할 수 있지만 외부 저장 장치를 사용할 수없는 경우 이미지를 캡처하여 메일을 보내려고하기 때문에 외부 저장소가 아닌 데이터 디렉토리에 이미지를 저장하고 싶습니다.

콘텐츠 제공을 생성하면 이메일 응용 프로그램에 내 이미지를 공유 할 수도 있습니다.

카메라 인 텐트와 함께 엑스트라를 제공하지 않으면 액티비티 결과에 추가 데이터로 이미지를 byte []로 반환하지만 이것은 썸네일을위한 것이므로이 방법으로 고해상도 이미지를 얻을 수 없습니다. .


이 문제를 해결하는 방법에는 두 가지가 있습니다.

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

자체 공급자를 생성하지 않으려면 support-library-v4의 FileProvider사용할 수 있습니다 . 자세한 도움이 필요하면 이 게시물을 살펴볼 수 있습니다.


내가 찾은 최고의 솔루션은 다음과 같습니다 : FileProvider (support-library-v4 필요) 내부 저장소를 사용합니다! https://developer.android.com/reference/android/support/v4/content/FileProvider.html

  1. Manifest in Application 요소에서 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 카드의 존재를 해킹 할 수 있지만 카메라 의도 캡처로는 해킹 할 수 없습니다 .... 따라서 외부 저장소를 확인하고 있지만이 시점에서 필요합니다 .... 참고로 다음과 같은 자르기 라이브러리도 확인해야합니다. 기기에서 잘 지원되지 않기 때문에 네이티브를 사용하는 대신 이미지 자르기.

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

After researching this bug for some time, I have noticed that the activity that called the camera intent is only restarted when the phone is running out of memory. so because the activity is restarted, the object holding the path or Uri to the captured image is refreshed (nulled)

so I would suggest you catch/ detect the null object in the onActivityResult, and prompt the user to free up space on their device or restart their phone for a quick temporary fix.

참고URL : https://stackoverflow.com/questions/10042695/how-to-get-camera-result-as-a-uri-in-data-folder

반응형