کار با دوربین در اندروید- قسمت ۱

با سلام، میخوام در ادامه در مورد آموزش کار کردن با دوربین در اندروید مطالبی رو به اشتراک بزارم.

 

تو این قسمت میخوام با استفاده از دوربین عکسی گرفته بشه و اون عکس رو در حافظه گوشی ذخیره کنیم.

نکته اول: اکتیویتی به نام UserProfileActivity داریم که یه button داخلش کردیم و داخل Onclick اون button میخوایم عکس گرفته شود و ذخیره شود و در ادامه به سرور ارسال شود.

نکته دوم – تا مرحله سوم بیشتر خود کدهای خام آورده شده، بعد از تغییرات Manifest بهتره برای استفاده از مرحله 4 به بعد رو استفاده کنید.

1- اول از همه داخل Manifest اجازه دسترسی به حافظه داخلی رو بدید.

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    ...
</manifest>

2- بعد از گرفتن عکس نیاز به فایلی داریم که عکس در آن ذخیره شود، به این منظور از تابع زیر استفاده می کنیم (داخل Activity مورد نظر استفاده شود – تو این مثال UserProfileActivity):

String mCurrentPhotoPath;

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

نکته: برای اندروی قبل از 24 از FileProvide استفاده نمیشد ولی برای اندروید های بیشتر از 24 اگر از FileProvider  استفاده نشه به ارور زیر بر خواهید خورد:

.../android.os.FileUriExposedException: file:///storage/emulated/0

برای تنظیمات File provider در قدم اول داخل Manifest تگ providerزیر را وارد کنید:

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

در خط دوم از تگ provider داریم applicationId که این ID درون فایل buil.gradle قابل دسترس هست.

حال باید فایل file_paths.xml  داخل res/xml ساخته بشه:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>

3- حال برای گرفتن عکس و فراخوانی intent جدید می توانید از کد زیر استفاده کنید (داخل Activity مورد نظر استفاده شود – تو این مثال UserProfileActivity) :

static final int REQUEST_TAKE_PHOTO = 1;
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // Ensure that there's a camera activity to handle the intent
        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            // Create the File where the photo should go
            File photoFile = null;
            try {
                photoFile = createImageFile();
            } catch (IOException ex) {
                // Error occurred while creating the File

            }
            // Continue only if the File was successfully created
            if (photoFile != null) {
                Context context = this;
                Uri photoURI = FileProvider.getUriForFile(this,
                        context.getPackageName()+ ".provider" ,
                        photoFile);
                takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
                startActivityForResult(takePictureIntent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE);
            }
        }

4- نکته: قبل از فراخوانی کد های فوق بهتره که ابتدا توسط تابع زیر بررسی کنیم که دستگاه مورد نظر دوربین داره یا خیر (داخل Activity مورد نظر استفاده شود – تو این مثال UserProfileActivity) :

/** Sadegh Camera take picture
     * Checking device has camera hardware or not
     * */
    private boolean isDeviceSupportCamera() {
        if (getApplicationContext().getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_CAMERA)) {
            // this device has a camera
            return true;
        } else {
            // no camera on this device
            return false;
        }
    }

و از طریق زیر بررسیش می کنیم (داخل Activity مورد نظر استفاده شود) :

if (!isDeviceSupportCamera()) {
                                    Toast.makeText(getApplicationContext(),
                                            "Sorry! Your device doesn't support camera",
                                            Toast.LENGTH_LONG).show();
                                    // will close the app if the device does't have camera
                                    finish();
                                }
                                // capture picture
                                captureImage();

captureImage در اصل همان کد نوشته شده در مرحله 3 است که تغییراتی توش دادم و در زیر آورده شده است:

private void captureImage() {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

    //// sadegh added for error : FileUriExposedException
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    //fileUri = getOutputMediaFileUri(MEDIA_TYPE_IMAGE);
    Context context = this;
    fileUri = FileProvider.getUriForFile(this, this.getPackageName() + ".provider", getOutputMediaFile(MEDIA_TYPE_IMAGE));


    intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);

    // start the image capture Intent
    startActivityForResult(intent, CAMERA_CAPTURE_IMAGE_REQUEST_CODE);

}

