feat: Upload document basic Logic till taking pic from phone camera
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 35ad0f3..f0de421 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -3,6 +3,9 @@
package="com.mifos.apache.fineract">
<uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CAMERA"/>
+
+ <uses-feature android:name="android.hardware.camera" android:required="false"/>
<application
android:allowBackup="true"
diff --git a/app/src/main/java/com/mifos/apache/fineract/data/datamanager/DataManagerCustomer.java b/app/src/main/java/com/mifos/apache/fineract/data/datamanager/DataManagerCustomer.java
index 664c3ff..ef5d849 100644
--- a/app/src/main/java/com/mifos/apache/fineract/data/datamanager/DataManagerCustomer.java
+++ b/app/src/main/java/com/mifos/apache/fineract/data/datamanager/DataManagerCustomer.java
@@ -15,6 +15,7 @@
import io.reactivex.Completable;
import io.reactivex.Observable;
+import okhttp3.MultipartBody;
/**
* @author Rajan Maurya
@@ -65,4 +66,11 @@
return baseApiManager.getCustomerApi().fetchIdentificationScanCards(customerIdentifier,
identificationNumber);
}
+
+ public Completable uploadIdentificationCardScan(String customerIdentifier,
+ String identificationNumber, String scanIdentifier, String description,
+ MultipartBody.Part file) {
+ return baseApiManager.getCustomerApi().uploadIdentificationCardScan(customerIdentifier,
+ identificationNumber, scanIdentifier, description, file);
+ }
}
diff --git a/app/src/main/java/com/mifos/apache/fineract/data/services/CustomerService.java b/app/src/main/java/com/mifos/apache/fineract/data/services/CustomerService.java
index a2a5ab5..f9d6b1a 100644
--- a/app/src/main/java/com/mifos/apache/fineract/data/services/CustomerService.java
+++ b/app/src/main/java/com/mifos/apache/fineract/data/services/CustomerService.java
@@ -11,9 +11,12 @@
import io.reactivex.Completable;
import io.reactivex.Observable;
+import okhttp3.MultipartBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
+import retrofit2.http.Multipart;
import retrofit2.http.POST;
+import retrofit2.http.Part;
import retrofit2.http.Path;
import retrofit2.http.Query;
@@ -57,4 +60,14 @@
Observable<List<ScanCard>> fetchIdentificationScanCards(
@Path("identifier") String identifier,
@Path("identificationnumber") String identificationnumber);
+
+ @Multipart
+ @POST(EndPoints.API_CUSTOMER_PATH +
+ "/customers/{identifier}/identifications/{identificationnumber}/scans")
+ Completable uploadIdentificationCardScan(
+ @Path("identifier") String identifier,
+ @Path("identificationnumber") String identificationnumber,
+ @Query("scanIdentifier") String scanIdentifier,
+ @Query("description") String description,
+ @Part MultipartBody.Part file);
}
diff --git a/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/identificationdetails/IdentificationDetailsFragment.java b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/identificationdetails/IdentificationDetailsFragment.java
index 1197564..a908989 100644
--- a/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/identificationdetails/IdentificationDetailsFragment.java
+++ b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/identificationdetails/IdentificationDetailsFragment.java
@@ -16,6 +16,8 @@
import com.mifos.apache.fineract.ui.adapters.IdentificationScanAdapter;
import com.mifos.apache.fineract.ui.base.MifosBaseActivity;
import com.mifos.apache.fineract.ui.base.MifosBaseFragment;
+import com.mifos.apache.fineract.ui.online.identification.uploadidentificationscan
+ .UploadIdentificationCardBottomSheet;
import com.mifos.apache.fineract.utils.ConstantKeys;
import com.mifos.apache.fineract.utils.DateUtils;
@@ -104,6 +106,14 @@
return rootView;
}
+ @OnClick(R.id.fab_upload_identification_scan_card)
+ void addIdentificationCard() {
+ UploadIdentificationCardBottomSheet uploadIdentificationCardBottomSheet =
+ new UploadIdentificationCardBottomSheet();
+ uploadIdentificationCardBottomSheet.show(getChildFragmentManager(),
+ getString(R.string.upload_new_identification_card_scan));
+ }
+
@OnClick(R.id.iv_retry)
void onRetry() {
identificationDetailsPresenter.fetchIdentificationScanCards(customerIdentifier,
diff --git a/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardBottomSheet.java b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardBottomSheet.java
new file mode 100644
index 0000000..2666382
--- /dev/null
+++ b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardBottomSheet.java
@@ -0,0 +1,251 @@
+package com.mifos.apache.fineract.ui.online.identification.uploadidentificationscan;
+
+import static android.app.Activity.RESULT_OK;
+
+import android.Manifest;
+import android.annotation.TargetApi;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.MediaStore;
+import android.support.annotation.NonNull;
+import android.support.design.widget.BottomSheetBehavior;
+import android.support.design.widget.BottomSheetDialog;
+import android.support.design.widget.TextInputLayout;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.EditText;
+
+import com.mifos.apache.fineract.R;
+import com.mifos.apache.fineract.ui.base.MifosBaseActivity;
+import com.mifos.apache.fineract.ui.base.MifosBaseBottomSheetDialogFragment;
+import com.mifos.apache.fineract.ui.base.Toaster;
+import com.mifos.apache.fineract.utils.CheckSelfPermissionAndRequest;
+import com.mifos.apache.fineract.utils.ConstantKeys;
+import com.mifos.apache.fineract.utils.ValidateIdentifierUtil;
+import com.mifos.apache.fineract.utils.ValidationUtil;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+
+/**
+ * @author Rajan Maurya
+ * On 01/08/17.
+ */
+public class UploadIdentificationCardBottomSheet extends MifosBaseBottomSheetDialogFragment
+ implements UploadIdentificationCardContract.View, TextWatcher {
+
+ public static final int REQUEST_IMAGE_CAPTURE = 1;
+
+ @BindView(R.id.et_identifier)
+ EditText etIdentifier;
+
+ @BindView(R.id.et_description)
+ EditText etDescription;
+
+ @BindView(R.id.et_selected_file)
+ EditText etSelectFile;
+
+ @BindView(R.id.til_identifier)
+ TextInputLayout tilIdentifier;
+
+ @BindView(R.id.til_description)
+ TextInputLayout tilDescription;
+
+ @BindView(R.id.til_selected_file)
+ TextInputLayout tilSelectedFile;
+
+ View rootView;
+
+ private BottomSheetBehavior behavior;
+ private File capturedImage;
+
+ @NonNull
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ BottomSheetDialog dialog = (BottomSheetDialog) super.onCreateDialog(savedInstanceState);
+ rootView = View.inflate(getContext(),
+ R.layout.bottom_sheet_upload_identification_scan_card, null);
+ dialog.setContentView(rootView);
+ behavior = BottomSheetBehavior.from((View) rootView.getParent());
+ ButterKnife.bind(this, rootView);
+
+ showUserInterface();
+
+ return dialog;
+ }
+
+ @Override
+ public void showUserInterface() {
+ etIdentifier.addTextChangedListener(this);
+ etSelectFile.addTextChangedListener(this);
+ etDescription.addTextChangedListener(this);
+ }
+
+ @OnClick(R.id.btn_upload_identification_card_scan)
+ public void onUploadIdentificationCard() {
+ if (validateIdentifier() && validateDescription() && validateSelectFile()) {
+
+ }
+ }
+
+ @OnClick(R.id.btn_cancel)
+ void onCancel() {
+ dismiss();
+ }
+
+ @OnClick(R.id.btn_browse_document)
+ void browseDocument() {
+ checkCameraPermission();
+ }
+
+ @Override
+ public void checkCameraPermission() {
+ if (CheckSelfPermissionAndRequest.checkSelfPermission(getActivity(),
+ Manifest.permission.CAMERA)) {
+ openCamera();
+ } else {
+ requestPermission();
+ }
+ }
+
+ @Override
+ public void openCamera() {
+ Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
+ startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
+ Bundle extras = data.getExtras();
+ Bitmap imageBitmap = (Bitmap) extras.get("data");
+ etSelectFile.setText(getString(R.string.scan_file));
+
+ // CALL THIS METHOD TO GET THE URI FROM THE BITMAP
+ Uri tempUri = getImageUri(getActivity(), imageBitmap);
+
+ // CALL THIS METHOD TO GET THE ACTUAL PATH
+ capturedImage = new File(getRealPathFromURI(tempUri));
+ }
+ }
+
+ public Uri getImageUri(Context inContext, Bitmap inImage) {
+ ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+ inImage.compress(Bitmap.CompressFormat.JPEG, 100, bytes);
+ String path = MediaStore.Images.Media.insertImage(inContext.getContentResolver(), inImage,
+ "Title", null);
+ return Uri.parse(path);
+ }
+
+ public String getRealPathFromURI(Uri uri) {
+ Cursor cursor = getActivity().getContentResolver().query(uri, null, null, null, null);
+ cursor.moveToFirst();
+ int idx = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
+ return cursor.getString(idx);
+ }
+
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ @Override
+ public void requestPermission() {
+ CheckSelfPermissionAndRequest.requestPermission(
+ (MifosBaseActivity) getActivity(),
+ Manifest.permission.CAMERA,
+ ConstantKeys.PERMISSIONS_REQUEST_CAMERA,
+ getResources().getString(
+ R.string.dialog_message_camera_permission_denied_prompt),
+ getResources().getString(R.string.dialog_message_camera_permission_never_ask_again),
+ ConstantKeys.PERMISSIONS_CAMERA_STATUS);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
+ @NonNull int[] grantResults) {
+ switch (requestCode) {
+ case ConstantKeys.PERMISSIONS_REQUEST_CAMERA : {
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ openCamera();
+ } else {
+ Toaster.show(rootView, getString(R.string.permission_denied_camera));
+ }
+ }
+ }
+ }
+
+ @Override
+ public void showScanUploadedSuccessfully() {
+
+ }
+
+ @Override
+ public void showProgressDialog() {
+ showMifosProgressDialog("Uploading identification scan card...");
+ }
+
+ @Override
+ public void hideProgressDialog() {
+ hideMifosProgressDialog();
+ }
+
+ @Override
+ public void showError(String message) {
+ Toaster.show(rootView, message);
+ }
+
+ @Override
+ public boolean validateIdentifier() {
+ return ValidateIdentifierUtil.isValid(getActivity(),
+ etIdentifier.getText().toString().trim(), tilIdentifier);
+ }
+
+ @Override
+ public boolean validateDescription() {
+ return ValidationUtil.isEmpty(getActivity(),
+ etDescription.getText().toString().trim(), tilDescription);
+ }
+
+ @Override
+ public boolean validateSelectFile() {
+ return ValidationUtil.isEmpty(getActivity(),
+ etSelectFile.getText().toString().trim(), tilSelectedFile);
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if ((etIdentifier.getText().hashCode() == s.hashCode())) {
+ validateIdentifier();
+ } else if (etDescription.getText().hashCode() == s.hashCode()) {
+ validateDescription();
+ } else if (etSelectFile.getText().hashCode() == s.hashCode()) {
+ validateSelectFile();
+ }
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ hideMifosProgressDialog();
+ }
+}
diff --git a/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardContract.java b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardContract.java
new file mode 100644
index 0000000..a81238f
--- /dev/null
+++ b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardContract.java
@@ -0,0 +1,44 @@
+package com.mifos.apache.fineract.ui.online.identification.uploadidentificationscan;
+
+import android.graphics.Bitmap;
+
+import com.mifos.apache.fineract.ui.base.MvpView;
+
+/**
+ * @author Rajan Maurya
+ * On 01/08/17.
+ */
+public interface UploadIdentificationCardContract {
+
+ interface View extends MvpView {
+
+ void showUserInterface();
+
+ void checkCameraPermission();
+
+ void openCamera();
+
+ void requestPermission();
+
+ void showScanUploadedSuccessfully();
+
+ void showProgressDialog();
+
+ void hideProgressDialog();
+
+ void showError(String message);
+
+ boolean validateIdentifier();
+
+ boolean validateDescription();
+
+ boolean validateSelectFile();
+ }
+
+ interface Presenter {
+
+ void uploadIdentificationCardScan(String customerIdentifier,
+ String identificationNumber, String scanIdentifier, String description,
+ Bitmap bitmap);
+ }
+}
diff --git a/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardPresenter.java b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardPresenter.java
new file mode 100644
index 0000000..83d936b
--- /dev/null
+++ b/app/src/main/java/com/mifos/apache/fineract/ui/online/identification/uploadidentificationscan/UploadIdentificationCardPresenter.java
@@ -0,0 +1,73 @@
+package com.mifos.apache.fineract.ui.online.identification.uploadidentificationscan;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+
+import com.mifos.apache.fineract.data.datamanager.DataManagerCustomer;
+import com.mifos.apache.fineract.injection.ApplicationContext;
+import com.mifos.apache.fineract.injection.ConfigPersistent;
+import com.mifos.apache.fineract.ui.base.BasePresenter;
+
+import javax.inject.Inject;
+
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.observers.DisposableCompletableObserver;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.MultipartBody;
+
+/**
+ * @author Rajan Maurya
+ * On 01/08/17.
+ */
+@ConfigPersistent
+public class UploadIdentificationCardPresenter extends
+ BasePresenter<UploadIdentificationCardContract.View>
+ implements UploadIdentificationCardContract.Presenter {
+
+ private DataManagerCustomer dataManagerCustomer;
+ private final CompositeDisposable compositeDisposable;
+
+ @Inject
+ protected UploadIdentificationCardPresenter(@ApplicationContext Context context,
+ DataManagerCustomer dataManagerCustomer) {
+ super(context);
+ this.dataManagerCustomer = dataManagerCustomer;
+ compositeDisposable = new CompositeDisposable();
+ }
+
+ @Override
+ public void attachView(UploadIdentificationCardContract.View mvpView) {
+ super.attachView(mvpView);
+ }
+
+ @Override
+ public void detachView() {
+ super.detachView();
+ compositeDisposable.clear();
+ }
+
+ @Override
+ public void uploadIdentificationCardScan(String customerIdentifier, String identificationNumber,
+ String scanIdentifier, String description, Bitmap bitmap) {
+ MultipartBody.Part file = null;
+ checkViewAttached();
+ getMvpView().showProgressDialog();
+ compositeDisposable.add(dataManagerCustomer.uploadIdentificationCardScan(
+ customerIdentifier, identificationNumber, scanIdentifier, description, file)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribeWith(new DisposableCompletableObserver() {
+ @Override
+ public void onComplete() {
+
+ }
+
+ @Override
+ public void onError(Throwable e) {
+
+ }
+ })
+ );
+ }
+}
diff --git a/app/src/main/java/com/mifos/apache/fineract/utils/CheckSelfPermissionAndRequest.java b/app/src/main/java/com/mifos/apache/fineract/utils/CheckSelfPermissionAndRequest.java
new file mode 100644
index 0000000..280bdd6
--- /dev/null
+++ b/app/src/main/java/com/mifos/apache/fineract/utils/CheckSelfPermissionAndRequest.java
@@ -0,0 +1,144 @@
+package com.mifos.apache.fineract.utils;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Build;
+import android.provider.Settings;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AppCompatActivity;
+import android.widget.Toast;
+
+import com.mifos.apache.fineract.R;
+import com.mifos.apache.fineract.data.local.PreferencesHelper;
+
+/**
+ * This Class is the CheckSelfPermissionAndRequest Class
+ * Created by Rajan Maurya on 03/08/16.
+ */
+public class CheckSelfPermissionAndRequest {
+
+
+ /**
+ * This Method Check the Permission is granted or not to the App. If the Permission granted,
+ * returns true and If not permission denied then returns false.
+ *
+ * @param context Context
+ * @param permission Manifest.permission...Permission...
+ * @return Boolean True or False.
+ */
+ public static Boolean checkSelfPermission(Context context, String permission) {
+ return ContextCompat.checkSelfPermission(context, permission) ==
+ PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * This Method is requesting to device to grant the permission. When App is trying to
+ * request the device to grant the permission, then their is Three cases.
+ * 1. First case Device Prompt the Permission Dialog to user and user accepted or denied the
+ * Permission.
+ * 2. Second case will come, if user will denied the permission, after onclick dialog denied
+ * button and next time App ask for permission, It will show a Material Dialog and there
+ * will be a message to tell the user that you have denied the permission before, So do
+ * you want to give this permission to app or not, If yes then click on Re-Try dialog button
+ * and if not then click on Dialog button "I'M Sure", to not to give this permission to the
+ * app.
+ * <p/>
+ * And as user will click on "Re-Try" dialog button, he will be prompt with the with
+ * permission dialog with "[-] never ask again" and have two options first one to click on
+ * denied button again and put Un check the never ask check box. In this case, user will
+ * prompt with permission dialog with "[-] never ask again" in the loop, whenever app ask
+ * for that permission.
+ * <p/>
+ * and If user will click on "[_/] never ask again" check box then permission dialog with
+ * that permission will not prompt to the user.
+ * 3. Third case will came. when user have denied to accept permission with never ask again.
+ * then user will prompt with dialog and message that you have denied this permission with
+ * never ask again. but this is necessary permission to this app feature. and to grant
+ * this permission please click on dialog app settings button and give the permission to
+ * work with this feature.
+ *
+ * @param activity AppCompatActivity
+ * @param permission Manifest.permission...Permission...
+ * @param permissionRequestCode Permission Request Code.
+ * @param dialogMessageRetry Dialog Message Retry
+ * @param messageNeverAskAgain Dialog Message Never Ask Again
+ * @param permissionDeniedStatus Permission Denied Status
+ */
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
+ public static void requestPermission(final AppCompatActivity activity,
+ final String permission,
+ final int permissionRequestCode,
+ final String dialogMessageRetry,
+ final String messageNeverAskAgain,
+ final String permissionDeniedStatus) {
+ // Should we show an explanation?
+ if (ActivityCompat.shouldShowRequestPermissionRationale(activity, permission)) {
+
+ // Show an explanation to the user *asynchronously* -- don't block
+ // this thread waiting for the user's response! After the user
+ // sees the explanation, try again to request the permission.
+ new MaterialDialog.Builder().init(activity)
+ .setTitle(R.string.dialog_permission_denied)
+ .setMessage(dialogMessageRetry)
+ .setPositiveButton(R.string.dialog_action_re_try,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ActivityCompat.requestPermissions(activity, new
+ String[]{permission},
+ permissionRequestCode);
+ }
+ })
+ .setNegativeButton(R.string.dialog_action_i_am_sure)
+ .createMaterialDialog()
+ .show();
+ } else {
+
+ //Requesting Permission, first time to the device.
+ PreferencesHelper preferencesHelper = new PreferencesHelper(activity.
+ getApplicationContext());
+ if (preferencesHelper.getBoolean(permissionDeniedStatus, true)) {
+ preferencesHelper.putBoolean(permissionDeniedStatus, false);
+
+ ActivityCompat.requestPermissions(activity, new String[]{permission},
+ permissionRequestCode);
+ } else {
+ //Requesting Permission, more the one time and opening the setting to change
+ // the Permission in App Settings.
+ new MaterialDialog.Builder().init(activity)
+ .setMessage(messageNeverAskAgain)
+ .setNegativeButton(R.string.dialog_action_cancel)
+ .setPositiveButton(R.string.dialog_action_app_settings,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ //Making the Intent to grant the permission
+ Intent intent =
+ new Intent(Settings
+ .ACTION_APPLICATION_DETAILS_SETTINGS);
+ Uri uri = Uri.fromParts(activity.getResources().getString(
+ R.string.package_name), activity.getPackageName()
+ , null);
+ intent.setData(uri);
+ PackageManager pm = activity.getPackageManager();
+ if (intent.resolveActivity(pm) != null) {
+ activity.startActivityForResult(intent,
+ ConstantKeys.REQUEST_PERMISSION_SETTING);
+ } else {
+ Toast.makeText(activity, activity.getString(
+ R.string.msg_setting_activity_not_found)
+ , Toast.LENGTH_LONG).show();
+ }
+ }
+ })
+ .createMaterialDialog()
+ .show();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/mifos/apache/fineract/utils/ConstantKeys.java b/app/src/main/java/com/mifos/apache/fineract/utils/ConstantKeys.java
index feb1939..0503944 100644
--- a/app/src/main/java/com/mifos/apache/fineract/utils/ConstantKeys.java
+++ b/app/src/main/java/com/mifos/apache/fineract/utils/ConstantKeys.java
@@ -12,4 +12,10 @@
public static final String ACCOUNT_IDENTIFIER = "account_identifier";
public static final String LOAN_CREDITWORTHINESSSNAPSHOTS = "loan_creditWorthinessSnapshots";
public static final String IDENTIFICATION_CARD = "identification_card";
+
+ public static final int REQUEST_PERMISSION_SETTING = 254;
+
+ public static final int PERMISSIONS_REQUEST_CAMERA = 1;
+ public static final String PERMISSIONS_CAMERA_STATUS = "camera_status";
+
}
diff --git a/app/src/main/java/com/mifos/apache/fineract/utils/MaterialDialog.java b/app/src/main/java/com/mifos/apache/fineract/utils/MaterialDialog.java
new file mode 100644
index 0000000..9f5a0ec
--- /dev/null
+++ b/app/src/main/java/com/mifos/apache/fineract/utils/MaterialDialog.java
@@ -0,0 +1,166 @@
+package com.mifos.apache.fineract.utils;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.support.annotation.StringRes;
+import android.support.v7.app.AlertDialog;
+
+import com.mifos.apache.fineract.R;
+
+/**
+ * This Class is the Material Dialog Builder Class
+ * Created by Rajan Maurya on 03/08/16.
+ */
+public final class MaterialDialog {
+
+ public static class Builder {
+
+ private AlertDialog.Builder mMaterialDialogBuilder;
+
+ //This is the Default Builder Initialization with Material Style
+ public Builder init(Context context) {
+ mMaterialDialogBuilder =
+ new AlertDialog.Builder(context, R.style.MaterialAlertDialogStyle);
+ return this;
+ }
+
+ //This method set the custom Material Style
+ public Builder init(Context context, int theme) {
+ mMaterialDialogBuilder = new AlertDialog.Builder(context, theme);
+ return this;
+ }
+
+ //This method set the String Title
+ public Builder setTitle(String title) {
+ mMaterialDialogBuilder.setTitle(title);
+ return this;
+ }
+
+ //This Method set the String Resources to Title
+ public Builder setTitle(@StringRes int title) {
+ mMaterialDialogBuilder.setTitle(title);
+ return this;
+ }
+
+ //This Method set the String Message
+ public Builder setMessage(String message) {
+ mMaterialDialogBuilder.setMessage(message);
+ return this;
+ }
+
+ //This Method set the String Resources message
+ public Builder setMessage(@StringRes int message) {
+ mMaterialDialogBuilder.setMessage(message);
+ return this;
+ }
+
+ //This Method set String Test to the Positive Button and set the Onclick null
+ public Builder setPositiveButton(String positiveText) {
+ mMaterialDialogBuilder.setPositiveButton(positiveText, null);
+ return this;
+ }
+
+ //This Method Set the String Resources Text To Positive Button
+ public Builder setPositiveButton(@StringRes int positiveText) {
+ mMaterialDialogBuilder.setPositiveButton(positiveText, null);
+ return this;
+ }
+
+ //This Method set the String Text to Positive Button and set the OnClick Event to it
+ public Builder setPositiveButton(String positiveText,
+ DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setPositiveButton(positiveText, listener);
+ return this;
+ }
+
+ //This method set the String Resources text To Positive button and set the Onclick Event
+ public Builder setPositiveButton(@StringRes int positiveText,
+ DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setPositiveButton(positiveText, listener);
+ return this;
+ }
+
+ //This Method the String Text to Negative Button and Set the onclick event to null
+ public Builder setNegativeButton(String negativeText) {
+ mMaterialDialogBuilder.setNegativeButton(negativeText, null);
+ return this;
+ }
+
+ //This Method set the String Resources Text to Negative button
+ // and set the onclick event to null
+ public Builder setNegativeButton(@StringRes int negativeText) {
+ mMaterialDialogBuilder.setNegativeButton(negativeText, null);
+ return this;
+ }
+
+ //This Method set String Text to Negative Button and
+ //Set the Onclick event
+ public Builder setNegativeButton(String negativeText,
+ DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setNegativeButton(negativeText, listener);
+ return this;
+ }
+
+ //This method set String Resources Text to Negative Button and set Onclick Event
+ public Builder setNegativeButton(@StringRes int negativeText,
+ DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setNegativeButton(negativeText, listener);
+ return this;
+ }
+
+ //This Method the String Text to Neutral Button and Set the onclick event to null
+ public Builder setNeutralButton(String neutralText) {
+ mMaterialDialogBuilder.setNeutralButton(neutralText, null);
+ return this;
+ }
+
+ //This Method set the String Resources Text to Neutral button
+ // and set the onclick event to null
+ public Builder setNeutralButton(@StringRes int neutralText) {
+ mMaterialDialogBuilder.setNeutralButton(neutralText, null);
+ return this;
+ }
+
+ //This Method set String Text to Neutral Button and
+ //Set the Onclick event
+ public Builder setNeutralButton(String neutralText,
+ DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setNeutralButton(neutralText, listener);
+ return this;
+ }
+
+ //This method set String Resources Text to Neutral Button and set Onclick Event
+ public Builder setNeutralButton(@StringRes int neutralText,
+ DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setNeutralButton(neutralText, listener);
+ return this;
+ }
+
+ public Builder setCancelable(Boolean cancelable) {
+ mMaterialDialogBuilder.setCancelable(cancelable);
+ return this;
+ }
+
+ public Builder setItems(int items, DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setItems(items, listener);
+ return this;
+ }
+
+ public Builder setItems(CharSequence[] items, DialogInterface.OnClickListener listener) {
+ mMaterialDialogBuilder.setItems(items, listener);
+ return this;
+ }
+
+ //This Method Create the Final Material Dialog
+ public Builder createMaterialDialog() {
+ mMaterialDialogBuilder.create();
+ return this;
+ }
+
+ //This Method Show the Dialog
+ public Builder show() {
+ mMaterialDialogBuilder.show();
+ return this;
+ }
+ }
+}
diff --git a/app/src/main/java/com/mifos/apache/fineract/utils/ValidateIdentifierUtil.java b/app/src/main/java/com/mifos/apache/fineract/utils/ValidateIdentifierUtil.java
new file mode 100644
index 0000000..11e95f8
--- /dev/null
+++ b/app/src/main/java/com/mifos/apache/fineract/utils/ValidateIdentifierUtil.java
@@ -0,0 +1,65 @@
+package com.mifos.apache.fineract.utils;
+
+import android.content.Context;
+import android.support.design.widget.TextInputLayout;
+import android.text.TextUtils;
+
+import com.mifos.apache.fineract.R;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+/**
+ * @author Rajan Maurya
+ * On 02/08/17.
+ */
+public class ValidateIdentifierUtil {
+
+ public static boolean isValid(Context context, String string, TextInputLayout textInputLayout) {
+ if (TextUtils.isEmpty(string)) {
+ showTextInputLayoutError(textInputLayout, context.getString(R.string.required));
+ return false;
+ }
+ return validate(context, string, textInputLayout);
+ }
+
+ private static boolean validate(Context context, String string,
+ TextInputLayout textInputLayout) {
+ if (string.length() < 3) {
+ showTextInputLayoutError(textInputLayout,
+ context.getString(R.string.must_be_at_least_three_characters, 3));
+ return false;
+ }
+
+ if (string.length() > 32) {
+ showTextInputLayoutError(textInputLayout,
+ context.getString(R.string.only_thirty_two_character_allowed));
+ return false;
+ }
+
+ try {
+ if (encode(string).equals(string)) {
+ showTextInputLayoutError(textInputLayout, null);
+ return true;
+ } else {
+ showTextInputLayoutError(textInputLayout, context.getString(
+ R.string.only_alphabetic_decimal_digits_characters_allowed));
+ return false; //If we can't encode with UTF-8, then there are no valid names.
+ }
+ } catch (UnsupportedEncodingException e) {
+ showTextInputLayoutError(textInputLayout,
+ context.getString(R.string.only_alphabetic_decimal_digits_characters_allowed));
+ return false; //If we can't encode with UTF-8, then there are no valid names.
+ }
+ }
+
+ private static String encode(String identifier) throws UnsupportedEncodingException {
+ return URLEncoder.encode(identifier, "UTF-8");
+ }
+
+ public static void showTextInputLayoutError(TextInputLayout textInputLayout,
+ String errorMessage) {
+ textInputLayout.setErrorEnabled(true);
+ textInputLayout.setError(errorMessage);
+ }
+}
diff --git a/app/src/main/java/com/mifos/apache/fineract/utils/ValidationUtil.java b/app/src/main/java/com/mifos/apache/fineract/utils/ValidationUtil.java
index 97fbdaa..eba04ad 100644
--- a/app/src/main/java/com/mifos/apache/fineract/utils/ValidationUtil.java
+++ b/app/src/main/java/com/mifos/apache/fineract/utils/ValidationUtil.java
@@ -1,5 +1,11 @@
package com.mifos.apache.fineract.utils;
+import android.content.Context;
+import android.support.design.widget.TextInputLayout;
+import android.text.TextUtils;
+
+import com.mifos.apache.fineract.R;
+
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@@ -33,4 +39,14 @@
}
return true;
}
+
+ public static boolean isEmpty(Context context, String string, TextInputLayout inputLayout) {
+ if (TextUtils.isEmpty(string)) {
+ ValidateIdentifierUtil.showTextInputLayoutError(inputLayout,
+ context.getString(R.string.required));
+ return false;
+ }
+ ValidateIdentifierUtil.showTextInputLayoutError(inputLayout, null);
+ return true;
+ }
}
diff --git a/app/src/main/res/drawable/ic_cancel_black_24dp.xml b/app/src/main/res/drawable/ic_cancel_black_24dp.xml
new file mode 100644
index 0000000..7d2b57e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cancel_black_24dp.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
+</vector>
diff --git a/app/src/main/res/layout/bottom_sheet_upload_identification_scan_card.xml b/app/src/main/res/layout/bottom_sheet_upload_identification_scan_card.xml
new file mode 100644
index 0000000..0efb1bf
--- /dev/null
+++ b/app/src/main/res/layout/bottom_sheet_upload_identification_scan_card.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<android.support.design.widget.CoordinatorLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+
+ <android.support.v4.widget.NestedScrollView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:visibility="visible"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior">
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="@dimen/layout_padding_24dp">
+
+ <LinearLayout
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/Base.TextAppearance.AppCompat.Large"
+ android:id="@+id/tv_header"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:paddingBottom="@dimen/layout_padding_30dp"
+ android:paddingLeft="@dimen/layout_padding_24dp"
+ android:paddingRight="@dimen/layout_padding_24dp"
+ android:paddingStart="@dimen/layout_padding_24dp"
+ android:paddingTop="@dimen/layout_padding_24dp"
+ android:text="@string/upload_new_identification_card_scan"
+ android:textColor="@color/colorPrimaryDark"
+ android:textStyle="bold"/>
+
+ <android.support.design.widget.TextInputLayout
+ android:id="@+id/til_identifier"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:paddingLeft="@dimen/layout_padding_24dp"
+ android:paddingRight="@dimen/layout_padding_24dp">
+
+ <EditText
+ android:hint="@string/identifier"
+ android:id="@+id/et_identifier"
+ android:inputType="text"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"/>
+ </android.support.design.widget.TextInputLayout>
+
+ <android.support.design.widget.TextInputLayout
+ android:id="@+id/til_description"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:paddingLeft="@dimen/layout_padding_24dp"
+ android:paddingRight="@dimen/layout_padding_24dp"
+ android:paddingTop="@dimen/layout_padding_16dp">
+
+ <EditText
+ android:hint="@string/description"
+ android:id="@+id/et_description"
+ android:inputType="textMultiLine"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scrollHorizontally="false"
+ android:scrollbars="vertical"/>
+ </android.support.design.widget.TextInputLayout>
+
+
+ <LinearLayout
+ android:gravity="center"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:paddingLeft="@dimen/layout_padding_24dp"
+ android:paddingRight="@dimen/layout_padding_24dp"
+ android:paddingTop="@dimen/layout_padding_16dp"
+ android:weightSum="1">
+
+ <android.support.design.widget.TextInputLayout
+ android:id="@+id/til_selected_file"
+ android:gravity="center"
+ android:layout_height="wrap_content"
+ android:layout_weight=".7"
+ android:layout_width="wrap_content"
+ android:paddingEnd="@dimen/layout_padding_8dp"
+ android:paddingLeft="0dp"
+ android:paddingRight="@dimen/layout_padding_8dp"
+ android:paddingStart="0dp">
+ <EditText
+ android:hint="@string/selected_file"
+ android:id="@+id/et_selected_file"
+ android:inputType="text"
+ android:lines="1"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:scrollHorizontally="false"
+ android:enabled="false"
+ android:scrollbars="vertical"/>
+ </android.support.design.widget.TextInputLayout>
+
+ <Button
+ android:drawableLeft="@drawable/ic_folder_black_24dp"
+ android:drawableStart="@drawable/ic_folder_black_24dp"
+ android:id="@+id/btn_browse_document"
+ android:layout_height="wrap_content"
+ android:layout_weight=".3"
+ android:layout_width="wrap_content"
+ android:text="@string/browse"
+ android:textAllCaps="true"/>
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_gravity="end"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent"
+ android:orientation="horizontal"
+ android:paddingEnd="@dimen/layout_padding_24dp"
+ android:paddingLeft="@dimen/layout_padding_64dp"
+ android:paddingRight="@dimen/layout_padding_24dp"
+ android:paddingStart="@dimen/layout_padding_64dp"
+ android:paddingTop="@dimen/layout_padding_24dp"
+ android:weightSum="1">
+
+ <Button
+ android:id="@+id/btn_cancel"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/layout_padding_16dp"
+ android:layout_marginRight="@dimen/layout_padding_16dp"
+ android:layout_weight=".5"
+ android:layout_width="wrap_content"
+ android:text="@string/cancel"
+ android:textAllCaps="false"/>
+
+ <Button
+ android:id="@+id/btn_upload_identification_card_scan"
+ android:layout_height="wrap_content"
+ android:layout_weight=".5"
+ android:layout_width="wrap_content"
+ android:text="@string/upload"
+ android:textAllCaps="false"
+ />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ </android.support.v4.widget.NestedScrollView>
+
+</android.support.design.widget.CoordinatorLayout>
+
+
+
diff --git a/app/src/main/res/layout/fragment_identification_details.xml b/app/src/main/res/layout/fragment_identification_details.xml
index 1244acf..b9ea89e 100644
--- a/app/src/main/res/layout/fragment_identification_details.xml
+++ b/app/src/main/res/layout/fragment_identification_details.xml
@@ -235,7 +235,7 @@
<android.support.design.widget.FloatingActionButton
android:clickable="true"
- android:id="@+id/fab_edit_customer_loan"
+ android:id="@+id/fab_upload_identification_scan_card"
android:layout_gravity="bottom|end"
android:layout_height="wrap_content"
android:layout_margin="@dimen/layout_padding_16dp"
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index ce1be26..204e34e 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -13,6 +13,7 @@
<dimen name="layout_padding_16dp">16dp</dimen>
<dimen name="layout_padding_24dp">24dp</dimen>
<dimen name="layout_padding_30dp">30dp</dimen>
+ <dimen name="layout_padding_50dp">50dp</dimen>
<dimen name="layout_padding_64dp">64dp</dimen>
<dimen name="layout_padding_75dp">75dp</dimen>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 563c1f7..9682e48 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -82,7 +82,7 @@
<string name="edit_income">Edit Income</string>
<string name="debt_income">Debt Income</string>
<string name="name">Name</string>
- <string name="browse">Browse</string>
+ <string name="browse">Browse…</string>
<string name="fetching_customer_please_wait">Fetching customer please wait…</string>
<string name="creating_loan_please_wait">Creating loan please wait…</string>
<string name="creating_customer_please_wait">Creating customer please wait…</string>
@@ -114,6 +114,12 @@
<string name="expiration_date">Expiration date</string>
<string name="scans_uploaded">Scans uploaded</string>
<string name="loading_scans_please_wait">Loading scans please wait…</string>
+ <string name="identifier">Identifier</string>
+ <string name="selected_file">Selected file</string>
+ <string name="upload_new_identification_card_scan">Upload new Identification card scan</string>
+ <string name="upload">Upload</string>
+ <string name="package_name" translatable="false">package</string>
+ <string name="scan_file">scanFile.png</string>
<!--Edit Text hint required-->
@@ -176,6 +182,25 @@
<string name="error_fetching_scans">Error while fetching scan cards</string>
+ <!--Material Dialog-->
+ <string name="dialog_action_ok">OK</string>
+ <string name="dialog_action_cancel">Cancel</string>
+ <string name="dialog_action_back">Back</string>
+ <string name="dialog_permission_denied">Permission Denied</string>
+ <string name="dialog_action_i_am_sure">I\'M Sure</string>
+ <string name="dialog_action_re_try">Retry</string>
+ <string name="dialog_action_app_settings">App Settings</string>
+ <string name="dialog_message_camera_permission_denied_prompt">Without camera permission you will
+ not be able to scan the document. Are you sure you want to deny this
+ permission?</string>
+ <string name="dialog_message_camera_permission_never_ask_again">You have denied permission to
+ use camera, without this permission you will not be able to scan the document.
+ Please enable it in settings</string>
+ <string name="msg_setting_activity_not_found">Something went wrong finding Settings activity.
+ \nGo to \'Settings\' and grant permission manually.
+ </string>
+ <string name="permission_denied_camera">Permission Denied to use Camera</string>
+
<!--Customer Current status-->
<string name="active">ACTIVE</string>
<string name="pending">PENDING</string>
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index f8175c3..56b8f21 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -34,4 +34,10 @@
<item name="android:padding">@dimen/layout_padding_7dp</item>
</style>
+ <style name="MaterialAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert">
+ <item name="colorAccent">@color/colorPrimary</item>
+ <item name="android:textColorPrimary">@color/black</item>
+ <item name="android:background">@color/white</item>
+ </style>
+
</resources>