In this article, I’m going to explain how to upload
Objective
- Get image from Camera/Gallery using FileProvider and show preview on ImageView
- Upload selected to server with progress and without ProgressBar
Android Upload File to Server with Progress
Prerequisite
To better understanding this article, You need to have basic knowledge of the following topics.
- You have to the idea about android Camera utilities
- Have basic knowledge of FileProvider
- How to use retrofit with RxJava
- Have working experience of the MVP Design Pattern.
Read our previous article that give you idea about Camera, FileProvider and MVP
1. Create a new Project
Open Android Studio and create a new project with BasicActivity template in Android
2. Add Dependency
2.1 Now open build.gradle file and add following dependency
// image implementation('com.github.bumptech.glide:glide:4.7.1@aar') { transitive = true } implementation 'com.karumi:dexter:4.2.0' implementation 'com.android.support:cardview-v7:28.0.0' implementation 'com.android.support:design:28.0.0' // code generator for view implementation "com.jakewharton:butterknife:$rootProject.butterKnifeVersion" annotationProcessor "com.jakewharton:butterknife-compiler:$rootProject.butterKnifeVersion" // reactive implementation "io.reactivex.rxjava2:rxjava:$rootProject.rxjava2Version" implementation "io.reactivex.rxjava2:rxandroid:$rootProject.rxandroidVersion" implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
2.2 Now go to parent build.gradle (Project level build.gradle) add all dependency version in single place
// Define versions in a single place ext { // App dependencies rxjava2Version = '2.1.9' rxandroidVersion = '2.0.1' butterKnifeVersion = '8.8.1' rxjava2Version = '2.1.9' rxandroidVersion = '2.0.1' }
2.3. Set Java Source 1.8 for using lambda expression
Inside build.gradle set source compatibility and target compatibility java version 1.8 in compile options
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 }
3. Add Uses Permission
Add Storage, Camera and Internet permission and uses features in AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_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" /> <uses-permission android:name="android.permission.INTERNET"/>
4. Create a FileProvider
4.1 In this demo, we get file and Camera/Gallery so we need file provider access for getting file URI. So declare provider in AndroidManifest.xml inside <application> Tag.
<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>
4.2 So, define a file provider path. go to res folder and create an XML folder after that create a file with name file_provider_paths.
<?xml version="1.0" encoding="utf-8"?> <paths> <external-path name="my_images" path="Android/data/com.androidwave.fileupload/files/Pictures" /> </paths>
You must replace com.androidwave.fileupload with your package name
5. Create a Contract for Image for developing MVP design pattern
Go to src folder and create a new source folder name picker for image src ImageContract. Meanwhile, Create a new file inside picker folder with ImageContract.java .
package com.androidwave.fileupload.picker; import android.net.Uri; import java.io.File; /** * Created on : Jan 07, 2019 */ public class ImageContract { public interface View { boolean checkPermission(); void showPermissionDialog(boolean isGallery); File getFilePath(); void openSettings(); void startCamera(File file); void chooseGallery(); File newFile(); void showErrorDialog(); void displayImagePreview(File mFile); String getRealPathFromUri(Uri contentUri); } interface Presenter { void cameraClick(); void chooseGalleryClick(); void saveImage(String filePath); String getImage(); void showPreview(File mFile); } }
6. Prepare Image Presenter
Go to src=>picker folder and create a new file with ImagePresenter which implementing ImageContract.Presenter class.
package com.androidwave.fileupload.picker; import java.io.File; /** * Created on : Jan 07, 2019 */ public class ImagePresenter implements ImageContract.Presenter { private final ImageContract.View view; private String selectedFile; public ImagePresenter(ImageContract.View view) { this.view = view; } @Override public void cameraClick() { if (!view.checkPermission()) { view.showPermissionDialog(false); return; } File file = view.newFile(); if (file == null) { view.showErrorDialog(); return; } view.startCamera(file); } @Override public void chooseGalleryClick() { if (!view.checkPermission()) { view.showPermissionDialog(true); return; } view.chooseGallery(); } @Override public void saveImage(String path) { selectedFile = path; } @Override public String getImage() { return selectedFile; } @Override public void showPreview(File mFile) { view.displayImagePreview(mFile); } }
So now first part of this project is defined now come to second part which is image uploading
7. Write a Contract for File Uploading
Go to src folder and create a new file with name FileUploaderContract and defined blueprint of Model, View and Presenter.
package com.androidwave.fileupload.network; import io.reactivex.Flowable; import io.reactivex.Single; import okhttp3.ResponseBody; /** * Created on : Dec 30, 2018 */ public class FileUploaderContract { public interface View { void showErrorMessage(String message); void uploadCompleted(); void setUploadProgress(int progress); } interface Presenter { void onFileSelected(String selectedFile, String userName, String email); void onFileSelectedWithoutShowProgress(String selectedFilePath, String userName, String email); void cancel(); } interface Model { Flowable<Double> uploadFile(String selectedFilePath, String userName, String email); Single<ResponseBody> uploadFileWithoutProgress(String filePath, String userName, String email); } }
8. Furthermore, Define File Uploader Model
Simply create a file with FileUploaderModel names and implement FileUploaderContract.Model
package com.androidwave.fileupload.network; import android.support.annotation.NonNull; import io.reactivex.BackpressureStrategy; import io.reactivex.Flowable; import io.reactivex.FlowableEmitter; import io.reactivex.Single; import java.io.File; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; /** * Created on : Dec 30, 2018 */ public class FileUploaderModel implements FileUploaderContract.Model { private final FileUploadService service; public static final String MULTIPART_FORM_DATA = "multipart/form-data"; public FileUploaderModel(FileUploadService service) { this.service = service; } /** * Create request body for image resource */ private RequestBody createRequestForImage(File file) { return RequestBody.create(MediaType.parse("image/*"), file); } /** * Create request body for video resource */ private RequestBody createRequestForVideo(File file) { return RequestBody.create(MediaType.parse("video/*"), file); } /** * Create request body for string */ @NonNull private RequestBody createPartFromString(String descriptionString) { return RequestBody.create(MediaType.parse(MULTIPART_FORM_DATA), descriptionString); } /** * return multipart part request body */ private MultipartBody.Part createMultipartBody(String filePath) { File file = new File(filePath); RequestBody requestBody = createRequestForImage(file); return MultipartBody.Part.createFormData("file_name", file.getName(), requestBody); } /** * return multi part body in format of FlowableEmitter */ private MultipartBody.Part createMultipartBody(String filePath, FlowableEmitter<Double> emitter) { File file = new File(filePath); return MultipartBody.Part.createFormData("file_name", file.getName(), createCountingRequestBody(file, emitter)); } private RequestBody createCountingRequestBody(File file, FlowableEmitter<Double> emitter) { RequestBody requestBody = createRequestForImage(file); return new CountingRequestBody(requestBody, (bytesWritten, contentLength) -> { double progress = (1.0 * bytesWritten) / contentLength; emitter.onNext(progress); }); } @Override public Flowable<Double> uploadFile(String selectedFile, String username, String email) { RequestBody mUserName = createPartFromString(username); RequestBody mEmail = createPartFromString(email); return Flowable.create(emitter -> { try { ResponseBody response = service.onFileUpload(mUserName, mEmail, createMultipartBody(selectedFile, emitter)) .blockingGet(); emitter.onComplete(); } catch (Exception e) { emitter.tryOnError(e); } }, BackpressureStrategy.LATEST); } @Override public Single<ResponseBody> uploadFileWithoutProgress(String filePath, String username, String email) { RequestBody mUserName = createPartFromString(username); RequestBody mEmail = createPartFromString(email); return service.onFileUpload(mUserName, mEmail, createMultipartBody(filePath)); } }
9. Create a presenter for File Uploading
Same as previously create a file with FileUploaderPresenter and implement FileUploaderContract.Presenter
package com.androidwave.fileupload.network; import android.text.TextUtils; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; /** * Created on : Dec 30, 2018 */ public class FileUploaderPresenter implements FileUploaderContract.Presenter { private final FileUploaderContract.Model model; private final FileUploaderContract.View view; private Disposable videoUploadDisposable; public FileUploaderPresenter(FileUploaderContract.View view, FileUploaderContract.Model model) { this.view = view; this.model = model; } @Override public void onFileSelected(String selectedFilePath, String userName, String email) { if (TextUtils.isEmpty(selectedFilePath)) { view.showErrorMessage("Incorrect file path"); return; } videoUploadDisposable = model.uploadFile(selectedFilePath, userName, email) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( progress -> view.setUploadProgress((int) (100 * progress)), error -> view.showErrorMessage(error.getMessage()), () -> view.uploadCompleted() ); } @Override public void onFileSelectedWithoutShowProgress(String selectedFilePath, String userName, String email) { if (TextUtils.isEmpty(selectedFilePath)) { view.showErrorMessage("Incorrect file path"); return; } videoUploadDisposable = model.uploadFileWithoutProgress(selectedFilePath, userName, email) .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( result -> view.uploadCompleted(), error -> view.showErrorMessage(error.getMessage()) ); } @Override public void cancel() { if (videoUploadDisposable != null && !videoUploadDisposable.isDisposed()) { videoUploadDisposable.dispose(); } } }
Finally, MVP stuff is almost complete
10. Create an API interface for using Retrofit lib
Go in src folder and create an interface with FileUploadService names and create an on file upload method
package com.androidwave.fileupload.network; import io.reactivex.Single; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.Part; /** * Created on : Dec 30, 2018 */ public interface FileUploadService { @Multipart @POST("fileUpload.php") Single<ResponseBody> onFileUpload(@Part("username") RequestBody mUserName, @Part("email") RequestBody mEmail, @Part MultipartBody.Part file); }
11. Create Retrofit instance
In src folder create a Retrofit utility class which return Retrofit client using OkHttpClient and RxJava2 CallAdapter
package com.androidwave.fileupload.network; import com.androidwave.fileupload.BuildConfig; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import okhttp3.Request; import retrofit2.Retrofit; import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory; /** * Created on : Dec 30, 2018 */ public class ServiceGenerator { public static FileUploadService createService() { OkHttpClient client = new OkHttpClient.Builder() .readTimeout(2, TimeUnit.MINUTES) .writeTimeout(2, TimeUnit.MINUTES).addInterceptor(chain -> { Request original = chain.request(); Request.Builder requestBuilder = requestBuilder = original.newBuilder() .method(original.method(), original.body()); Request request = requestBuilder.build(); return chain.proceed(request); }).build(); return new Retrofit .Builder() .baseUrl(BuildConfig.BASE_URL) .client(client) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .build() .create(FileUploadService.class); } }
12 . Open build.gradle file define BASE_URL like below
buildConfigField("String", "BASE_URL", "\"http://apps-point.com/api2/\"")
While creating the project, We were selected BasicActivity template. So, MainActivty.java and activity_main.xml was automatically created.
13. Open activity_main.xml and create a profile view using 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" android:fillViewport="true" app:layout_behavior="@string/appbar_scrolling_view_behavior" > <RelativeLayout android:layout_width="match_parent" android:layout_height="wrap_content" > <ImageView android:id="@+id/header_cover_image" android:layout_width="match_parent" android:layout_height="200dp" android:scaleType="centerCrop" android:src="@drawable/user_profile_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/user" /> <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="#eb597C8C" android:elevation="4dp" android:paddingBottom="24dp" > <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" > <Button android:id="@+id/upload_file_progress" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:gravity="center" android:text="@string/title_with_progress" android:textSize="14sp" /> <Button android:id="@+id/btn_upload_file_without_progress" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_margin="5dp" android:layout_marginTop="3dp" android:layout_marginBottom="3dp" android:background="#fff" android:clickable="true" android:elevation="4dp" android:gravity="center" android:text="@string/title_without_progress" android:textSize="14sp" /> <TextView android:id="@+id/textViewProgress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:padding="14dp" android:text="" android:textSize="15sp" /> </LinearLayout> </RelativeLayout> </ScrollView>
14. Open MainActivity and do following this
In this activity, I dividing into three-part
- Bind view with an activity using Butterknife
- Getting Image from camera/gallery
- Compress the file using FileCompressor utility. (I have explained or the previous article Read Here )
- Upload selected to the server
Bind view with activity using Butterknife
Go to setContentView layout xml name do right click and sleeted generate after that generate butter knife injection
static final int REQUEST_TAKE_PHOTO = 1001; static final int REQUEST_GALLERY_PHOTO = 1002; static String[] permissions = new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}; @BindView(R.id.header_cover_image) ImageView headerCoverImage; @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; @BindView(R.id.profile_layout) RelativeLayout profileLayout; @BindView(R.id.textViewProgress) TextView txtProgress; @BindView(R.id.upload_file_progress) Button uploadFileProgress; @BindView(R.id.btn_upload_file_without_progress) Button btnUploadFileWithoutProgress; private ImagePresenter mImagePresenter; private FileUploaderPresenter mUploaderPresenter; private FileCompressor mCompressor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick({R.id.user_profile_photo, R.id.upload_file_progress, R.id.btn_upload_file_without_progress}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.user_profile_photo: break; case R.id.upload_file_progress: break; case R.id.btn_upload_file_without_progress: break; } }
14.2 getting image from camera/gallery
- Implement ImageContract.View and override un implement methods. Define each one operation like below
@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(boolean isGallery) { Dexter.withActivity(this).withPermissions(permissions) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { if (isGallery) { mImagePresenter.chooseGalleryClick(); } else { mImagePresenter.cameraClick(); } } // 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); } @Override public void openSettings() { } @Override public void startCamera(File file) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { if (file != null) { Uri mPhotoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoURI); mPhotoFile = file; 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) { try { File resultedFile = mCompressor.compressToFile(mPhotoFile); mImagePresenter.saveImage(resultedFile.getPath()); mImagePresenter.showPreview(resultedFile); } catch (IOException e) { e.printStackTrace(); } } else if (requestCode == REQUEST_GALLERY_PHOTO) { Uri selectedImage = data.getData(); try { File resultedFile = mCompressor.compressToFile(new File(Objects.requireNonNull(getRealPathFromUri(selectedImage)))); mImagePresenter.saveImage(resultedFile.getPath()); mImagePresenter.showPreview(resultedFile); } catch (IOException e) { e.printStackTrace(); } } } } @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(File mFile) { Glide.with(MainActivity.this).load(mFile).apply(new RequestOptions().centerCrop().circleCrop().placeholder(R.drawable.user)).into(userProfilePhoto); } @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(); } } } 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(); }
14.3 Upload selected to the server
- For doing this implement FileUploaderContract.View and add un implement method.
@Override public void showErrorMessage(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); } @Override public void uploadCompleted() { CommonUtils.hideLoading(); Toast.makeText(getApplicationContext(), getString(R.string.file_upload_successful), Toast.LENGTH_SHORT).show(); } @Override public void setUploadProgress(int progress) { txtProgress.setText("Uploading ..." + String.valueOf(progress)); }
15. The finally MainActivity looks like below
package com.androidwave.fileupload; 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.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.Toast; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.OnClick; import com.androidwave.fileupload.network.FileUploaderContract; import com.androidwave.fileupload.network.FileUploaderModel; import com.androidwave.fileupload.network.FileUploaderPresenter; import com.androidwave.fileupload.network.ServiceGenerator; import com.androidwave.fileupload.picker.ImageContract; import com.androidwave.fileupload.picker.ImagePresenter; import com.androidwave.fileupload.utils.CommonUtils; import com.androidwave.fileupload.utils.FileCompressor; 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; import java.util.Objects; public class MainActivity extends AppCompatActivity implements ImageContract.View, FileUploaderContract.View { static final int REQUEST_TAKE_PHOTO = 1001; static final int REQUEST_GALLERY_PHOTO = 1002; static String[] permissions = new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA }; @BindView(R.id.header_cover_image) ImageView headerCoverImage; @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; @BindView(R.id.profile_layout) RelativeLayout profileLayout; @BindView(R.id.textViewProgress) TextView txtProgress; @BindView(R.id.upload_file_progress) Button uploadFileProgress; @BindView(R.id.btn_upload_file_without_progress) Button btnUploadFileWithoutProgress; private ImagePresenter mImagePresenter; private FileUploaderPresenter mUploaderPresenter; private FileCompressor mCompressor; File mPhotoFile; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mImagePresenter = new ImagePresenter(this); mUploaderPresenter = new FileUploaderPresenter(this, new FileUploaderModel(ServiceGenerator.createService())); mCompressor = new FileCompressor(this); } @OnClick({ R.id.user_profile_photo, R.id.upload_file_progress, R.id.btn_upload_file_without_progress }) public void onViewClicked(View view) { switch (view.getId()) { case R.id.user_profile_photo: selectImage(); break; case R.id.upload_file_progress: mUploaderPresenter.onFileSelected(mImagePresenter.getImage(), "androidwave", "info@androidwave"); break; case R.id.btn_upload_file_without_progress: CommonUtils.showLoadingDialog(this); mUploaderPresenter.onFileSelectedWithoutShowProgress(mImagePresenter.getImage(), "androidwave", "info@androidwave"); break; } } private void selectImage() { txtProgress.setText(""); 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("Capture Photo")) { mImagePresenter.cameraClick(); } else if (items[item].equals("Choose from Library")) { mImagePresenter.chooseGalleryClick(); } else if (items[item].equals("Cancel")) { dialog.dismiss(); } }); builder.show(); } @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(boolean isGallery) { Dexter.withActivity(this).withPermissions(permissions) .withListener(new MultiplePermissionsListener() { @Override public void onPermissionsChecked(MultiplePermissionsReport report) { // check if all permissions are granted if (report.areAllPermissionsGranted()) { if (isGallery) { mImagePresenter.chooseGalleryClick(); } else { mImagePresenter.cameraClick(); } } // 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); } @Override public void openSettings() { } @Override public void startCamera(File file) { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (takePictureIntent.resolveActivity(getPackageManager()) != null) { if (file != null) { Uri mPhotoURI = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, mPhotoURI); mPhotoFile = file; 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) { try { File resultedFile = mCompressor.compressToFile(mPhotoFile); mImagePresenter.saveImage(resultedFile.getPath()); mImagePresenter.showPreview(resultedFile); } catch (IOException e) { e.printStackTrace(); } } else if (requestCode == REQUEST_GALLERY_PHOTO) { Uri selectedImage = data.getData(); try { File resultedFile = mCompressor.compressToFile( new File(Objects.requireNonNull(getRealPathFromUri(selectedImage)))); mImagePresenter.saveImage(resultedFile.getPath()); mImagePresenter.showPreview(resultedFile); } catch (IOException e) { e.printStackTrace(); } } } } @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(File mFile) { Glide.with(MainActivity.this) .load(mFile) .apply(new RequestOptions().centerCrop().circleCrop().placeholder(R.drawable.user)) .into(userProfilePhoto); } @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(); } } } 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 showErrorMessage(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show(); } @Override public void uploadCompleted() { CommonUtils.hideLoading(); Toast.makeText(getApplicationContext(), getString(R.string.file_upload_successful), Toast.LENGTH_SHORT).show(); } @Override public void setUploadProgress(int progress) { txtProgress.setText("Uploading ..." + String.valueOf(progress)); } }
Read our recent post on how to generate Android and iOS folders in React Native
6 Comments
thanks for the article, but no access to download the project.
Where is class CountingRequestBody? and fileupload.php?
Where is fileUpload.php?
Will update soon
Holy cow!! A working example.. This code is VERY nicely laid out…
Thanks so much for posting it..
Open app/build.gradle file here I have define = > buildConfigField(“String”, “BASE_URL”, “\”http://apps-point.com/api2/\””)