How to get image from Gallery
Android device has Gallery application installed by default which contains images and videos. It is the place where the images and videos you download from internet or upload from your computer can be viewed.
Lets see how you can use the Gallery to pick a photo for your application.
First create a layout with a Button
and an ImageView
.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:onClick="pick" android:text="Pick" android:layout_centerHorizontal="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/iv" android:layout_centerInParent="true" android:layout_width="200dp" android:layout_height="200dp" /> </RelativeLayout>
Next use following snippet for photo picker.
public class MainActivity extends AppCompatActivity { private static final int PICK_IMAGE_REQUEST = 100; private ImageView iv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); } public void pick(View v) { Intent intent = new Intent(Intent.ACTION_GET_CONTENT); intent.setType("image/*"); startActivityForResult(intent, PICK_IMAGE_REQUEST); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode){ case PICK_IMAGE_REQUEST: if(resultCode == RESULT_OK){ Uri selectedImage = data.getData(); // method 1 try { Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), selectedImage); iv.setImageBitmap(bitmap); } catch (IOException e) { e.printStackTrace(); } // method 2 //try { // InputStream imageStream = getContentResolver().openInputStream(selectedImage); // Bitmap yourSelectedImage = BitmapFactory.decodeStream(imageStream); // imageStream.close(; // iv.setImageBitmap(yourSelectedImage); //} catch (FileNotFoundException e) { // e.printStackTrace(); //} // method 3 // iv.setImageURI(selectedImage); } break; } } }
If you’re wondering why I used ACTION_GET_CONTENT
and not ACTION_PICK
, then the answer lies here.
Sometimes it's useful to get full path to image from Gallery. For this purpose use following auxiliary class
public class ImageFilePath { @SuppressLint("NewApi") public static String getPath(final Context context, final Uri uri) { //check for KITKAT or above 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]; } } // 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; } 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; } public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } }
Usage
if(resultCode == RESULT_OK){ Uri selectedImage = data.getData(); String path = ImageFilePath.getPath(activity.getApplicationContext(), selectedImage); }
How to get image from Camera
Open your AndroidManifest.xml file where we add permission to access camera and write external storage.
<uses-feature android:name="android.hardware.camera" android:required="true" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.CAMERA"/>
Lets see how you can use the camera to take a photo for your application.
First create a layout with a Button
and an ImageView
.
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <Button android:onClick="take" android:text="Take" android:layout_centerHorizontal="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ImageView android:id="@+id/iv" android:layout_centerInParent="true" android:layout_width="200dp" android:layout_height="200dp" /> </RelativeLayout>
Next use following snippet to take photo from camera. The take picture button in activity will fire an ACTION_IMAGE_CAPTURE
intent to open the camera, after a picture is taken and saved, it will return to this activity calling the onActivityResult
method will be called.
import android.Manifest; public class MainActivity extends AppCompatActivity { private static final int CAMERA_TAKE_REQUEST = 200; private final static int ALL_PERMISSIONS_RESULT = 101; private ImageView iv; File file; Uri uri; private Context context; private Activity activity; ArrayList<String> permissions = new ArrayList<>(); ArrayList<String> permissionsToRequest; ArrayList<String> permissionsRejected = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); iv = (ImageView) findViewById(R.id.iv); context = this; activity = MainActivity.this; permissions.add(Manifest.permission.CAMERA); permissions.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); permissionsToRequest = findUnAskedPermissions(permissions); } @TargetApi(Build.VERSION_CODES.M) public void take(View v) { if(checkCameraExists()) { if (permissionsToRequest.size() > 0) { requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), ALL_PERMISSIONS_RESULT); } else { Toast.makeText(context,"Permissions already granted.", Toast.LENGTH_LONG).show(); launchCamera(); } } else { Toast.makeText(activity, "Camera not available.", Toast.LENGTH_SHORT).show(); } } public boolean checkCameraExists() { return activity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA); } private void launchCamera() { Intent intent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE); file = new File(Environment.getExternalStorageDirectory(), String.valueOf(System.currentTimeMillis()) + ".jpg"); uri = FileProvider.getUriForFile(activity, activity.getApplicationContext().getPackageName() + ".provider", file); intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, CAMERA_TAKE_REQUEST); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch(requestCode){ case CAMERA_TAKE_REQUEST: iv.setImageURI(android.net.Uri.parse(file.toURI().toString())); break; } } // // Permissions // private ArrayList findUnAskedPermissions(ArrayList<String> wanted) { ArrayList result = new ArrayList(); for (String perm : wanted) { if (!hasPermission(perm)) { result.add(perm); } } return result; } private boolean hasPermission(String permission) { if (canAskPermission()) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { return (checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED); } } return true; } private boolean canAskPermission() { return (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1); } @TargetApi(Build.VERSION_CODES.M) @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case ALL_PERMISSIONS_RESULT: for (String perms : permissionsToRequest) { if (!hasPermission(perms)) { permissionsRejected.add(perms); } } if (permissionsRejected.size() > 0) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (shouldShowRequestPermissionRationale(permissionsRejected.get(0))) { String msg = "These permissions are mandatory for the application. Please allow access."; showMessageOKCancel(msg, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(permissionsRejected.toArray( new String[permissionsRejected.size()]), ALL_PERMISSIONS_RESULT); } } }); return; } } } else { Toast.makeText(context, "Permissions garanted.", Toast.LENGTH_LONG).show(); launchCamera(); } break; } } private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(activity) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", null) .create() .show(); } }
Android 7.0 Nougat introduced some file system permission changes in order to improve security. If you’ve already updated your app to targetSdkVersion 24
(or higher) and you’re passing a file://
URI outside your package domain through an Intent
, then what you’ll get is a FileUriExposedException
.
So, Android may throw FileUriExposedException
in Android 7.0 (API level 24) and above, this exception will come when you will expose a file://
URIs outside your package domain through Intent
.
FileProvider
is a special subclass of ContentProvider which allows us to securely share file through a content://
URI instead of file://
one. Why is this a better approach? Because you’re granting a temporary access to the file, which will be available for the receiver activity or service until they are active/running.
We create our own class inheriting FileProvider
in order to make sure our FileProvider
doesn't conflict with FileProviders
declared in imported dependencies as described here.
Add a class extending FileProvider
public class GenericFileProvider extends FileProvider {}
Next, add the GenericFileProvider
in our AndroidManifest.xml:
<manifest> ... <application> ... <provider android:name=".GenericFileProvider" android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_provider_paths" /> </provider> ... </application> </manifest>
We’re going to set android:exported
to false
because we don’t need it to be public, android:grantUriPermissions
to true
because it will grant temporary access to files and android:authorities
to a domain you control, so if your domain is me.proft.superapp
then you can use something like me.proft.superapp.provider
. The authority of a provider should be unique and that’s the reason why we are using our application ID plus something like .provider.
Then we need to create the file_provider_path
in the res/xml
folder. That’s the file which defines the folders which contain the files you will be allowed to share safely. In our case we just need access to the external storage folder:
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="external_files" path="." /> </paths>
The final step is to change the line of code below in
File file = new File(Environment.getExternalStorageDirectory(), String.valueOf(System.currentTimeMillis()) + ".jpg"); Uri photoURI = Uri.fromFile(file);
to
Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
If you're using an Intent
to make the system open your file, you may need to add the following line of code:
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Instead of using Uri.fromFile(file)
we create our URI with FileProvider.getUriForFile(context, string, file)
which will generate a new content://
URI with the authority defined pointing to the file we passed in.
Useful links