همون طور که تو کد بالا اومده برای ساخت فایل از تابع  getOutputMediaFile استفاده شده که کدش میشه:

/**
 * returning image / video
 */
private static File getOutputMediaFile(int type) {

    // External sdcard location
    File mediaStorageDir = new File(
            Environment
                    .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),
            AppConfig.IMAGE_DIRECTORY_NAME);

    // Create the storage directory if it does not exist
    if (!mediaStorageDir.exists()) {
        if (!mediaStorageDir.mkdirs()) {
            Log.d(TAG, "Oops! Failed create "
                    + AppConfig.IMAGE_DIRECTORY_NAME + " directory");
            return null;
        }
    }

    // Create a media file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss",
            Locale.getDefault()).format(new Date());
    File mediaFile;
    if (type == MEDIA_TYPE_IMAGE) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator
                + "IMG_" + timeStamp + ".jpg");
    } else if (type == MEDIA_TYPE_VIDEO) {
        mediaFile = new File(mediaStorageDir.getPath() + File.separator
                + "VID_" + timeStamp + ".mp4");
    } else {
        return null;
    }

    return mediaFile;
}
/**
 * Here we store the file url as it will be null after returning from camera
 * app
 */
@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    // save file url in bundle as it will be null on screen orientation
    // changes
    outState.putParcelable("file_uri", fileUri);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);

    // get the file url
    fileUri = savedInstanceState.getParcelable("file_uri");
}

5- ذخیره عکس بر روی سرور (بخش اندروید استادیو) (داخل Activity مورد نظر استفاده شود):

یه Activity و فایل Layout جدا برای صفحه Upload داریم، اسم Activity مورد نظر UploadActivity هست.

خب یه تابع داخل Activity مورد نظرمان داریم (UserProfileActivity) که کدش زیر هست و اکتیویتی آپلود رو فراخوانی می کنه:

private void launchUploadActivity(boolean isImage){
        Intent i = new Intent(UserProfileActivity.this, UploadActivity.class);
        i.putExtra("filePath", fileUri.getPath());
        i.putExtra("isImage", isImage);
        startActivity(i);
    }

از تابع زیر نیز برای فراخوانی UploadActivity استفاده می کنیم (باز هم داخل Activity مورد نظر که اینجاUserProfileActivity استفاده میشه):

/**
 * Receiving activity result method will be called after closing the camera
 * */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    // if the result is capturing Image
    if (requestCode == CAMERA_CAPTURE_IMAGE_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {

            // successfully captured the image
            // launching upload activity
            launchUploadActivity(true);


        } else if (resultCode == RESULT_CANCELED) {

            // user cancelled Image capture
            Toast.makeText(getApplicationContext(),
                    "User cancelled image capture", Toast.LENGTH_SHORT)
                    .show();

        } else {
            // failed to capture image
            Toast.makeText(getApplicationContext(),
                    "Sorry! Failed to capture image", Toast.LENGTH_SHORT)
                    .show();
        }

    } else if (requestCode == CAMERA_CAPTURE_VIDEO_REQUEST_CODE) {
        if (resultCode == RESULT_OK) {

            // video successfully recorded
            // launching upload activity
            launchUploadActivity(false);

        } else if (resultCode == RESULT_CANCELED) {

            // user cancelled recording
            Toast.makeText(getApplicationContext(),
                    "User cancelled video recording", Toast.LENGTH_SHORT)
                    .show();

        } else {
            // failed to record video
            Toast.makeText(getApplicationContext(),
                    "Sorry! Failed to record video", Toast.LENGTH_SHORT)
                    .show();
        }
    }
}

6- در ادامه نوبت به ساخت UploadActivity.java می رسد که کدش از قرار زیر است: