My previous tutorials explains how to capture image Camera & Gallery using FileProvider. Most people
Prerequisite
In this article, Getting images from Camera & Gallery using MVP, You need to have knowledge camera and FileProvider module. I recommend you go through my previous tutorials that give you an idea about camera integration into your apps using standard design patterns. Let’s start getting image Camera & Gallery tutorial
Getting images from Camera & Gallery Sample App
Create a New Project
Simply, Open Android Studio , Choose a Create a New Project – > Fill Project Details -> Select Min SDK Version – > Select BasicActivity template -> Finish
Open string.xml from res=>Value and add below String Values
<resources> <string name="app_name">ImagePicker</string> <string name="action_settings">Settings</string> <string name="error_message_no_more_space"> Your storage is unavailable.</string> <string name="error_message_insufficient_space"> You have few space available, please make sure your have more than 5MB on disk to take pictures!</string> <string name="ok">Ok</string> <string name="error_message">Error occurred!</string> <string name="message_need_permission">Need Permissions</string> <string name="message_grant_permission">This app needs permission to use this feature. You can grant them in app settings.</string> <string name="label_setting">GOTO SETTINGS</string> <string name="cancel">Cancel</string> <string name="take_photo">Take Photo</string> <string name="choose_gallery">Choose from Gallery</string> <string name="url_androidwave">https://androidwave.com/</string> <string name="url_androidwave_android">https://androidwave.com/category/android/</string> </resources>
Create a new package folder names picker is picker for separating other’s files.
Simple right-click on src folder and choose package furthermore put name is the picker
Create MVP Contract
As you know are following
package com.androidwave.imagepicker.picker; import android.net.Uri; import java.io.File; /** * Created on : Jan 06, 2019 * Author : AndroidWave * Website : https://androidwave.com/ */ public class ImageContract { public interface View { boolean checkPermission(); void showPermissionDialog(); File getFilePath(); void openSettings(); void startCamera(File file); void chooseGallery(); void showNoSpaceDialog(); int availableDisk(); File newFile(); void showErrorDialog(); void displayImagePreview(String mFilePath); void displayImagePreview(Uri mFileUri); String getRealPathFromUri(Uri contentUri); } interface Presenter { void cameraClick(); void ChooseGalleryClick(); void saveImage(Uri uri); void permissionDenied(); void showPreview(String mFilePath); void showPreview(Uri mFileUri); } }
Now create a new class with name ImagePresenter.java
For implementing Presenter Contract create a new class ImagePresenter
package com.androidwave.imagepicker.picker; import android.net.Uri; import java.io.File; /** * Created on : Jan 06, 2019 * Author : AndroidWave * Website : https://androidwave.com/ */ public class ImagePresenter implements ImageContract.Presenter { private final ImageContract.View view; public ImagePresenter(ImageContract.View view) { this.view = view; } @Override public void cameraClick() { if (!view.checkPermission()) { view.showPermissionDialog(); return; } if (view.availableDisk() <= 5) { view.showNoSpaceDialog(); return; } File file = view.newFile(); if (file == null) { view.showErrorDialog(); return; } view.startCamera(file); } @Override public void ChooseGalleryClick() { if (!view.checkPermission()) { view.showPermissionDialog(); return; } view.chooseGallery(); } @Override public void saveImage(Uri uri) { } @Override public void permissionDenied() { view.showPermissionDialog(); } @Override public void showPreview(String mFilePath) { view.displayImagePreview(mFilePath); } @Override public void showPreview(Uri mFileUri) { view.displayImagePreview(mFileUri); } }
Open AndroidManifest.xml add below uses permission
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> <uses-feature android:name="android.hardware.camera.flash" />
Define a FileProvider in AndroidManifest.xml
We are using FileProvider in this example. So open AndroidManifest.xml and put below code inside the <application>
<provider android:name="android.support.v4.content.FileProvider" 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>
Go to res directory and create a file inside xml folder
Note make sure, you have to replace com.androidwave.imagepicker with your package name
<paths> <external-path name="my_images" path="Android/data/com.androidwave.imagepicker/files/Pictures" /> <!-- replace com.androidwave.imagepicker with your package name --> </paths>
Add some views in activity_main.xml
In Android Studio, If you choose the BasicActivity template while creating a project. below file will automatically create
- MainActivity.java
- activity_main.xml
- content_main.xml
Open activity_main.xml and add following view
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout 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" tools:context=".MainActivity"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:visibility="gone" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <include layout="@layout/content_main" /> </android.support.design.widget.CoordinatorLayout>
Now open content_main.xml add below code
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/header_cover_image" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop" android:src="@drawable/nav_menu_header_bg" /> <ImageButton android:id="@+id/user_profile_photo" android:layout_width="120dp" android:layout_height="120dp" android:layout_below="@+id/header_cover_image" android:layout_centerHorizontal="true" android:layout_marginTop="-60dp" android:background="@drawable/profile_imageview_bg" android:elevation="5dp" android:scaleType="centerCrop" android:src="@drawable/profile_pic" /> <RelativeLayout android:id="@+id/profile_layout" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@+id/header_cover_image" android:background="#ebca0707" android:elevation="4dp" android:paddingBottom="24dp"> <ImageView android:id="@+id/add_friend" android:layout_width="30dp" android:layout_height="30dp" android:layout_alignParentRight="true" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" android:layout_marginTop="16dp" android:src="@android:drawable/ic_menu_add" /> <TextView android:id="@+id/user_profile_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="76dp" android:text="AndroidWave " android:textColor="#fff" android:textSize="24sp" android:textStyle="bold" /> <TextView android:id="@+id/user_profile_short_bio" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/user_profile_name" android:layout_centerHorizontal="true" android:layout_marginTop="12dp" android:text="Android & iOS Developer Blog" android:textColor="#fff" android:textSize="14sp" /> </RelativeLayout> <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/profile_layout" android:layout_marginTop="5dp" android:orientation="vertical"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:padding="20dp" android:text="Android ExoPlayer Tutorial" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginBottom="3dp" android:layout_marginTop="3dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:padding="20dp" android:text="Android Camera2 API Tutorial" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:padding="20dp" android:text="Android RecyclerView UI " /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginBottom="3dp" android:layout_marginTop="3dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:padding="20dp" android:text="Android Kotlin Development" /> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:padding="20dp" android:text="iOS UI Design" /> </LinearLayout> </RelativeLayout> </ScrollView>
Open MainActivity.java and do following operation
package com.ripleee.helper; import android.Manifest; import android.app.AlertDialog; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.provider.Settings; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import com.androidwave.imagepicker.picker.ImageContract; import com.androidwave.imagepicker.picker.ImagePresenter; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.karumi.dexter.Dexter; import com.karumi.dexter.MultiplePermissionsReport; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.multi.MultiplePermissionsListener; import java.io.File; import java.io.IOException; import java.util.Calendar; import java.util.List; package com.androidwave.imagepicker; public class MainActivity extends AppCompatActivity implements ImageContract.View { static final int REQUEST_TAKE_PHOTO = 101; static final int REQUEST_GALLERY_PHOTO = 102; static String[] permissions = new String[] { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }; @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.user_profile_photo) ImageButton userProfilePhoto; @BindView(R.id.user_profile_name) TextView userProfileName; @BindView(R.id.user_profile_short_bio) TextView userProfileShortBio; private ImagePresenter mPresenter; Uri photoURI; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setSupportActionBar(toolbar); ButterKnife.bind(this); mPresenter = new ImagePresenter(this); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } @Override public void startCamera(File file) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { if (file != null) { photoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO); } } } @Override public void chooseGallery() { Intent pickPhoto = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); pickPhoto.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); startActivityForResult(pickPhoto, REQUEST_GALLERY_PHOTO); } @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == RESULT_OK) { if (requestCode == REQUEST_TAKE_PHOTO) { mPresenter.showPreview(photoURI); } else if (requestCode == REQUEST_GALLERY_PHOTO) { Uri selectedImage = data.getData(); String mPhotoPath = getRealPathFromUri(selectedImage); mPresenter.showPreview(mPhotoPath); } } } @Override public boolean checkPermission() { for (String mPermission : permissions) { int result = ActivityCompat.checkSelfPermission(this, mPermission); if (result == PackageManager.PERMISSION_DENIED) return false; } return true; } @Override public void showPermissionDialog() { Dexter.withActivity(this).withPermissions(permissions) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { } // check for permanent denial of any permission if (report.isAnyPermissionPermanentlyDenied()) { // show alert dialog navigating to Settings showSettingsDialog(); } } @Override public void onPermissionRationaleShouldBeShown(List<PermissionRequest> permissions, PermissionToken token) { token.continuePermissionRequest(); } }).withErrorListener(error -> showErrorDialog()) .onSameThread() .check(); } @Override public File getFilePath() { return getExternalFilesDir(Environment.DIRECTORY_PICTURES); } public void showSettingsDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.message_need_permission)); builder.setMessage(getString(R.string.message_grant_permission)); builder.setPositiveButton(getString(R.string.label_setting), (dialog, which) -> { dialog.cancel(); openSettings(); }); builder.setNegativeButton(getString(R.string.cancel), (dialog, which) -> dialog.cancel()); builder.show(); } @Override public void openSettings() { Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, 101); } @Override public void showNoSpaceDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.error_message_no_more_space)); builder.setMessage(getString(R.string.error_message_insufficient_space)); builder.setPositiveButton(getString(R.string.ok), (dialog, which) -> dialog.cancel()); builder.show(); } @Override public int availableDisk() { File mFilePath = getFilePath(); long freeSpace = mFilePath.getFreeSpace(); return Math.round(freeSpace / 1048576); } @Override public File newFile() { Calendar cal = Calendar.getInstance(); long timeInMillis = cal.getTimeInMillis(); String mFileName = String.valueOf(timeInMillis) + ".jpeg"; File mFilePath = getFilePath(); try { File newFile = new File(mFilePath.getAbsolutePath(), mFileName); newFile.createNewFile(); return newFile; } catch (IOException e) { e.printStackTrace(); } return null; } @Override public void showErrorDialog() { Toast.makeText(getApplicationContext(), getString(R.string.error_message), Toast.LENGTH_SHORT) .show(); } @Override public void displayImagePreview(String mFilePath) { Glide.with(MainActivity.this) .load(mFilePath) .apply(new RequestOptions().centerCrop().circleCrop().placeholder(R.drawable.profile_pic)) .into(userProfilePhoto); } @Override public void displayImagePreview(Uri mFileUri) { Glide.with(MainActivity.this) .load(mFileUri) .apply(new RequestOptions().centerCrop().circleCrop().placeholder(R.drawable.profile_pic)) .into(userProfilePhoto); } /** * Get real file path from URI */ @Override public String getRealPathFromUri(Uri contentUri) { Cursor cursor = null; try { String[] proj = { MediaStore.Images.Media.DATA }; cursor = getContentResolver().query(contentUri, proj, null, null, null); assert cursor != null; int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); cursor.moveToFirst(); return cursor.getString(columnIndex); } finally { if (cursor != null) { cursor.close(); } } } private void selectImage() { final CharSequence[] items = { getString(R.string.take_photo), getString(R.string.choose_gallery), getString(R.string.cancel) }; AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); builder.setItems(items, (dialog, item) -> { if (items[item].equals("Take Photo")) { mPresenter.cameraClick(); } else if (items[item].equals("Choose from Gallery")) { mPresenter.ChooseGalleryClick(); } else if (items[item].equals("Cancel")) { dialog.dismiss(); } }); builder.show(); } @OnClick({ R.id.user_profile_photo, R.id.user_profile_name, R.id.user_profile_short_bio }) public void onViewClicked(View view) { switch (view.getId()) { case R.id.user_profile_photo: selectImage(); break; case R.id.user_profile_name: Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_androidwave))); startActivity(browserIntent); break; case R.id.user_profile_short_bio: Intent mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_androidwave_android))); startActivity(mIntent); break; } } }
In this post we learned how to capture image camera & gallery in android using MVP design patterns. Happy Coding 😁
2 Comments
Hi, please i have this bug in MultiplePermissionsListener()
Nice job, man!