Today we are going to learn how to capture image via Camera in Android.
Integrating camera can be done in two ways. One is to use android inbuilt camera app which is very easy process. This method won’t give you much control over camera as everything is taken care by inbuilt camera app. This way will be appropriate when your app need just a picture or video from camera.
Second way is to build a custom camera interface and adding the functionality. This needs higher effort as we have to do everything by our own. Building custom camera interface will be useful when you are building an app which mainly deals with images and videos like WhatsApp.
1. Create a new project in Android Studio by navigating to File ⇒ New ⇒ New Project and fill required details. By default my activity is MainActivity.java.
2. Open res ⇒ values ⇒ strings.xml and add below string values. These are some strings that we are going to use in our project.
1 2 3 4 5 6 7 8 9 |
<resources> <string name="app_name">Camera Demo</string> <string name="capture">Capture</string> <string name="camera_not_supported">Your Device doesn\'t Support Camera.</string> <string name="capture_image_failed">Failed to capture image.</string> <string name="permission_message">Both permissions are required. Please allow both permission to take picture.</string> <string name="cancel_message">Cancelled by user.</string> <string name="capture_deny_message">Capture image won\'t work without both permissions.</string> </resources> |
3. Now open activity_main.xml and all the below layout code to it. It contains one Button to trigger capture image and an ImageView to display captured image.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" tools:context="com.camera_demo.MainActivity"> <Button android:id="+id/capture_image" android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="captureImage" android:text="string/capture" android:textColor="android:color/white" android:textSize="15sp" /> <!-- ImageView to display captured Image --> <ImageView android:id="+id/image_view" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerCrop" /> </LinearLayout> |
4. Open your AndroidManifest.xml and add the below permissions into it. This permissions will require to Capture Image.
1 2 3 4 5 |
<!-- Permission required for CAMERA --> <uses-permission android:name="android.permission.CAMERA" /> <!-- To write External Storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
5. Now open your MainActivity.java and follow the below steps to implement capture image functionality:
1 2 3 4 5 6 7 8 9 10 |
// Checking camera supportability private boolean isDeviceSupportCamera() { if (getPackageManager().hasSystemFeature( PackageManager.FEATURE_CAMERA)) return true; else { Toast.makeText(MainActivity.this, getResources().getString(R.string.camera_not_supported), Toast.LENGTH_SHORT).show(); return false; } } |
1 2 3 4 5 6 7 |
/* Capture Image Method */ private void captureImage() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//Start intent with Action_Image_Capture fileUri = CameraUtils.getOutputMediaFileUri(this);//get fileUri from CameraUtils intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);//Send fileUri with intent startActivityForResult(intent, CAMERA_REQUEST_CODE);//start activity for result with CAMERA_REQUEST_CODE } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CAMERA_REQUEST_CODE: try { //When image is captured successfully if (resultCode == RESULT_OK) { //Check if device SDK is greater than 22 then we get the actual image path via below method if (Build.VERSION.SDK_INT > 22) getImageUrl = ImagePath_MarshMallow.getPath(MainActivity.this, fileUri); else //else we will get path directly getImageUrl = fileUri.getPath(); //After image capture show captured image over image view showCapturedImage(); } else Toast.makeText(this, R.string.cancel_message, Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); } break; } } |
1 2 3 4 5 6 7 |
/* Show Captured over ImageView */ private void showCapturedImage() { if (!getImageUrl.equals("") && getImageUrl != null) imageView.setImageBitmap(CameraUtils.convertImagePathToBitmap(getImageUrl, false)); else Toast.makeText(this, R.string.capture_image_failed, Toast.LENGTH_SHORT).show(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/** * 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 scren orientation // changes outState.putParcelable("file_uri", fileUri); } /* * Here we restore the fileUri again */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // get the file url fileUri = savedInstanceState.getParcelable("file_uri"); } |
Full Code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
package com.camera_demo; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; import android.widget.Toast; import java.util.ArrayList; import static android.Manifest.permission.CAMERA; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; public class MainActivity extends AppCompatActivity { public static final int PERMISSION_REQUEST_CODE = 114;//request code for Camera and External Storage permission private static final int CAMERA_REQUEST_CODE = 133;//request code for capture image private Uri fileUri = null;//Uri to capture image private String getImageUrl = ""; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = (ImageView) findViewById(R.id.image_view); } /* Check both permissions */ private boolean checkPermission() { ArrayList<String> permissions = new ArrayList<>(); for (String permission : getAllPermissions()) { int result = checkPermission(this, permission); if (result != PackageManager.PERMISSION_GRANTED) { permissions.add(permission); } } //If both permissions are granted if (permissions.size() == 0) allPermissionGranted(); else //if any one of them are not granted then request permission requestPermission(permissions.toArray(new String[permissions.size()])); return true; } /* on both permission granted */ private void allPermissionGranted() { //Initiate capture image method if (isDeviceSupportCamera()) captureImage(); } /* Request permissions */ private void requestPermission(String[] permissions) { ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE); } /* Permissions string array */ private String[] getAllPermissions() { return new String[]{CAMERA, WRITE_EXTERNAL_STORAGE}; } /* Method to check permissions */ public static int checkPermission(final Context context, String permission) { return ContextCompat.checkSelfPermission(context, permission); } /* on Capture image button click check permissions */ public void captureImage(View view) { checkPermission(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case PERMISSION_REQUEST_CODE: if (grantResults.length > 0) { int counter = 0;//counter to traverse all permissions for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { //show alert dialog if any of the permission denied showMessageOKCancel(getString(R.string.permission_message), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //If user click on OK button check permission again. checkPermission(); } } }, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Toast.makeText(MainActivity.this, R.string.capture_deny_message, Toast.LENGTH_SHORT).show(); } }); return; } else { counter++; //If counter is equal to permissions length mean all permission granted. if (counter == permissions.length) allPermissionGranted(); } } } break; } } /* Alert dialog on permission denied */ private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener, DialogInterface.OnClickListener cancelListener) { new AlertDialog.Builder(MainActivity.this) .setMessage(message) .setPositiveButton(android.R.string.ok, okListener) .setNegativeButton(android.R.string.cancel, cancelListener) .setCancelable(false) .create() .show(); } // Checking camera supportability private boolean isDeviceSupportCamera() { if (getPackageManager().hasSystemFeature( PackageManager.FEATURE_CAMERA)) return true; else { Toast.makeText(MainActivity.this, getResources().getString(R.string.camera_not_supported), Toast.LENGTH_SHORT).show(); return false; } } /* Capture Image Method */ private void captureImage() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//Start intent with Action_Image_Capture fileUri = CameraUtils.getOutputMediaFileUri(this);//get fileUri from CameraUtils intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);//Send fileUri with intent startActivityForResult(intent, CAMERA_REQUEST_CODE);//start activity for result with CAMERA_REQUEST_CODE } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { case CAMERA_REQUEST_CODE: try { //When image is captured successfully if (resultCode == RESULT_OK) { //Check if device SDK is greater than 22 then we get the actual image path via below method if (Build.VERSION.SDK_INT > 22) getImageUrl = ImagePath_MarshMallow.getPath(MainActivity.this, fileUri); else //else we will get path directly getImageUrl = fileUri.getPath(); //After image capture show captured image over image view showCapturedImage(); } else Toast.makeText(this, R.string.cancel_message, Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); } break; } } /* Show Captured over ImageView */ private void showCapturedImage() { if (!getImageUrl.equals("") && getImageUrl != null) imageView.setImageBitmap(CameraUtils.convertImagePathToBitmap(getImageUrl, false)); else Toast.makeText(this, R.string.capture_image_failed, Toast.LENGTH_SHORT).show(); } /** * 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 scren orientation // changes outState.putParcelable("file_uri", fileUri); } /* * Here we restore the fileUri again */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); // get the file url fileUri = savedInstanceState.getParcelable("file_uri"); } } |
6. Create new java class naming CameraUtils.java, this class contains helper methods for camera.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
package com.camera_demo; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Environment; import android.util.Log; import java.io.File; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; /** * Created by sonu on 23/03/17. */ public class CameraUtils { //Get Uri Of captured Image public static Uri getOutputMediaFileUri(Context context) { File mediaStorageDir = new File( context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), "Camera"); //If File is not present create directory if (!mediaStorageDir.exists()) { if (mediaStorageDir.mkdir()) Log.e("Create Directory", "Main Directory Created : " + mediaStorageDir); } String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());//Get Current timestamp File mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");//create image path with system mill and image format return Uri.fromFile(mediaFile); } /* Convert Captured image path into Bitmap to display over ImageView */ public static Bitmap convertImagePathToBitmap(String imagePath, boolean scaleBitmap) { BitmapFactory.Options bmOptions = new BitmapFactory.Options(); Bitmap bitmap = BitmapFactory.decodeFile(imagePath, bmOptions);//Decode image path //If you want to scale bitmap/reduce captured image size then send true if (scaleBitmap) return Bitmap.createScaledBitmap(bitmap, 500, 500, true); else //if you don't want to scale bitmap then send false return bitmap; } } |
7. For Lollipop+ Devices we will get the actual image path from Uri with the help of below class naming ImagePath_Marshmallow.java. We have to pass the Uri and it will return image path.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
package com.camera_demo; import android.content.ContentUris; import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; /** * Created by SONU on 14/01/16. */ public class ImagePath_MarshMallow { public static String getPath(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } // TODO handle non-primary volumes } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{ split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = { column }; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } } |
8. Now, you are all done and you can also add the Camera functionality to your app.
Thanks.
Subscribe to us and get the latest news.
2 Comments
sandhya
Monday, April 2nd, 2018Caused by: android.content.ActivityNotFoundException: No Activity found to handle Intent { act=android.media.action.IMAGE_CAPTURE flg=0x3 clip={text/uri-list U:file:///storage/emulated/0/Android/data/com.camera_demo/files/Pictures/Camera/IMG_20180402_124437.jpg} (has extras) }
Dr. Droid
Monday, April 2nd, 2018Hi Sandhya,
In which device you are running the APK?
Please check this link for more clarification : https://stackoverflow.com/questions/9026085/no-activity-found-to-handle-intent-act-android-media-action-image-capture
Thanks