آپلود فایل بر روی سرور در یک پروژه اندرویدی


آپلود فایل بر روی سرور در اندروید

در این مقاله به آموزش آپلود فایل روی سرور با استفاده از retrofit می پردازیم. آپلود فایل امکانی ست که در بسیاری برنامه ها وجود دارد. آخرین راه حل موجود برای ایجاد این امکان موضوع این مقاله می باشد. در این آموزش با استفاده از Rxjava و Retrofit با معماری MVP آپلود فایل در برنامه اندرویدی را عملی می سازیم. این آموزش از مجموعه آموزش های صفر تا صد اندرویدی ست.

نیازمندی های این آموزش

  • آشنایی با بکارگیری دوربین در پروژه اندرویدی
  • آشنایی با FileProvider
  • چگونگی استفاده از rxjava با retrofit
  • تجربه کار با معماری MVP

قدم اول : ساخت پروژه و افزودن dependency های لازم به فایل build.gradle

// 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'

برای مدیریت نسخه کتابخانه های اضافه شده در build.gradle :

// 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'
}

قدم دوم : افزودن permission های لازم در مانیفست

در 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"/>

قدم سوم : ساخت FileProvider

برای دستیابی به URI فایل های عکس های دوربین یا گالری نیاز به تعریف FileProvider داریم. پس در فایل AndroidManifest.xml، در تگ <application> به شکل زیر FileProvider را مشخص می کنیم:

<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>

در فولدر res فایل xml ی با نام file_provider_paths  تعریف می کنیم که مسیر مورد نیاز FileProvider را در خود نگه می دارد :

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path
        name="my_images"
        path="Android/data/com.gitigit.fileupload/files/Pictures" />
</paths>

فراموش نکنید به جای com.gitigit.fileupload نام پکیج پروژه خود را بنویسید.

قدم چهارم : ساخت کلاس entitiy برای فایل عکس

در قسمت src فولدر جدیدی با نام picker می سازیم. سپس کلاس ImageContract.java را درون آن تعریف می کنیم.

import android.net.Uri;
import java.io.File;

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);
    }
}

قدم پنجم : آماده سازی presenter برای پروژه

در فولدر picker کلاس ImagePresenter  را به صورت زیر می سازیم :

import java.io.File;

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);
    }
}

قدم ششم : ساخت کلاس FileUploaderContract که model , view , presenter درون آن تعریف می شود

import io.reactivex.Flowable;
import io.reactivex.Single;
import okhttp3.ResponseBody;

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);
    }
}

قدم هفتم : ساخت کلاس model برای آپلود عکس

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;

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));
  }
}

قدم هشتم : ساخت presenter برای آپلود فایل

import android.text.TextUtils;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;

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

تا این مرحله معماری mvp در پروژه ساخته شده است.

قدم نهم : ساخت interface برای ارتباط با retrofit

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;

public interface FileUploadService {
  @Multipart
  @POST("fileUpload.php")
  Single<ResponseBody> onFileUpload(@Part("username") RequestBody mUserName,
      @Part("email") RequestBody mEmail, @Part MultipartBody.Part file);
}

قدم دهم : ساخت نمونه از retrofit

برای استفاده از سرویس retrofit کلاسی تعریف می کنیم که موارد مورد نیاز را در آن پیاده می سازیم. این کلاس یک کلاینت برای retrofit بر می گرداند که از Rxjava2 و  OkHttpClient استفاده می کند.

import com.androidwave.fileupload.BuildConfig;
import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;

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);
  }
}

قدم یازدهم : تعریف BASE_URL در build.gradle

buildConfigField("String", "BASE_URL", "\"http://apps-point.com/api2/\"")

قدم دوازدهم : ساخت activity_main.xml

?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>

قدم سیزدهم : تکمیل MainActivity

در کلاس اکتیویتی پیاده سازی چند قسمت را در نظر داریم :

  • bind نمودن view با اکتیویتی با استفاده از کتابخانه butterknife
  • دریافت تصویر از دوربین یا گالری
  • فشرده سازی فایل انتخابی
  • آپلود فایل روی سرور
  • bind نمودن view با اکتیویتی با استفاده از کتابخانه butterknife
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;
        }
    }
  • دریافت تصویر از دوربین یا گالری
@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();
    }
  • آپلود فایل روی سرور

برای این منظور متدهای زیر را در FileUploaderContract.View پیاده می سازیم :

@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));
   }

در انتها کلاس اکتیویتی باید به شکل زیر باشد :

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));
  }
}

 

به این پست امتیاز دهید

روی ستاره های کلیک کنید و امتیاز بدید

میانگین امتیاز / 5. تعداد:

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *




Enter Captcha Here : *

Reload Image