From 522a5c336363d4de2dd158d98aa175c21bb3fdbf Mon Sep 17 00:00:00 2001 From: malinowskikam Date: Fri, 20 May 2022 10:29:24 +0200 Subject: [PATCH 1/3] OTC-768 Removed MANAGE_EXTERNAL_STORAGE permission --- app/build.gradle | 2 +- .../org/openimis/imispolicies/tools/Log.java | 8 +- app/src/main/AndroidManifest.xml | 4 +- app/src/main/assets/pages/Sync.js | 6 +- .../org/openimis/imispolicies/Acquire.java | 98 ++- .../imispolicies/ClientAndroidInterface.java | 694 ++++++------------ .../org/openimis/imispolicies/Compressor.java | 69 -- .../imispolicies/ControlNumberService.java | 5 +- .../CustomOnItemSelectedListener.java | 8 +- .../org/openimis/imispolicies/Enquire.java | 7 +- .../org/openimis/imispolicies/Feedback.java | 112 ++- .../openimis/imispolicies/FeedbackList.java | 48 +- .../org/openimis/imispolicies/Global.java | 76 +- .../openimis/imispolicies/MainActivity.java | 131 ++-- .../imispolicies/OutputStreamImageTarget.java | 16 +- .../org/openimis/imispolicies/RenewList.java | 97 ++- .../org/openimis/imispolicies/Renewal.java | 22 +- .../org/openimis/imispolicies/SQLHandler.java | 2 +- .../imispolicies/tools/ImageManager.java | 5 +- .../imispolicies/tools/StorageManager.java | 117 --- .../org/openimis/imispolicies/tools/Util.java | 371 ---------- .../imispolicies/util/AndroidUtils.java | 52 ++ .../openimis/imispolicies/util/FileUtils.java | 204 +++++ .../openimis/imispolicies/util/JsonUtils.java | 33 + .../imispolicies/util/StreamUtils.java | 60 ++ .../imispolicies/util/StringUtils.java | 31 + .../openimis/imispolicies/util/UriUtils.java | 127 ++++ .../Compressor.java => util/ZipUtils.java} | 51 +- app/src/main/res/xml/paths.xml | 1 + build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 31 files changed, 1047 insertions(+), 1414 deletions(-) delete mode 100644 app/src/main/java/org/openimis/imispolicies/Compressor.java delete mode 100644 app/src/main/java/org/openimis/imispolicies/tools/Util.java create mode 100644 app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java create mode 100644 app/src/main/java/org/openimis/imispolicies/util/FileUtils.java create mode 100644 app/src/main/java/org/openimis/imispolicies/util/JsonUtils.java create mode 100644 app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java create mode 100644 app/src/main/java/org/openimis/imispolicies/util/StringUtils.java create mode 100644 app/src/main/java/org/openimis/imispolicies/util/UriUtils.java rename app/src/main/java/org/openimis/imispolicies/{tools/Compressor.java => util/ZipUtils.java} (56%) diff --git a/app/build.gradle b/app/build.gradle index ce40f92d..a04c7082 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -34,7 +34,7 @@ android { } defaultConfig { applicationId "org.openimis.imispolicies" - minSdkVersion 19 + minSdkVersion 21 targetSdkVersion 30 versionCode gitVersionCode versionName gitVersionName diff --git a/app/src/debug/java/org/openimis/imispolicies/tools/Log.java b/app/src/debug/java/org/openimis/imispolicies/tools/Log.java index 61222eb9..48dc4293 100644 --- a/app/src/debug/java/org/openimis/imispolicies/tools/Log.java +++ b/app/src/debug/java/org/openimis/imispolicies/tools/Log.java @@ -1,11 +1,13 @@ package org.openimis.imispolicies.tools; +import android.content.Context; import android.net.Uri; import android.support.v4.content.FileProvider; import org.openimis.imispolicies.AppInformation; import org.openimis.imispolicies.BuildConfig; import org.openimis.imispolicies.Global; +import org.openimis.imispolicies.util.ZipUtils; import java.io.File; import java.io.FileOutputStream; @@ -88,21 +90,21 @@ private static void log(String tag, String msg, int level) { } } - public static void zipLogFiles() { + public static void zipLogFiles(Context context) { File cacheDir = Global.getContext().getExternalCacheDir(); File[] logFiles = cacheDir.listFiles((dir, filename) -> filename.startsWith(logFilePrefix)); File targetFile = new File(cacheDir, logExportFileName); if (logFiles != null) { ArrayList filesToZip = new ArrayList<>(Arrays.asList(logFiles)); - Compressor.zip(filesToZip, targetFile, ""); + ZipUtils.zipFiles(filesToZip, targetFile, ""); } Uri logExportUri = FileProvider.getUriForFile(Global.getContext(), String.format("%s.fileprovider", BuildConfig.APPLICATION_ID), targetFile); - Global.getGlobal().sendFile(logExportUri, "application/zip"); + Global.getGlobal().sendFile(context, logExportUri, "application/octet-stream"); } public static void deleteLogFiles() { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c82445e7..689251bf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,6 @@ - + { - try { - Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); - intent.putExtra(MediaStore.EXTRA_OUTPUT, tempPhotoUri); - global.grantUriPermissions(this, tempPhotoUri, intent, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); - startActivityForResult(intent, TAKE_PHOTO_REQUEST_CODE); - } catch (ActivityNotFoundException e) { - Log.e(LOG_TAG, "Image capture activity not found", e); + if (!etCHFID.getText().toString().isEmpty()) { + try { + Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); + intent.putExtra(MediaStore.EXTRA_OUTPUT, tempPhotoUri); + global.grantUriPermissions(this, tempPhotoUri, intent, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION); + startActivityForResult(intent, TAKE_PHOTO_REQUEST_CODE); + } catch (ActivityNotFoundException e) { + Log.e(LOG_TAG, "Image capture activity not found", e); + } + } else { + Toast.makeText(this, R.string.MissingCHFID, Toast.LENGTH_LONG).show(); } }); @@ -256,15 +250,6 @@ public void afterTextChanged(Editable InsNo) { } runOnUiThread(() -> { - switch (result) { - case 1: - msg = getResources().getString(R.string.PhotoSaved); - break; - default: - msg = getResources().getString(R.string.CouldNotUpload); - break; - } - Toast.makeText(Acquire.this, getResources().getString(R.string.PhotoSaved), Toast.LENGTH_LONG).show(); etCHFID.setText(""); @@ -280,10 +265,7 @@ public void afterTextChanged(Editable InsNo) { @Override protected void onDestroy() { super.onDestroy(); - - if (!tempPhotoFile.delete()) { - Log.w(LOG_TAG, "Temp photo file deletion failed"); - } + FileUtils.removeTempFile(this, TEMP_PHOTO_PATH); } @Override @@ -331,6 +313,8 @@ private int SubmitData() throws IOException, UserException { String date = AppInformation.DateTimeInfo.getDefaultFileDatetimeFormatter().format(new Date()); String fName = etCHFID.getText() + "_" + global.getOfficerCode() + "_" + date + "_" + Latitude + "_" + Longitude + ".jpg"; + File[] oldInsureeImages = ImageManager.of(this).getInsureeImages(etCHFID.getText().toString()); + File file = new File(global.getSubdirectory("Images"), fName); if (file.exists()) { Log.w(LOG_TAG, String.format("File already exists: %s", file.getAbsolutePath())); @@ -342,13 +326,19 @@ private int SubmitData() throws IOException, UserException { if (file.length() == 0L) { Log.w(LOG_TAG, "Compressing photo failed, the resulting file has no content"); + if (!file.delete()) { + Log.w(LOG_TAG, "Deleting empty output file failed"); + } + return 0; + } else { + FileUtils.deleteFiles(oldInsureeImages); } ContentValues contentValues = new ContentValues(); contentValues.put("PhotoPath", file.getAbsolutePath()); String[] whereArgs = {etCHFID.getText().toString()}; - if(sqlHandler.updateData("tblInsuree", contentValues, "CHFID = ?", whereArgs, false) == 0) { + if (sqlHandler.updateData("tblInsuree", contentValues, "CHFID = ?", whereArgs, false) == 0) { Log.w(LOG_TAG, String.format("Cannot update photo path. No insuree for CHFID: %s", etCHFID.getText().toString())); } diff --git a/app/src/main/java/org/openimis/imispolicies/ClientAndroidInterface.java b/app/src/main/java/org/openimis/imispolicies/ClientAndroidInterface.java index 8a550525..3e2901a7 100644 --- a/app/src/main/java/org/openimis/imispolicies/ClientAndroidInterface.java +++ b/app/src/main/java/org/openimis/imispolicies/ClientAndroidInterface.java @@ -25,7 +25,6 @@ package org.openimis.imispolicies; -import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; @@ -46,16 +45,17 @@ import android.os.Parcelable; import android.provider.MediaStore; import android.support.annotation.RequiresApi; -import android.support.v4.content.FileProvider; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.util.Base64; +import org.openimis.imispolicies.util.UriUtils; +import org.openimis.imispolicies.util.ZipUtils; +import org.openimis.imispolicies.tools.ImageManager; import org.openimis.imispolicies.tools.Log; -import android.util.Xml; import android.view.LayoutInflater; import android.view.View; import android.webkit.JavascriptInterface; @@ -77,7 +77,6 @@ import java.util.Arrays; import java.util.Date; -import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.util.EntityUtils; @@ -108,10 +107,11 @@ import java.util.Set; import java.util.regex.Pattern; -import org.openimis.imispolicies.tools.Util; -import org.openimis.imispolicies.tools.Util.AndroidUtil; -import org.openimis.imispolicies.tools.Util.JsonUtil; -import org.xmlpull.v1.XmlSerializer; +import org.openimis.imispolicies.tools.StorageManager; +import org.openimis.imispolicies.util.AndroidUtils; +import org.openimis.imispolicies.util.FileUtils; +import org.openimis.imispolicies.util.JsonUtils; +import org.openimis.imispolicies.util.StringUtils; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; @@ -119,12 +119,9 @@ import static android.database.sqlite.SQLiteDatabase.openOrCreateDatabase; import static android.provider.MediaStore.EXTRA_OUTPUT; -import static org.openimis.imispolicies.tools.Util.JsonUtil.isStringEmpty; -import static org.openimis.imispolicies.tools.Util.StringUtil.isEmpty; public class ClientAndroidInterface { private static final String LOG_TAG_RENEWAL = "RENEWAL"; - private static final String LOG_TAG_SETTINGS = "SETTINGS"; private Context mContext; @@ -134,23 +131,16 @@ public class ClientAndroidInterface { private HashMap controls = new HashMap<>(); private String Path; private File[] files; - private File[] JSONfiles; - private int TotalFiles; - private int UploadCounter; // - private File XMLFile; private int result; private int UserId; - private Date edate; public static boolean Activate = false; private int IsFamilyAvailable; - private String payerId; private int DataDeleted; private int rtPolicyId = 0; private int rtPremiumId = 0; private int rtInsureeId = 0; private int rtEnrolledId = 0; private int enrol_result; - private Bitmap myBitmap; private String resu; private String fname = null; private Bitmap theImage; @@ -161,15 +151,12 @@ public class ClientAndroidInterface { EnrollmentReport enrollmentReport; - StringBuffer buffer = new StringBuffer(); - private ArrayList mylist = new ArrayList<>(); - private String salt; Picasso picassoInstance; Target imageTarget; - private File tempPhotoFile; + StorageManager storageManager; ClientAndroidInterface(Context c) { mContext = c; @@ -177,8 +164,9 @@ public class ClientAndroidInterface { sqlHandler = new SQLHandler(c); getControls(); SQLiteDatabase database = sqlHandler.getReadableDatabase(); - Path = global.getMainDirectory(); + Path = global.getAppDirectory(); filePath = database.getPath(); + storageManager = StorageManager.of(mContext); database.close(); // activity = (Activity) c.getApplicationContext(); picassoInstance = new Picasso.Builder(mContext) @@ -194,7 +182,6 @@ public void SetUrl(String Url) { } private void getControls() { - String tableName = "tblControls"; String[] columns = {"FieldName", "Adjustibility"}; String where = null; @@ -212,10 +199,7 @@ private void getControls() { } catch (JSONException e) { e.printStackTrace(); } - - } - } public String getSpecificControl(String FieldName) { @@ -281,12 +265,12 @@ public String getControl(String ctl) { @JavascriptInterface public void ShowToast(String msg) { - AndroidUtil.showToast(mContext, msg); + AndroidUtils.showToast(mContext, msg); } @JavascriptInterface public AlertDialog ShowDialog(String msg) { - return AndroidUtil.showDialog(mContext, msg); + return AndroidUtils.showDialog(mContext, msg); } @JavascriptInterface @@ -896,7 +880,11 @@ private JSONObject getFamilySMS(String familyId) { String query = "SELECT * FROM tblFamilySMS where FamilyId = ? LIMIT 1;"; String[] queryArgs = {familyId}; try { - JSONObject result = sqlHandler.getResult(query, queryArgs).getJSONObject(0); + JSONObject result = null; + JSONArray array = sqlHandler.getResult(query, queryArgs); + if (array.length() > 0) { + result = array.getJSONObject(0); + } return result; } catch (JSONException e) { e.printStackTrace(); @@ -1180,15 +1168,18 @@ private String copyImageFromGalleryToApplication(String selectedPath, String Ins Calendar cal = Calendar.getInstance(); String d = format.format(cal.getTime()); - String outputFileName = global.getImageFolder() + InsuranceNumber + "_" + global.getOfficerCode() + "_" + d + "_0_0.jpg"; - File outputFile = new File(outputFileName); + File[] oldFiles = ImageManager.of(mContext).getInsureeImages(InsuranceNumber); + Runnable deleteOldFiles = () -> FileUtils.deleteFiles(oldFiles); + + String outputFileName = InsuranceNumber + "_" + global.getOfficerCode() + "_" + d + "_0_0.jpg"; + File outputFile = new File(global.getImageFolder(), outputFileName); if (!outputFile.createNewFile()) { Log.e("IMAGES", "Creating image copy failed"); } FileOutputStream outputStream = new FileOutputStream(outputFile); - imageTarget = new OutputStreamImageTarget(outputStream, global.getIntKey("image_jpeg_quality", 40)); + imageTarget = new OutputStreamImageTarget(outputStream, global.getIntKey("image_jpeg_quality", 40), deleteOldFiles); try { ((Activity) mContext).runOnUiThread(() -> picassoInstance.load(selectedPath) .resize(global.getIntKey("image_width_limit", 400), @@ -1213,19 +1204,12 @@ public AlertDialog deleteDialog(String msg, final int FamilyId) { .setMessage(msg) .setIcon(R.drawable.ic_about) .setCancelable(false) - .setPositiveButton(R.string.Yes, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialogInterface, int i) { - if (DeleteFamily(FamilyId) == 1) { - ShowDialog(mContext.getResources().getString(R.string.FamilyDeleted)); - } + .setPositiveButton(R.string.Yes, (dialogInterface, i) -> { + if (DeleteFamily(FamilyId) == 1) { + ShowDialog(mContext.getResources().getString(R.string.FamilyDeleted)); } }) - .setNegativeButton(R.string.No, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - //some code when you click No - } + .setNegativeButton(R.string.No, (dialog, which) -> { }).show(); } @@ -1461,30 +1445,12 @@ else if (dateWithGracePeriod4 != null && (dEnrollDate.compareTo(dateWithGracePer public static String InsuranceNo; public static boolean inProgress = true; - - private void generateTempUri(String tempFileName) { - tempPhotoFile = new File(Path, tempFileName); - try { - if (tempPhotoFile.delete()) { - Log.i("selectPicture", "Leftover temp image deleted"); - } - if (!tempPhotoFile.createNewFile()) { - Log.w("selectPicture", "Temp photo file already exists"); - } - tempPhotoUri = FileProvider.getUriForFile(mContext, - String.format("%s.fileprovider", BuildConfig.APPLICATION_ID), - tempPhotoFile); - } catch (IOException e) { - Log.e("selectPicture", "Temp photo file creation failed", e); - Toast.makeText(mContext, "Temp photo file creation failed", Toast.LENGTH_LONG).show(); - } - } - @JavascriptInterface public void selectPicture() { Path = global.getSubdirectory("Images") + "/"; - generateTempUri("selectPictureTemp.jpg"); + File tempPhotoFile = FileUtils.createTempFile(mContext, "images/selectPictureTemp.jpeg"); + tempPhotoUri = UriUtils.createUriForFile(mContext, tempPhotoFile); if (tempPhotoUri == null) return; Intent galleryIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); @@ -1710,49 +1676,6 @@ public double getPolicyValue(String enrollDate, int ProductId, int FamilyId, Str return PolicyValue; } -/* String imagefile ="/sdcard/DCIM/100ANDRO/DSC_0530.jpg"; - Bitmap bm = ShrinkBitmap(imagefile, 300, 300); - - //this method compresses the image and saves into a location in sdcard - Bitmap ShrinkBitmap(String file, int width, int height){ - - BitmapFactory.Options bmpFactoryOptions = new BitmapFactory.Options(); - bmpFactoryOptions.inJustDecodeBounds = true; - Bitmap bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions); - - int heightRatio = (int)Math.ceil(bmpFactoryOptions.outHeight/(float)height); - int widthRatio = (int)Math.ceil(bmpFactoryOptions.outWidth/(float)width); - - if (heightRatio > 1 || widthRatio > 1) - { - if (heightRatio > widthRatio) - { - bmpFactoryOptions.inSampleSize = heightRatio; - } else { - bmpFactoryOptions.inSampleSize = widthRatio; - } - } - - bmpFactoryOptions.inJustDecodeBounds = false; - bitmap = BitmapFactory.decodeFile(file, bmpFactoryOptions); - - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); - byte[] imageInByte = stream.toByteArray(); - //this gives the size of the compressed image in kb - long lengthbmp = imageInByte.length / 1024; - - try { - bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream("/sdcard/mediaAppPhotos/compressed_new.jpg")); - } catch (FileNotFoundException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - - return bitmap; - }*/ - @JavascriptInterface public String getOfficers(int LocationId, String EnrolmentDate) { @@ -2971,75 +2894,67 @@ public void uploadEnrolment() throws Exception { } @JavascriptInterface - public void CreateEnrolmentXML() throws Exception { - - pd = new ProgressDialog(mContext); - pd = ProgressDialog.show(mContext, "", mContext.getResources().getString(R.string.Uploading)); - - try { - new Thread(() -> { - try { - enrol_result = Enrol(0, 0, 0, 0, 2); - if (enrol_result == 0) { - //zipFile(); - zipFiles(); - deleteUnzippedFie(); - deleteUnzippedPhotos(); - } - } catch (UserException e) { - e.printStackTrace(); - } catch (JSONException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (NumberFormatException e) { - e.printStackTrace(); + public void CreateEnrolmentXML() { + new Thread(() -> { + try { + enrol_result = Enrol(0, 0, 0, 0, 2); + if (enrol_result == 0) { + storageManager.requestCreateFile(MainActivity.REQUEST_CREATE_ENROL_EXPORT, + "application/octet-stream", getEnrolmentExportFilename()); } - if (mylist.size() == 0) { - ((Activity) mContext).runOnUiThread(() -> { - if (enrol_result != 999) { - //if error is encountered - if (enrolMessages.size() > 0 && enrolMessages != null) { - CharSequence[] charSequence = enrolMessages.toArray(new CharSequence[(enrolMessages.size())]); - AlertDialog.Builder builder = new AlertDialog.Builder(mContext); - builder.setTitle(mContext.getResources().getString(R.string.UploadFailureReport)); - builder.setCancelable(false); - builder.setItems(charSequence, null); - builder.setPositiveButton(mContext.getResources().getString(R.string.Ok), (dialogInterface, i) -> dialogInterface.dismiss()); - AlertDialog dialog = builder.create(); - dialog.show(); - enrolMessages.clear(); - } else { - //deleteImage(); - ShowDialog(mContext.getResources().getString(R.string.XmlCreated)); - } + } catch (Exception e) { + Log.e("ENROL XML", "Error while creating enrolment xml", e); + } + if (mylist.size() == 0) { + ((Activity) mContext).runOnUiThread(() -> { + if (enrol_result != 999) { + //if error is encountered + if (enrolMessages.size() > 0 && enrolMessages != null) { + CharSequence[] charSequence = enrolMessages.toArray(new CharSequence[(enrolMessages.size())]); + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + builder.setTitle(mContext.getResources().getString(R.string.UploadFailureReport)); + builder.setCancelable(false); + builder.setItems(charSequence, null); + builder.setPositiveButton(mContext.getResources().getString(R.string.Ok), (dialogInterface, i) -> dialogInterface.dismiss()); + AlertDialog dialog = builder.create(); + dialog.show(); + enrolMessages.clear(); } else { - ShowDialog(mContext.getResources().getString(R.string.NoDataAvailable)); + //deleteImage(); + ShowDialog(mContext.getResources().getString(R.string.XmlCreated)); } - //ShowDialog(mContext.getResources().getString(R.string.FamilyUploaded)); - }); - } else { - ((Activity) mContext).runOnUiThread( - () -> ShowDialog(mylist.toString())); - } - pd.dismiss(); - }).start(); - } catch (Exception e) { - e.printStackTrace(); - throw new Exception(e.getMessage()); + } else { + AndroidUtils.showToast(mContext, R.string.NoDataAvailable); + } + //ShowDialog(mContext.getResources().getString(R.string.FamilyUploaded)); + }); + } else { + ((Activity) mContext).runOnUiThread( + () -> ShowDialog(mylist.toString())); + } + }).start(); + } + + @JavascriptInterface + public void CreateRenewalExport() { + if (FileUtils.getFileCount(new File(global.getSubdirectory("Renewal"))) > 0) { + new Thread(() -> storageManager.requestCreateFile(MainActivity.REQUEST_CREATE_RENEWAL_EXPORT, + "application/octet-stream", getRenewalExportFilename())).start(); + } else { + AndroidUtils.showToast(mContext, R.string.NoDataAvailable); } + } - /* private void deleteImage() { - File fdelete = new File(PhotoPath.trim()); - if (fdelete.exists()) { - if (fdelete.delete()) { - System.out.println("file Deleted :" + file_dj_path); - } else { - System.out.println("file not Deleted :" + file_dj_path); - } - } - }*/ + @JavascriptInterface + public void CreateFeedbackExport() { + if (FileUtils.getFileCount(new File(global.getSubdirectory("Feedback"))) > 0) { + new Thread(() -> storageManager.requestCreateFile(MainActivity.REQUEST_CREATE_FEEDBACK_EXPORT, + "application/octet-stream", getFeedbackExportFilename())).start(); + } else { + AndroidUtils.showToast(mContext, R.string.NoDataAvailable); + } + } public boolean isPolicyRequired() { return !getRule("AllowFamilyWithoutPolicy", false); @@ -3673,33 +3588,14 @@ public void updatePolicyRecords(String policy) throws JSONException { } } - private void deleteUnzippedFie() { - /* global = (Global) mContext.getApplicationContext(); - @SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy-HH"); - Calendar cal = Calendar.getInstance(); - String d = format.format(cal.getTime());*/ - - String targetPath = global.getSubdirectory("Family"); - File file = new File(targetPath, fname); - file.delete(); - } - - public void deleteUnzippedPhotos() { - String targetPath = global.getSubdirectory("Photos"); - File folder = new File(targetPath); - for (int i = 0; i < folder.listFiles().length; i++) { - if (folder.listFiles()[i].isFile()) { - folder.listFiles()[i].delete(); - } + public void clearDirectory(String directory) { + File[] files = new File(global.getSubdirectory(directory)).listFiles(); + if (files != null) { + FileUtils.deleteFiles(files); } } - public InsureeImages[] FamilyPictures(JSONArray insurees, int CallerId) throws IOException { - - String Path = global.getSubdirectory("Photos"); - //Here we are creating a directory - InsureeImages[] images = new InsureeImages[insurees.length()]; String PhotoPath = null; @@ -3812,86 +3708,49 @@ public static void copy(String name, byte[] data) throws IOException { out.close(); } - public void xmlWriter(String ImgName, byte[] imgcontent) throws IOException { - File Dir = new File(global.getSubdirectory("EnrolmentXML")); - - //Here we are giving name to the XML file - String FileName = "EnrolmentXml.xml"; - - //Here we are creating file in that directory - File EnrollmentXML = new File(Dir, FileName); - //Here we are creating outputstream - FileOutputStream fos = new FileOutputStream(EnrollmentXML, true); - - - XmlSerializer serializer = Xml.newSerializer(); - - serializer.setOutput(fos, "UTF-8"); - serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); - serializer.startTag(null, "Pictures"); - - serializer.startTag(null, "ImageName"); - serializer.text(ImgName); - serializer.endTag(null, "ImageName"); + public String getEnrolmentExportFilename() { + return getExportFileName("Enrolment"); + } - serializer.startTag(null, "ImageContent"); - serializer.text(String.valueOf(imgcontent)); - serializer.endTag(null, "ImageContent"); + public String getRenewalExportFilename() { + return getExportFileName("Renewal"); + } - serializer.endTag(null, "Pictures"); - serializer.endDocument(); - serializer.flush(); - fos.close(); + public String getFeedbackExportFilename() { + return getExportFileName("Feedback"); } - public void zipFile() { - global = (Global) mContext.getApplicationContext(); - //@SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); - @SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy-HH-mm"); - @SuppressLint("SimpleDateFormat") SimpleDateFormat formatZip = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss"); + public String getExportFileName(String type) { + SimpleDateFormat formatZip = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss", Locale.US); Calendar cal = Calendar.getInstance(); - String d = format.format(cal.getTime()); String dzip = formatZip.format(cal.getTime()); + return type + "_" + global.getOfficerCode() + "_" + dzip + ".rar"; + } - String targetPath = global.getSubdirectory("Enrolment") + File.separator + "Enrolment_" + global.getOfficerCode() + "_" + d + ".xml"; - String zipFilePath = global.getSubdirectory("Enrolment") + File.separator + "Enrolment_" + global.getOfficerCode() + "_" + dzip + ".rar"; - //String unzippedFolderPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/IMIS/Enrolment/Enrolment_"+global.getOfficerCode()+"_"+d+".xml"; + + public File zipEnrolmentFiles() { + File zipFile = FileUtils.createTempFile(mContext, "exports/" + getEnrolmentExportFilename(), true); String password = getRarPwd(); + File imageDir = new File(global.getImageFolder()); + File familyDir = new File(global.getSubdirectory("Family")); - Compressor.zip(targetPath, zipFilePath, password); - //Compressor.unzip(zipFilePath, unzippedFolderPath, password); + return ZipUtils.zipDirectories(zipFile, password, familyDir, imageDir); } - public void zipFiles() { - global = (Global) mContext.getApplicationContext(); - @SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy-HH"); - @SuppressLint("SimpleDateFormat") SimpleDateFormat formatZip = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss"); - Calendar cal = Calendar.getInstance(); - String d = format.format(cal.getTime()); - String dzip = formatZip.format(cal.getTime()); - - String targetPath = global.getSubdirectory("Photos"); - String targetPathFamily = global.getSubdirectory("Family"); - String zipFilePath = global.getSubdirectory("Enrolment") + File.separator + "Enrolment_" + global.getOfficerCode() + "_" + dzip + ".rar"; - //String unzippedFolderPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/IMIS/Photos_"+global.getOfficerCode()+"_"+d+""; + public File zipRenewalFiles() { + File zipFile = FileUtils.createTempFile(mContext, "exports/" + getRenewalExportFilename(), true); String password = getRarPwd(); + File familyDir = new File(global.getSubdirectory("Renewal")); - ArrayList FilesToAdd = new ArrayList(); - File folder = new File(targetPath); - File Family = new File(targetPathFamily); - for (int i = 0; i < folder.listFiles().length; i++) { - if (folder.listFiles()[i].isFile()) { - FilesToAdd.add(new File(folder.listFiles()[i].getPath())); - } - } - for (int i = 0; i < Family.listFiles().length; i++) { - if (Family.listFiles()[i].isFile()) { - FilesToAdd.add(new File(Family.listFiles()[i].getPath())); - } - } + return ZipUtils.zipDirectories(zipFile, password, familyDir); + } + + public File zipFeedbackFiles() { + File zipFile = FileUtils.createTempFile(mContext, "exports/" + getFeedbackExportFilename(), true); + String password = getRarPwd(); + File familyDir = new File(global.getSubdirectory("Feedback")); - Compressor.zip(FilesToAdd, zipFilePath, password); - //Compressor.unzip(zipFilePath, unzippedFolderPath, password); + return ZipUtils.zipDirectories(zipFile, password, familyDir); } public boolean unZipWithPassword(String fileName, String password) { @@ -3900,7 +3759,7 @@ public boolean unZipWithPassword(String fileName, String password) { //String unzippedFolderPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/IMIS/Enrolment/Enrolment_"+global.getOfficerCode()+"_"+d+".xml"; //here we not don't have password set yet so we pass password from Edit Text rar input try { - Compressor.unzip(targetPath, unzippedFolderPath, password); + ZipUtils.unzipPath(targetPath, unzippedFolderPath, password); } catch (Exception e) { return false; } @@ -3914,125 +3773,23 @@ public boolean unZip(String FileName) { String password = getRarPwd(); try { - Compressor.unzip(targetPath, unzippedFolderPath, password); + ZipUtils.unzipPath(targetPath, unzippedFolderPath, password); } catch (Exception e) { return false; } return true; } - public boolean unZipFeedbacksRenewals(String FileName) { - String targetPath = global.getSubdirectory("Database") + File.separator + FileName; - String unzippedFolderPath = global.getSubdirectory("Database"); - + public boolean unZipFeedbacksRenewals(File file) { String password = getRarPwd(); try { - Compressor.unzip(targetPath, unzippedFolderPath, password); + ZipUtils.unzipFile(file, file.getParentFile(), password); } catch (Exception e) { return false; } return true; } - - @JavascriptInterface - public void zipFeedBackRenewal(final String FileType) { - global = (Global) mContext.getApplicationContext(); - //@SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); - @SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("dd-MM-yyyy-HH-mm-ss"); - Calendar cal = Calendar.getInstance(); - String d = format.format(cal.getTime()); - - - String targetPath = global.getMainDirectory(); - String zipFilePath = global.getMainDirectory() + File.separator + "Master" + FileType + ".rar"; - //String unzippedFolderPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS) + "/IMIS/Photos_"+global.getOfficerCode()+"_"+d+""; - - String password = getRarPwd(); - - ArrayList FilesToAdd = new ArrayList(); - File folder = new File(targetPath); - for (int i = 0; i < folder.listFiles().length; i++) { - final int[] status = {0}; - if (folder.listFiles()[i].isFile()) { - String fname = folder.listFiles()[i].getName(); - if (FileType.equals("Feedback")) { - String str = fname.substring(0, 9); - if (str.equals("feedback_")) { - FilesToAdd.add(new File(folder.listFiles()[i].getPath())); - } - } else { - String str = fname.substring(0, 7); - if (str.equals("RenPol_")) { - FilesToAdd.add(new File(folder.listFiles()[i].getPath())); - } - } - } - - } - - - Compressor.zip(FilesToAdd, zipFilePath, password); - if (FilesToAdd.size() > 0) { - ShowDialog(mContext.getResources().getString(R.string.XmlCreated) + " with " + FilesToAdd.size() + " Files"); - } else { - ShowDialog(mContext.getResources().getString(R.string.NoDataAvailable)); - } - - //Compressor.unzip(zipFilePath, unzippedFolderPath, password); - } - -/* @JavascriptInterface - public void UploadImages(final String Filename) { - - files = GetListOfImages(global.getImageFolder(), Filename); - UploadFile uf = new UploadFile(); - if(files.length > 0){ - if (uf.isValidFTPCredentials()) { - for (int i = 0; i < files.length; i++) { - UploadCounter = i + 1; - if (uf.uploadFileToServer(mContext, files[i], "org.openimis.imispolicies")) { - files[i].delete(); - } - } - } - } -*//* new Thread() { - public void run() { - files = GetListOfImages(global.getImageFolder(), Filename); - UploadFile uf = new UploadFile(); - if(files.length > 0){ - if (uf.isValidFTPCredentials()) { - - for (int i = 0; i < files.length; i++) { - UploadCounter = i + 1; - if (uf.uploadFileToServer(mContext, files[i], "org.openimis.imispolicies")) { - files[i].delete(); - } - } - } - } - - } - }.start();*//* - - }*/ - - public void createZipImage() { -/* OutputStream out; - String root = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS)+"/"; - File createDir = new File(root+"Folder Name"+File.separator); - if(!createDir.exists()) { - createDir.mkdir(); - } - File file = new File(root + "Folder Name" + File.separator +"Name of File"); - file.createNewFile(); - out = new FileOutputStream(file); - - out.write(data); - out.close();*/ - } - public String getRarPwd() { global = (Global) mContext.getApplicationContext(); String password = ""; @@ -4245,12 +4002,7 @@ public int isValidLogin(final String Username, final String Password) throws Int cs.setFunctionName("isValidLogin"); UserId = cs.isUserLoggedIn(Username, Password); global.setUserId(UserId); - ((Activity) mContext).runOnUiThread(new Runnable() { - @Override - public void run() { - MainActivity.SetLoggedIn(mContext.getResources().getString(R.string.Login), mContext.getResources().getString(R.string.Logout)); - } - }); + ((Activity) mContext).runOnUiThread(() -> MainActivity.SetLoggedIn(mContext.getResources().getString(R.string.Login), mContext.getResources().getString(R.string.Logout))); return UserId; } @@ -4282,7 +4034,7 @@ public String GetSnapshotIndicators() { private void DeleteUploadedData(final int FamilyId, ArrayList FamilyIDs, int CallerId) { if (FamilyIDs.size() == 0) { - FamilyIDs = new ArrayList() {{ + FamilyIDs = new ArrayList<>() {{ add(String.valueOf(FamilyId)); }}; } @@ -4333,63 +4085,49 @@ private void deleteUploadedTableData(String tableName, String where) { } @JavascriptInterface - public Boolean UploadOfflineFeedbackRenewal(final String ActivityName) { - final File[] files, jsonFiles; - final String functionName, jsonPropertyName, path; + public void uploadFeedbacks() { final int totalFiles; final ProgressDialog pd; - result = ToRestApi.UploadStatus.NO_RESPONSE; + if (!global.isNetworkAvailable()) { + ShowDialog(mContext.getResources().getString(R.string.NoInternet)); + return; + } - path = global.getMainDirectory(); + File feedbackDir = new File(global.getSubdirectory("Feedback")); + File[] xmlFiles = feedbackDir.listFiles((file)->file.getName().endsWith(".xml")); + File[] jsonFiles = feedbackDir.listFiles((file)->file.getName().endsWith(".json")); - if (ActivityName.equals("renewal")) { - files = GetListOfFiles(path, "Renewal", null); - jsonFiles = GetListOfJSONFiles(path, "Renewal", null); - functionName = "policy/renew"; - jsonPropertyName = "Policy"; - } else { - files = GetListOfFiles(Path, "Feedback", null); - jsonFiles = GetListOfJSONFiles(Path, "Feedback", null); - functionName = "feedback"; - jsonPropertyName = "feedback"; + if (xmlFiles == null || jsonFiles == null) { + ShowDialog(mContext.getResources().getString(R.string.NoFiles)); + DeleteFeedBacks(); + return; } - totalFiles = files.length; + totalFiles = jsonFiles.length; if (totalFiles == 0) { ShowDialog(mContext.getResources().getString(R.string.NoDataAvailable)); - //Clean tables in database as well - if (ActivityName.equals("renewal")) { - DeleteRenewals(); - } else { - DeleteFeedBacks(); - } - - return false; - } - - if (!global.isNetworkAvailable()) { - ShowDialog(mContext.getResources().getString(R.string.NoInternet)); - return false; + DeleteFeedBacks(); + return; } - pd = ProgressDialog.show(mContext, mContext.getResources().getString(R.string.Sync), mContext.getResources().getString(R.string.SyncProcessing)); + pd = AndroidUtils.showProgressDialog(mContext, R.string.Sync, R.string.SyncProcessing); new Thread(() -> { ToRestApi rest = new ToRestApi(); JSONObject obj; int uploadsAccepted = 0, uploadsRejected = 0, uploadFailed = 0; for (int i = 0; i < jsonFiles.length; i++) { - String jsonText = global.getFileText(jsonFiles[i].getPath()); + String jsonText = FileUtils.readFileAsUTF8String(jsonFiles[i]); HttpResponse response = null; int responseCode = -1; int uploadStatus = -1; try { - obj = new JSONObject(jsonText).getJSONObject(jsonPropertyName); - response = rest.postToRestApiToken(obj, functionName); + obj = new JSONObject(jsonText).getJSONObject("feedback"); + response = rest.postToRestApiToken(obj, "feedback"); if (response != null) { responseCode = response.getStatusLine().getStatusCode(); if (responseCode == HttpURLConnection.HTTP_OK) { @@ -4405,11 +4143,11 @@ public Boolean UploadOfflineFeedbackRenewal(final String ActivityName) { } else if (responseCode == HttpURLConnection.HTTP_OK) { if (uploadStatus == ToRestApi.UploadStatus.ACCEPTED) { uploadsAccepted += 1; - MoveFile(files[i], 1); + MoveFile(xmlFiles[i], 1); MoveFile(jsonFiles[i], 1); } else { uploadsRejected += 1; - MoveFile(files[i], 2); + MoveFile(xmlFiles[i], 2); MoveFile(jsonFiles[i], 2); } } @@ -4440,33 +4178,37 @@ public Boolean UploadOfflineFeedbackRenewal(final String ActivityName) { }); pd.dismiss(); }).start(); - return true; } @JavascriptInterface public void uploadRenewals() { - final String path; final int totalFiles; final ProgressDialog pd; - path = global.getMainDirectory(); - File[] files = GetListOfFiles(path, "Renewal", null); - File[] jsonFiles = GetListOfJSONFiles(path, "Renewal", null); + if (!global.isNetworkAvailable()) { + ShowDialog(mContext.getResources().getString(R.string.NoInternet)); + return; + } - totalFiles = jsonFiles.length; + File renewalDir = new File(global.getSubdirectory("Renewal")); + File[] xmlFiles = renewalDir.listFiles((file)->file.getName().endsWith(".xml")); + File[] jsonFiles = renewalDir.listFiles((file)->file.getName().endsWith(".json")); - if (totalFiles == 0) { - ShowDialog(mContext.getResources().getString(R.string.NoDataAvailable)); + if (xmlFiles == null || jsonFiles == null) { + ShowDialog(mContext.getResources().getString(R.string.NoFiles)); DeleteRenewals(); return; } - if (!global.isNetworkAvailable()) { - ShowDialog(mContext.getResources().getString(R.string.NoInternet)); + totalFiles = jsonFiles.length; + + if (totalFiles == 0) { + ShowDialog(mContext.getResources().getString(R.string.NoDataAvailable)); + DeleteRenewals(); return; } - pd = AndroidUtil.showProgressDialog(mContext, R.string.Sync, R.string.SyncProcessing); + pd = AndroidUtils.showProgressDialog(mContext, R.string.Sync, R.string.SyncProcessing); new Thread(() -> { StringBuilder messageBuilder = new StringBuilder(); @@ -4485,7 +4227,7 @@ public void uploadRenewals() { int acceptedRenewals = 0; for (int i = 0; i < jsonFiles.length; i++) { - String jsonText = global.getFileText(jsonFiles[i].getPath()); + String jsonText = FileUtils.readFileAsUTF8String(jsonFiles[i]); String renewalInsureeNo = ""; HttpResponse response; @@ -4529,11 +4271,11 @@ public void uploadRenewals() { if (messages.containsKey(uploadStatus)) { if (uploadStatus == ToRestApi.RenewalStatus.ACCEPTED || uploadStatus == ToRestApi.RenewalStatus.ALREADY_ACCEPTED) { - MoveFile(files[i], 1); + MoveFile(xmlFiles[i], 1); MoveFile(jsonFiles[i], 1); acceptedRenewals++; } else { - MoveFile(files[i], 2); + MoveFile(xmlFiles[i], 2); MoveFile(jsonFiles[i], 2); messageBuilder.append(String.format(messageFormat, renewalInsureeNo, messages.get(uploadStatus))); } @@ -4541,15 +4283,17 @@ public void uploadRenewals() { } String successMessage = mContext.getResources().getString(R.string.BulkUpload); - + String failMessage = mContext.getResources().getString(R.string.RenewalRejected); String resultMessage; - if (acceptedRenewals != jsonFiles.length) { + if (acceptedRenewals == 0) { + resultMessage = failMessage; + } else if (acceptedRenewals != jsonFiles.length) { resultMessage = successMessage + "\n" + messageBuilder.toString(); } else { resultMessage = successMessage; } - ((Activity) mContext).runOnUiThread(() -> AndroidUtil.showDialog(mContext, resultMessage)); + ((Activity) mContext).runOnUiThread(() -> AndroidUtils.showDialog(mContext, resultMessage)); pd.dismiss(); }).start(); @@ -4605,7 +4349,7 @@ public String GetListOfImagesContain(final String FileName) { Photos = Directory.listFiles((dir, filename) -> filename.startsWith(FileName + "_")); if (Photos != null && Photos.length > 0) { - Arrays.sort(Photos, (f1, f2) -> f1.getName().compareTo(f2.getName())); + Arrays.sort(Photos, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified())); newFileName = Photos[Photos.length - 1].toString(); } return newFileName; @@ -4613,7 +4357,7 @@ public String GetListOfImagesContain(final String FileName) { private void MoveFile(File file, int res) { String Accepted = "", Rejected = ""; - if (file.getName().contains("RenPol_") || file.getName().contains("RenPolJSON_")) { + if (file.getName().contains("Renewal_") || file.getName().contains("RenewalJSON_")) { Accepted = "AcceptedRenewal"; Rejected = "RejectedRenewal"; } else if (file.getName().contains("feedback_") || file.getName().contains("feedbackJSON_")) { @@ -5491,63 +5235,57 @@ public void UploadPhotos() throws JSONException { pd = new ProgressDialog(mContext); pd = ProgressDialog.show(mContext, "", mContext.getResources().getString(R.string.Uploading)); - new Thread() { - public void run() { + new Thread(() -> { // try { // Thread.sleep(10000); // } catch (InterruptedException e) { // e.printStackTrace(); // } - File[] Photo = getPhotos(); - - if (Photo.length > 0) { - UploadFile uf = new UploadFile(); - if (uf.isValidFTPCredentials()) { - for (int i = 0; i < Photo.length; i++) { - UploadCounter = i + 1; - String FileName = Photo[i].toString().substring(Photo[i].toString().lastIndexOf("/") + 1); - String PhotoQuery = "SELECT PhotoPath FROM tblInsuree WHERE isOffline = 1 AND REPLACE(PhotoPath, RTRIM(PhotoPath, REPLACE(PhotoPath, '/', '')), '') = '" + FileName + "'"; - JSONArray jsonArray = sqlHandler.getResult(PhotoQuery, null); - JSONObject jsonObject = null; - String PhotoPath = ""; - if (jsonArray.length() > 0) { - try { - jsonObject = jsonArray.getJSONObject(0); - PhotoPath = jsonObject.getString("PhotoPath"); - } catch (JSONException e) { - e.printStackTrace(); - } + File[] Photo = getPhotos(); + + if (Photo.length > 0) { + UploadFile uf = new UploadFile(); + if (uf.isValidFTPCredentials()) { + for (int i = 0; i < Photo.length; i++) { + String FileName = Photo[i].toString().substring(Photo[i].toString().lastIndexOf("/") + 1); + String PhotoQuery = "SELECT PhotoPath FROM tblInsuree WHERE isOffline = 1 AND REPLACE(PhotoPath, RTRIM(PhotoPath, REPLACE(PhotoPath, '/', '')), '') = '" + FileName + "'"; + JSONArray jsonArray = sqlHandler.getResult(PhotoQuery, null); + JSONObject jsonObject = null; + String PhotoPath = ""; + if (jsonArray.length() > 0) { + try { + jsonObject = jsonArray.getJSONObject(0); + PhotoPath = jsonObject.getString("PhotoPath"); + } catch (JSONException e) { + e.printStackTrace(); } - if (PhotoPath.trim().length() == 0) { - if (uf.uploadFileToServer(mContext, Photo[i], "com.imispolicies.imis.enrollment")) { - RegisterUploadDetails(FileName); - Uploaded = 1; - Photo[i].delete(); - } + } + if (PhotoPath.trim().length() == 0) { + if (uf.uploadFileToServer(mContext, Photo[i], "com.imispolicies.imis.enrollment")) { + RegisterUploadDetails(FileName); + Uploaded = 1; + Photo[i].delete(); } } } - - } else { - Uploaded = 0; } - ((Activity) mContext).runOnUiThread(new Runnable() { - @Override - public void run() { - if (Uploaded == 1) { - ShowDialog(mContext.getResources().getString(R.string.PhotosUploaded)); - } else { - ShowDialog(mContext.getResources().getString(R.string.NoPhoto)); - } - } - - }); - - pd.dismiss(); - + } else { + Uploaded = 0; } - }.start(); + ((Activity) mContext).runOnUiThread(new Runnable() { + @Override + public void run() { + if (Uploaded == 1) { + ShowDialog(mContext.getResources().getString(R.string.PhotosUploaded)); + } else { + ShowDialog(mContext.getResources().getString(R.string.NoPhoto)); + } + } + + }); + pd.dismiss(); + }).start(); } private SecretKeySpec generateKey(String encPassword) throws Exception { @@ -5637,7 +5375,7 @@ public int ModifyFamily(final String InsuranceNumber) { try { ToRestApi rest = new ToRestApi(); String MD = rest.getObjectFromRestApiToken("family/" + InsuranceNumber.trim()); - if (!isEmpty(MD)) { + if (!StringUtils.isEmpty(MD)) { JSONObject FamilyData = new JSONObject(MD); if (FamilyData.length() == 0) { IsFamilyAvailable = 0; @@ -5723,12 +5461,12 @@ private void DownloadFamilyData(JSONObject FamilyData) throws JSONException, Use for (int i = 0; i < newInsureeArr.length(); i++) { JSONObject insureeObj = newInsureeArr.getJSONObject(i); - if (!isStringEmpty(insureeObj, "photoPath", true)) { + if (!JsonUtils.isStringEmpty(insureeObj, "photoPath", true)) { String photoName = insureeObj.getString("photoPath"); String imagePath = global.getImageFolder() + photoName; insureeObj.put("photoPath", imagePath); OutputStream imageOutputStream = new FileOutputStream(imagePath); - if (!isStringEmpty(insureeObj, "photoBase64", true)) { + if (!JsonUtils.isStringEmpty(insureeObj, "photoBase64", true)) { try { byte[] imageBytes = Base64.decode(insureeObj.getString("photoBase64").getBytes(), Base64.DEFAULT); Bitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); @@ -5761,7 +5499,7 @@ private boolean InsertFamilyDataFromOnline(JSONArray jsonArray) throws JSONExcep "familyAddress", "ethnicity", "confirmationNo", "confirmationType"}; sqlHandler.insertData("tblFamilies", Columns, jsonArray.toString(), ""); - if (!Util.JsonUtil.isStringEmpty(object, "familySMS", true)) { + if (!JsonUtils.isStringEmpty(object, "familySMS", true)) { JSONObject smsData = object.getJSONObject("familySMS"); try { addOrUpdateFamilySms(object.getInt("familyId"), @@ -6347,7 +6085,7 @@ public boolean isLoggingEnabled() { @JavascriptInterface public void clearLogs() { - Util.AndroidUtil.showConfirmDialog( + AndroidUtils.showConfirmDialog( mContext, R.string.ConfirmClearLogs, (d, i) -> new Thread(Log::deleteLogFiles).start() @@ -6356,10 +6094,10 @@ public void clearLogs() { @JavascriptInterface public void exportLogs() { - Util.AndroidUtil.showConfirmDialog( + AndroidUtils.showConfirmDialog( mContext, R.string.ConfirmExportLogs, - (d, i) -> new Thread(Log::zipLogFiles).start() + (d, i) -> new Thread(()-> Log.zipLogFiles(mContext)).start() ); } } diff --git a/app/src/main/java/org/openimis/imispolicies/Compressor.java b/app/src/main/java/org/openimis/imispolicies/Compressor.java deleted file mode 100644 index c4ecf399..00000000 --- a/app/src/main/java/org/openimis/imispolicies/Compressor.java +++ /dev/null @@ -1,69 +0,0 @@ -package org.openimis.imispolicies; - -import net.lingala.zip4j.core.ZipFile; -import net.lingala.zip4j.model.ZipParameters; -import net.lingala.zip4j.util.Zip4jConstants; - -import java.io.File; -import java.util.ArrayList; - -public class Compressor { - public static void zip(ArrayList targetPath, String destinationFilePath, String password) { - try { - ZipParameters parameters = new ZipParameters(); - parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); - parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); - - if (password.length() > 0) { - parameters.setEncryptFiles(true); - parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES); - parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256); - parameters.setPassword(password); - } - - ZipFile zipFile = new ZipFile(destinationFilePath); - zipFile.addFiles(targetPath, parameters); - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static void zip(String targetPath, String destinationFilePath, String password) { - try { - ZipParameters parameters = new ZipParameters(); - parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); - parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); - - if (password.length() > 0) { - parameters.setEncryptFiles(true); - parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES); - parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256); - parameters.setPassword(password); - } - - ZipFile zipFile = new ZipFile(destinationFilePath); - - File targetFile = new File(targetPath); - if (targetFile.exists()) { - zipFile.addFile(targetFile, parameters); - } else if (targetFile.isDirectory()) { - zipFile.addFolder(targetFile, parameters); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - public static void unzip(String targetZipFilePath, String destinationFolderPath, String password) { - try { - ZipFile zipFile = new ZipFile(targetZipFilePath); - if (zipFile.isEncrypted()) { - zipFile.setPassword(password); - } - zipFile.extractAll(destinationFolderPath); - - } catch (Exception e) { - e.printStackTrace(); - } - } -} diff --git a/app/src/main/java/org/openimis/imispolicies/ControlNumberService.java b/app/src/main/java/org/openimis/imispolicies/ControlNumberService.java index d5c6e296..878b9b68 100644 --- a/app/src/main/java/org/openimis/imispolicies/ControlNumberService.java +++ b/app/src/main/java/org/openimis/imispolicies/ControlNumberService.java @@ -6,14 +6,13 @@ import android.content.Context; import org.openimis.imispolicies.tools.Log; -import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.openimis.imispolicies.tools.Util; +import org.openimis.imispolicies.util.StringUtils; import java.io.IOException; import java.net.HttpURLConnection; @@ -120,7 +119,7 @@ private void handleActionFetchBulkCN(String productCode) { JSONObject responseContent = new JSONObject(content); errorMessage = getErrorMessage(responseCode, responseContent); - if (Util.StringUtil.isEmpty(errorMessage)) { + if (StringUtils.isEmpty(errorMessage)) { JSONArray controlNumbers = responseContent.getJSONArray("controlNumbers"); insertBulkControlNumbers(controlNumbers); broadcastSuccess(); diff --git a/app/src/main/java/org/openimis/imispolicies/CustomOnItemSelectedListener.java b/app/src/main/java/org/openimis/imispolicies/CustomOnItemSelectedListener.java index 034a1f23..2122b938 100644 --- a/app/src/main/java/org/openimis/imispolicies/CustomOnItemSelectedListener.java +++ b/app/src/main/java/org/openimis/imispolicies/CustomOnItemSelectedListener.java @@ -9,13 +9,9 @@ import android.widget.AdapterView.OnItemSelectedListener; public class CustomOnItemSelectedListener implements OnItemSelectedListener { - - OverViewPolicies overViewPolicies = new OverViewPolicies(); - SearchOverViewControlNumber searchOverViewControlNumber = new SearchOverViewControlNumber(); - public void onItemSelected(AdapterView parent, View view, int pos, long id) { - overViewPolicies.PayType = parent.getItemAtPosition(pos).toString(); - searchOverViewControlNumber.PayType = parent.getItemAtPosition(pos).toString(); + OverViewPolicies.PayType = parent.getItemAtPosition(pos).toString(); + SearchOverViewControlNumber.PayType = parent.getItemAtPosition(pos).toString(); /* Toast.makeText(parent.getContext(), "OnItemSelectedListener : " + parent.getItemAtPosition(pos).toString(), Toast.LENGTH_SHORT).show();*/ diff --git a/app/src/main/java/org/openimis/imispolicies/Enquire.java b/app/src/main/java/org/openimis/imispolicies/Enquire.java index 4916ea03..3e95a671 100644 --- a/app/src/main/java/org/openimis/imispolicies/Enquire.java +++ b/app/src/main/java/org/openimis/imispolicies/Enquire.java @@ -56,13 +56,12 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.openimis.imispolicies.util.JsonUtils; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.HashMap; -import static org.openimis.imispolicies.tools.Util.JsonUtil.isStringEmpty; - public class Enquire extends AppCompatActivity { private static final String LOG_TAG = "ENQUIRE"; private static final int REQUEST_SCAN_QR_CODE = 1; @@ -241,7 +240,7 @@ private void getInsureeInfo() { if (global.isNetworkAvailable()) { String photo_url_str = ""; try { - if (!isStringEmpty(jsonObject, "photoBase64", true)) { + if (!JsonUtils.isStringEmpty(jsonObject, "photoBase64", true)) { try { byte[] imageBytes = Base64.decode(jsonObject.getString("photoBase64").getBytes(), Base64.DEFAULT); Bitmap image = BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length); @@ -250,7 +249,7 @@ private void getInsureeInfo() { Log.e(LOG_TAG, "Error while processing Base64 image", e); iv.setImageDrawable(getResources().getDrawable(R.drawable.person)); } - } else if (!isStringEmpty(jsonObject, "photoPath", true)) { + } else if (!JsonUtils.isStringEmpty(jsonObject, "photoPath", true)) { photo_url_str = AppInformation.DomainInfo.getDomain() + jsonObject.getString("photoPath"); iv.setImageResource(R.drawable.person); picasso.load(photo_url_str) diff --git a/app/src/main/java/org/openimis/imispolicies/Feedback.java b/app/src/main/java/org/openimis/imispolicies/Feedback.java index f063224d..851cd48c 100644 --- a/app/src/main/java/org/openimis/imispolicies/Feedback.java +++ b/app/src/main/java/org/openimis/imispolicies/Feedback.java @@ -38,7 +38,9 @@ import org.json.JSONException; import org.json.JSONObject; +import org.openimis.imispolicies.tools.Log; import org.xmlpull.v1.XmlSerializer; + import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -47,6 +49,7 @@ import java.util.Calendar; public class Feedback extends AppCompatActivity { + private static final String LOG_TAG = "FEEDBACK"; private Global global; private EditText etOfficer; @@ -107,53 +110,47 @@ protected void onCreate(Bundle savedInstanceState) { pd = ProgressDialog.show(Feedback.this, "", getResources().getString(R.string.UploadingFeedback)); final String[] feed = {null}; - new Thread() { - public void run() { - String Answers = Answers(); - try { - feed[0] = WriteJSON(String.valueOf(etOfficer.getText()), ClaimUUID, etCHFID.getText().toString(), Answers); - WriteXML(String.valueOf(etOfficer.getText()), ClaimUUID, etCHFID.getText().toString(), Answers); - } catch (IllegalArgumentException | IllegalStateException e) { - e.printStackTrace(); - return; - } catch (IOException e) { - e.printStackTrace(); - return; - } - - msgType = 3; - - runOnUiThread(() -> { - switch (msgType) { - case 1: - DeleteRow(ClaimUUID); - //ca.ShowDialog(getResources().getString(R.string.UploadedSuccessfully)); - Toast.makeText(getApplicationContext(), getResources().getString(R.string.UploadedSuccessfully), Toast.LENGTH_LONG).show(); - break; - case 2: - DeleteRow(ClaimUUID); - //ca. ShowDialog(getResources().getString(R.string.ServerRejected)); - Toast.makeText(getApplicationContext(), getResources().getString(R.string.ServerRejected), Toast.LENGTH_LONG).show(); - break; - case 3: - UpdateRow(ClaimUUID); - //ca. ShowDialog(getResources().getString(R.string.SavedOnSDCard)); - Toast.makeText(getApplicationContext(), getResources().getString(R.string.SavedOnSDCard), Toast.LENGTH_LONG).show(); - break; - case -1: - //ca. ShowDialog(getResources().getString(R.string.FeedBackNotUploaded)); - Toast.makeText(getApplicationContext(), getResources().getString(R.string.FeedBackNotUploaded), Toast.LENGTH_LONG).show(); - break; - } - finish(); - }); - pd.dismiss(); + new Thread(() -> { + String Answers = Answers(); + try { + feed[0] = WriteJSON(String.valueOf(etOfficer.getText()), ClaimUUID, etCHFID.getText().toString(), Answers); + WriteXML(String.valueOf(etOfficer.getText()), ClaimUUID, etCHFID.getText().toString(), Answers); + } catch (IllegalArgumentException | IllegalStateException | IOException e) { + e.printStackTrace(); + return; } - }.start(); + + msgType = 3; + + runOnUiThread(() -> { + switch (msgType) { + case 1: + DeleteRow(ClaimUUID); + //ca.ShowDialog(getResources().getString(R.string.UploadedSuccessfully)); + Toast.makeText(getApplicationContext(), getResources().getString(R.string.UploadedSuccessfully), Toast.LENGTH_LONG).show(); + break; + case 2: + DeleteRow(ClaimUUID); + //ca. ShowDialog(getResources().getString(R.string.ServerRejected)); + Toast.makeText(getApplicationContext(), getResources().getString(R.string.ServerRejected), Toast.LENGTH_LONG).show(); + break; + case 3: + UpdateRow(ClaimUUID); + //ca. ShowDialog(getResources().getString(R.string.SavedOnSDCard)); + Toast.makeText(getApplicationContext(), getResources().getString(R.string.SavedOnSDCard), Toast.LENGTH_LONG).show(); + break; + case -1: + //ca. ShowDialog(getResources().getString(R.string.FeedBackNotUploaded)); + Toast.makeText(getApplicationContext(), getResources().getString(R.string.FeedBackNotUploaded), Toast.LENGTH_LONG).show(); + break; + } + finish(); + }); + pd.dismiss(); + }).start(); }); } - private void DeleteRow(String ClaimUUID) { ca.CleanFeedBackTable(ClaimUUID); } @@ -162,10 +159,9 @@ private void UpdateRow(String ClaimUUID) { ca.UpdateFeedBack(ClaimUUID); } - private void WriteXML(String Officer, String ClaimUUID, String CHFID, String Answers) throws IllegalArgumentException, IllegalStateException, IOException { - File MyDir = new File(global.getMainDirectory()); - FileName = "feedback_" + etClaimCode.getText() + ".xml"; + File MyDir = new File(global.getSubdirectory("Feedback")); + FileName = "Feedback_" + etClaimCode.getText() + ".xml"; FeedbackXML = new File(MyDir, FileName); FileOutputStream fos = new FileOutputStream(FeedbackXML); @@ -203,16 +199,14 @@ private void WriteXML(String Officer, String ClaimUUID, String CHFID, String Ans serializer.endDocument(); serializer.flush(); fos.close(); - - } private String WriteJSON(String Officer, String ClaimUUID, String CHFID, String Answers) { - File MyDir = new File(global.getMainDirectory()); + File MyDir = new File(global.getSubdirectory("Feedback")); SimpleDateFormat format = AppInformation.DateTimeInfo.getDefaultDateFormatter(); Calendar cal = Calendar.getInstance(); String d = format.format(cal.getTime()); - FileName = "feedbackJSON_" + etClaimCode.getText() + ".txt"; + FileName = "FeedbackJSON_" + etClaimCode.getText() + ".json"; FeedbackJSON = new File(MyDir, FileName); JSONObject FullObject = new JSONObject(); @@ -238,27 +232,13 @@ private String WriteJSON(String Officer, String ClaimUUID, String CHFID, String e.printStackTrace(); } - } catch (IllegalStateException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (JSONException e) { - e.printStackTrace(); + } catch (IllegalStateException | JSONException e) { + Log.e(LOG_TAG, "Error while writing feedback file", e); } return FullObject.toString(); } - private void MoveFile(File file) { - switch (msgType) { - case 1: - file.renameTo(new File(global.getSubdirectory("AcceptedFeedback"), file.getName())); - break; - case 2: - file.renameTo(new File(global.getSubdirectory("RejectedFeedback"), file.getName())); - break; - } - } - private String Answers() { String Ans = ""; rg1 = findViewById(R.id.radioGroup1); diff --git a/app/src/main/java/org/openimis/imispolicies/FeedbackList.java b/app/src/main/java/org/openimis/imispolicies/FeedbackList.java index ac813bae..1ce1ec8f 100644 --- a/app/src/main/java/org/openimis/imispolicies/FeedbackList.java +++ b/app/src/main/java/org/openimis/imispolicies/FeedbackList.java @@ -131,25 +131,20 @@ protected void onCreate(Bundle savedInstanceState) { etFeedbackSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { - } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { - ((SimpleAdapter) adapter).getFilter().filter(s); - } @Override public void afterTextChanged(Editable s) { - } }); lv.setOnScrollListener(new AbsListView.OnScrollListener() { @Override public void onScrollStateChanged(AbsListView view, int scrollState) { - } @Override @@ -218,7 +213,7 @@ private void fillFeedbacks() { } public String getMasterDataText(String filename) { - ca.unZipFeedbacksRenewals(filename); + //ca.unZipFeedbacksRenewals(filename); String fname = filename.substring(0, filename.indexOf(".")); try { String dir = global.getSubdirectory("Database"); @@ -333,40 +328,33 @@ public void openDialogForFeedbackRenewal() { private void RefreshFeedbacks() throws IOException, XmlPullParserException { if (ca.CheckInternetAvailable()) { // pd = ProgressDialog.show(this, "", getResources().getString(R.string.Loading)); - new Thread() { - public void run() { - String result = null; + new Thread(() -> { + String result = null; - try { - ToRestApi rest = new ToRestApi(); - result = rest.getObjectFromRestApiToken("feedback"); + try { + ToRestApi rest = new ToRestApi(); + result = rest.getObjectFromRestApiToken("feedback"); - if (result.equalsIgnoreCase("[]") || result == null) { - FeedbackList.clear(); - return; - } - } catch (Exception e) { - e.printStackTrace(); + if (result.equalsIgnoreCase("[]") || result == null) { + FeedbackList.clear(); + return; } + } catch (Exception e) { + e.printStackTrace(); + } - Boolean IsInserted = ca.InsertFeedbacks(result); + Boolean IsInserted = ca.InsertFeedbacks(result); - if (!IsInserted) { - ca.ShowDialog(getResources().getString(R.string.ErrorOccurred)); - } - - runOnUiThread(() -> fillFeedbacks()); + if (!IsInserted) { + ca.ShowDialog(getResources().getString(R.string.ErrorOccurred)); + } - // pd.dismiss(); - // swipe.setRefreshing(false); + runOnUiThread(() -> fillFeedbacks()); - } - }.start(); - //fillFeedbacks(); + }).start(); } else { openDialogForFeedbackRenewal(); - //Toast.makeText(this, getResources().getString(R.string.NoInternet), Toast.LENGTH_LONG).show(); } } diff --git a/app/src/main/java/org/openimis/imispolicies/Global.java b/app/src/main/java/org/openimis/imispolicies/Global.java index f5e22362..0dccd566 100644 --- a/app/src/main/java/org/openimis/imispolicies/Global.java +++ b/app/src/main/java/org/openimis/imispolicies/Global.java @@ -37,7 +37,6 @@ import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; -import android.os.Build; import android.os.Environment; import android.util.DisplayMetrics; @@ -46,14 +45,10 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +import org.openimis.imispolicies.util.StreamUtils; -import java.io.BufferedReader; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -82,7 +77,6 @@ public class Global extends Application { private String MainDirectory; private String AppDirectory; private Map SubDirectories; - private List ProtectedDirectories; public static Global getGlobal() { return GlobalContext; @@ -97,29 +91,26 @@ public void onCreate() { super.onCreate(); GlobalContext = this; SubDirectories = new HashMap<>(); - ProtectedDirectories = Arrays.asList("Authentications", "Database", "Images"); initSharedPrefsInts(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - permissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.VIBRATE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE, Manifest.permission.MANAGE_EXTERNAL_STORAGE}; - } else { - permissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.VIBRATE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE}; - } - + permissions = new String[]{Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.VIBRATE, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.INTERNET, Manifest.permission.CAMERA, Manifest.permission.ACCESS_NETWORK_STATE, Manifest.permission.ACCESS_WIFI_STATE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CHANGE_WIFI_STATE}; } private void initSharedPrefsInts() { SharedPreferences sp = getSharedPreferences(PREF_NAME, MODE_PRIVATE); SharedPreferences.Editor editor = sp.edit(); try { - String text = getInputStreamText(getAssets().open("JSON/default_ints.json")); - JSONObject intDefaults = new JSONObject(text); + String text = StreamUtils.readInputStreamAsUTF8String(getAssets().open("JSON/default_ints.json")); - for (Iterator it = intDefaults.keys(); it.hasNext(); ) { - String key = it.next(); + if (text != null) { + JSONObject intDefaults = new JSONObject(text); - if (!sp.contains(key)) { - editor.putInt(key, intDefaults.getInt(key)); + for (Iterator it = intDefaults.keys(); it.hasNext(); ) { + String key = it.next(); + + if (!sp.contains(key)) { + editor.putInt(key, intDefaults.getInt(key)); + } } } } catch (IOException e) { @@ -247,7 +238,7 @@ public String getMainDirectory() { public String getAppDirectory() { if (AppDirectory == null || "".equals(AppDirectory)) { - AppDirectory = createOrCheckDirectory(getApplicationInfo().dataDir); + AppDirectory = createOrCheckDirectory(getApplicationInfo().dataDir + File.separator); if ("".equals(AppDirectory)) { Log.w(FILE_IO_LOG_TAG, "App directory could not be created"); @@ -258,15 +249,8 @@ public String getAppDirectory() { public String getSubdirectory(String subdirectory) { if (!SubDirectories.containsKey(subdirectory) || "".equals(SubDirectories.get(subdirectory))) { - String directory; - - if (ProtectedDirectories.contains(subdirectory)) { - directory = getAppDirectory(); - } else { - directory = getMainDirectory(); - } - - String subDirPath = createOrCheckDirectory(directory + File.separator + subdirectory); + String directory = getAppDirectory(); + String subDirPath = createOrCheckDirectory(directory + subdirectory + File.separator); if ("".equals(subDirPath)) { Log.w(FILE_IO_LOG_TAG, String.format("%s directory could not be created", subdirectory)); @@ -278,32 +262,6 @@ public String getSubdirectory(String subdirectory) { return SubDirectories.get(subdirectory); } - public String getFileText(String path) { - String result = ""; - try { - InputStream inputStream = new FileInputStream(path); - result = getInputStreamText(inputStream); - } catch (IOException e) { - Log.e(FILE_IO_LOG_TAG, String.format("Creating input stream for path: %s failed", path), e); - } - return result; - } - - public String getInputStreamText(InputStream inputStream) { - String line; - StringBuilder stringBuilder = new StringBuilder(); - try { - BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); - while ((line = reader.readLine()) != null) { - stringBuilder.append(line); - stringBuilder.append("\n"); - } - } catch (IOException e) { - Log.e(FILE_IO_LOG_TAG, "Reading text from input stream failed", e); - } - return stringBuilder.toString(); - } - public int getIntKey(String key, int defaultValue) { try { return getSharedPreferences(PREF_NAME, MODE_PRIVATE).getInt(key, defaultValue); @@ -346,7 +304,7 @@ public void setStringKey(String key, String value) { public LinkedHashMap getHFLevels() { LinkedHashMap levels = new LinkedHashMap<>(); try { - String text = getInputStreamText(getAssets().open("JSON/hflevels.json")); + String text = StreamUtils.readInputStreamAsUTF8String(getAssets().open("JSON/hflevels.json")); JSONArray root = new JSONArray(text); for (int i = 0; i < root.length(); i++) { @@ -385,13 +343,13 @@ public void grantUriPermissions(Context context, Uri uri, Intent intent, int per } } - public void sendFile(Uri uri, String mimeType) { + public void sendFile(Context context, Uri uri, String mimeType) { Intent shareExportIntent = new Intent(Intent.ACTION_SEND); shareExportIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); shareExportIntent.putExtra(Intent.EXTRA_STREAM, uri); shareExportIntent.setType(mimeType); Intent chooserIntent = Intent.createChooser(shareExportIntent, null); grantUriPermissions(this, uri, chooserIntent, Intent.FLAG_GRANT_READ_URI_PERMISSION); - startActivity(chooserIntent); + context.startActivity(chooserIntent); } } diff --git a/app/src/main/java/org/openimis/imispolicies/MainActivity.java b/app/src/main/java/org/openimis/imispolicies/MainActivity.java index 71ecd94b..09c882c6 100644 --- a/app/src/main/java/org/openimis/imispolicies/MainActivity.java +++ b/app/src/main/java/org/openimis/imispolicies/MainActivity.java @@ -37,16 +37,16 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.AsyncTask; -import android.os.Build; import android.os.Bundle; -import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.text.TextUtils; + import org.openimis.imispolicies.tools.Log; + import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -69,7 +69,8 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.openimis.imispolicies.tools.Util; +import org.openimis.imispolicies.util.AndroidUtils; +import org.openimis.imispolicies.util.UriUtils; import java.io.BufferedReader; import java.io.File; @@ -80,15 +81,15 @@ import java.io.InputStreamReader; import java.io.OutputStream; -import static android.provider.Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION; - public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener { public static final String LOG_TAG = "MAIN_ACTIVITY"; - private static final int REQUEST_ALL_FILES_ACCESS_CODE = 7; private static final int REQUEST_PERMISSIONS_CODE = 1; - private static final int REQUEST_PICK_MD_FILE = 4; + private static final int REQUEST_PICK_MD_FILE = 2; + public static final int REQUEST_CREATE_ENROL_EXPORT = 3; + public static final int REQUEST_CREATE_FEEDBACK_EXPORT = 4; + public static final int REQUEST_CREATE_RENEWAL_EXPORT = 5; private static final String PREFS_NAME = "CMPref"; private NavigationView navigationView; @@ -150,10 +151,47 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { } else { finish(); } - } else if (requestCode == REQUEST_ALL_FILES_ACCESS_CODE) { - if (checkRequirements()) { - onAllRequirementsMet(); + } else if (requestCode == REQUEST_CREATE_ENROL_EXPORT && resultCode == RESULT_OK && data != null) { + Uri fileUri = data.getData(); + + File exportFile = ca.zipEnrolmentFiles(); + if (exportFile != null) { + ca.clearDirectory("Family"); + ca.clearDirectory("Images"); + + UriUtils.copyFileToUri(this, exportFile, fileUri); + if (!exportFile.delete()) { + Log.w("EXPORT", "Deleting enrol export cache failed"); + } + AndroidUtils.showToast(this, R.string.XmlCreated); + } + } else if (requestCode == REQUEST_CREATE_RENEWAL_EXPORT && resultCode == RESULT_OK && data != null) { + Uri fileUri = data.getData(); + + File exportFile = ca.zipRenewalFiles(); + if (exportFile != null) { + ca.clearDirectory("Renewal"); + + UriUtils.copyFileToUri(this, exportFile, fileUri); + if (!exportFile.delete()) { + Log.w("EXPORT", "Deleting renewal export cache failed"); + } + AndroidUtils.showToast(this, R.string.XmlCreated); } + } else if (requestCode == REQUEST_CREATE_FEEDBACK_EXPORT && resultCode == RESULT_OK && data != null) { + Uri fileUri = data.getData(); + + File exportFile = ca.zipFeedbackFiles(); + if (exportFile != null) { + ca.clearDirectory("Feedback"); + + UriUtils.copyFileToUri(this, exportFile, fileUri); + if (!exportFile.delete()) { + Log.w("EXPORT", "Deleting feedback export cache failed"); + } + AndroidUtils.showToast(this, R.string.XmlCreated); + } + } else { //if user cancels ClientAndroidInterface.inProgress = false; @@ -182,8 +220,7 @@ protected void onCreate(Bundle savedInstanceState) { sqlHandler = new SQLHandler(this); sqlHandler.isPrivate = true; //Set the Image folder path - global.setImageFolder(global.getApplicationInfo().dataDir + "/Images/"); - CreateFolders(); + global.setImageFolder(global.getSubdirectory("Images")); //Check if database exists File database = global.getDatabasePath(SQLHandler.DBNAME); if (!database.exists()) { @@ -289,23 +326,11 @@ protected void onResume() { OfficerName.setText(global.getOfficerName()); } - public static void SetLoggedIn(String Lg, String Lo) { + public static void SetLoggedIn(String LogInText, String LogOutText) { if (global.isLoggedIn()) { - Login.setText(Lo); + Login.setText(LogOutText); } else { - Login.setText(Lg); - } - } - - public void CreateFolders() { - boolean dirsCreated = !"".equals(global.getMainDirectory()); - String[] subdirectories = {"Enrolment", "Photos", "Database", "Authentications", "AcceptedFeedback", "RejectedFeedback", "AcceptedRenewal", "RejectedRenewal", "Family", "EnrolmentXML"}; - for (String dir : subdirectories) { - dirsCreated &= !"".equals(global.getSubdirectory(dir)); - } - - if (!dirsCreated) { - Toast.makeText(getApplicationContext(), getResources().getString(R.string.ImisDirNotCreated), Toast.LENGTH_SHORT).show(); + Login.setText(LogInText); } } @@ -370,9 +395,7 @@ public void PickMasterDataFileDialogFromPage() { } // Write your code here to execute after dialog }).setNegativeButton(getResources().getString(R.string.No), - (dialog, id) -> dialog.cancel()); - - alertDialog2.show(); + (dialog, id) -> dialog.cancel()).show(); } public void ConfirmMasterDataDialog(String filename) { @@ -391,9 +414,7 @@ public void ConfirmMasterDataDialog(String filename) { (dialog, id) -> { dialog.cancel(); finish(); - }); - - alertDialog2.show(); + }).show(); } public void ConfirmDialogPage(String filename) { @@ -409,9 +430,7 @@ public void ConfirmDialogPage(String filename) { MasterDataLocalAsync masterDataLocalAsync = new MasterDataLocalAsync(); masterDataLocalAsync.execute(); }).setNegativeButton(getResources().getString(R.string.Quit), - (dialog, id) -> dialog.cancel()); - - alertDialog2.show(); + (dialog, id) -> dialog.cancel()).show(); } public void ShowEnrolmentOfficerDialog() { @@ -429,8 +448,8 @@ public void ShowEnrolmentOfficerDialog() { alertDialogBuilder.setView(promptsView); - final EditText userInput = (EditText) promptsView.findViewById(R.id.txtOfficerCode); - final TextView tVDialogTile = (TextView) promptsView.findViewById(R.id.tvDialogTitle); + final EditText userInput = promptsView.findViewById(R.id.txtOfficerCode); + final TextView tVDialogTile = promptsView.findViewById(R.id.tvDialogTitle); if (MasterData == 0) { tVDialogTile.setText(getResources().getString(R.string.MasterDataNotFound)); @@ -484,9 +503,7 @@ public void ShowEnrolmentOfficerDialog() { (dialog, id) -> { dialog.cancel(); finish(); - }); - - alertDialog = alertDialogBuilder.show(); + }).show(); } public void ShowMasterDataDialog() { @@ -617,7 +634,7 @@ private void createImageFolder() { @Override public void onBackPressed() { - DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); + DrawerLayout drawer = findViewById(R.id.drawer_layout); if (drawer.isDrawerOpen(GravityCompat.START)) { drawer.closeDrawer(GravityCompat.START); } else { @@ -644,8 +661,6 @@ public boolean onCreateOptionsMenu(Menu menu) { } private void changeLanguage(String LanguageCode, boolean withRefresh) { - - //General gen = new General(); global.changeLanguage(this, LanguageCode); if (withRefresh) { @@ -892,27 +907,6 @@ public static boolean hasPermissions(Context context, String... permissions) { return true; } - public void externalStorageAccessDialog() { - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context) - .setTitle(R.string.ExternalStorageAccess) - .setMessage(getResources().getString(R.string.ExternalStorageAccessInfo, getResources().getString(R.string.app_name_policies))) - .setCancelable(false) - .setPositiveButton(R.string.Ok, - (dialog, id) -> { - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.R) { - Intent intent = new Intent(ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION); - startActivityForResult(intent, REQUEST_ALL_FILES_ACCESS_CODE); - } - }) - .setNegativeButton(R.string.ForceClose, - (dialog, id) -> { - dialog.cancel(); - finish(); - }); - - alertDialogBuilder.show(); - } - public void PermissionsDialog() { AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(context) .setTitle(R.string.Permissions) @@ -930,13 +924,6 @@ public void PermissionsDialog() { } public boolean checkRequirements() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - if (!Environment.isExternalStorageManager()) { - externalStorageAccessDialog(); - return false; - } - } - if (!hasPermissions(this, global.getPermissions())) { PermissionsDialog(); return false; diff --git a/app/src/main/java/org/openimis/imispolicies/OutputStreamImageTarget.java b/app/src/main/java/org/openimis/imispolicies/OutputStreamImageTarget.java index d3d74c9b..932a311a 100644 --- a/app/src/main/java/org/openimis/imispolicies/OutputStreamImageTarget.java +++ b/app/src/main/java/org/openimis/imispolicies/OutputStreamImageTarget.java @@ -2,6 +2,7 @@ import android.graphics.Bitmap; import android.graphics.drawable.Drawable; + import org.openimis.imispolicies.tools.Log; import com.squareup.picasso.Picasso; @@ -13,15 +14,28 @@ public class OutputStreamImageTarget implements Target { private static final String LOG_TAG = "OSIMAGETARGET"; private final OutputStream outputStream; private final int imageQuality; + private final Runnable onSuccess; public OutputStreamImageTarget(OutputStream outputStream, int imageQuality) { this.outputStream = outputStream; this.imageQuality = imageQuality; + this.onSuccess = () -> { + }; + } + + public OutputStreamImageTarget(OutputStream outputStream, int imageQuality, Runnable onSuccess) { + this.outputStream = outputStream; + this.imageQuality = imageQuality; + this.onSuccess = onSuccess; } @Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { - bitmap.compress(Bitmap.CompressFormat.JPEG, imageQuality, outputStream); + if (bitmap.compress(Bitmap.CompressFormat.JPEG, imageQuality, outputStream)) { + onSuccess.run(); + } else { + Log.e(LOG_TAG, "Compressing image failed"); + } } @Override diff --git a/app/src/main/java/org/openimis/imispolicies/RenewList.java b/app/src/main/java/org/openimis/imispolicies/RenewList.java index 97b2936d..c3416223 100644 --- a/app/src/main/java/org/openimis/imispolicies/RenewList.java +++ b/app/src/main/java/org/openimis/imispolicies/RenewList.java @@ -36,7 +36,9 @@ import android.support.v7.app.AppCompatActivity; import android.text.Editable; import android.text.TextWatcher; + import org.openimis.imispolicies.tools.Log; + import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -50,18 +52,14 @@ import android.widget.TextView; import android.widget.Toast; -import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.openimis.imispolicies.tools.Util; +import org.openimis.imispolicies.util.FileUtils; +import org.openimis.imispolicies.util.UriUtils; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; import java.net.HttpURLConnection; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -70,9 +68,8 @@ public class RenewList extends AppCompatActivity { private static final String LOG_TAG = "RENEWAL"; - private static final int REQUEST_OPEN_DOCUMENT_CODE = 1; + private static final int REQUEST_IMPORT_RENEWAL_FILE = 1; private Global global; - private String aBuffer = ""; private ListView lv; private SwipeRefreshLayout swipe; private ArrayList> renewalList = new ArrayList<>(); @@ -80,7 +77,6 @@ public class RenewList extends AppCompatActivity { private ClientAndroidInterface ca; private ListAdapter adapter; public String UnlistedRenPolicy; - boolean isUserLogged = false; @Override @@ -178,12 +174,28 @@ public void confirmImportRenewal(String filename, Uri uri) { .setMessage(filename) .setPositiveButton(R.string.Yes, (dialog, which) -> new Thread(() -> { - if (copyRenewalFile(filename, uri)) { - ca.unZipFeedbacksRenewals(filename); - String txtFilename = Util.FileUtil.replaceFilenameExtension(filename, ".txt"); - loadRenewalFile(txtFilename); - ca.InsertRenewalsFromExtract(aBuffer); - runOnUiThread(this::fillRenewals); + File renewalFile = copyRenewalFile(uri); + if (renewalFile != null) { + ca.unZipFeedbacksRenewals(renewalFile); + File[] unzippedFiles = null; + File renewalDir = renewalFile.getParentFile(); + if (renewalDir != null) { + unzippedFiles = renewalDir.listFiles((file) -> file.isFile() && !file.equals(renewalFile)); + } + if (unzippedFiles != null) { + if (unzippedFiles.length == 0) { + Log.w(LOG_TAG, "No renewal files after unpacking"); + } + for(File f: unzippedFiles) { + String renewals = loadRenewalFile(f); + if (renewals != null) { + ca.InsertRenewalsFromExtract(renewals); + } + } + runOnUiThread(this::fillRenewals); + FileUtils.deleteFiles(unzippedFiles); + } + FileUtils.deleteFile(renewalFile); } }).start()) .setNegativeButton(R.string.No, @@ -191,44 +203,23 @@ public void confirmImportRenewal(String filename, Uri uri) { .show(); } - private void loadRenewalFile(String filename) { - File file = new File(global.getSubdirectory("Database"), filename); - try { - if (file.exists()) { - InputStream inputStream = new FileInputStream(file); - aBuffer = Util.StreamUtil.readInputStreamAsUTF8String(inputStream); - } else { - Log.e(LOG_TAG, "Unpacked renewal file does not exists"); - } - } catch (IOException e) { - Log.e(LOG_TAG, "Error while creating input stream for renewal file", e); + private String loadRenewalFile(File file) { + if (file.exists()) { + return FileUtils.readFileAsUTF8String(file); + } else { + Log.e(LOG_TAG, "Unpacked renewal file does not exists"); + return null; } } - private boolean copyRenewalFile(String filename, Uri uri) { - try { - boolean success; - InputStream is = getContentResolver().openInputStream(uri); - File outputFile = new File(global.getSubdirectory("Database"), filename); - - if (outputFile.exists()) { - success = outputFile.delete(); - if (!success) { - throw new IOException("Deleting existing renewal file failed"); - } - } - - success = outputFile.createNewFile(); - if (!success) { - throw new IOException("Creating new renewal file failed"); - } - - IOUtils.copy(is, new FileOutputStream(outputFile)); - return true; - } catch (Exception e) { - Log.e(LOG_TAG, "Copying renewal file failed", e); + private File copyRenewalFile(Uri uri) { + File copiedFile = UriUtils.copyUriContentToCache(this, uri, "imports/renewal/renewal.rar"); + if (copiedFile != null) { + return copiedFile; + } else { + Log.e(LOG_TAG, "Copying renewal file failed, uri: " + uri); + return null; } - return false; } public void requestImportRenewal() { @@ -241,7 +232,7 @@ public void requestImportRenewal() { intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); try { - startActivityForResult(intent, REQUEST_OPEN_DOCUMENT_CODE); + startActivityForResult(intent, REQUEST_IMPORT_RENEWAL_FILE); } catch (ActivityNotFoundException e) { Toast.makeText(getApplicationContext(), getResources().getString(R.string.NoFileExporerInstalled), Toast.LENGTH_SHORT).show(); } @@ -254,9 +245,9 @@ public void requestImportRenewal() { @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (requestCode == REQUEST_OPEN_DOCUMENT_CODE && resultCode == RESULT_OK && data != null) { + if (requestCode == REQUEST_IMPORT_RENEWAL_FILE && resultCode == RESULT_OK && data != null) { Uri uri = data.getData(); - String filename = Util.UriUtil.getDisplayName(this, uri); + String filename = UriUtils.getDisplayName(this, uri); String expectedFilename = String.format("renewal_%s.rar", global.getOfficerCode().toLowerCase()); if (filename != null && filename.toLowerCase().equals(expectedFilename)) { @@ -279,7 +270,7 @@ private void fillRenewals() { JSONObject object; try { - HashMap renewal; + HashMap renewal; JSONArray jsonArray = new JSONArray(result); renewalList.clear(); diff --git a/app/src/main/java/org/openimis/imispolicies/Renewal.java b/app/src/main/java/org/openimis/imispolicies/Renewal.java index fd8578ab..c42b6a51 100644 --- a/app/src/main/java/org/openimis/imispolicies/Renewal.java +++ b/app/src/main/java/org/openimis/imispolicies/Renewal.java @@ -54,7 +54,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -import org.openimis.imispolicies.tools.Util.StringUtil; +import org.openimis.imispolicies.util.StringUtils; import org.xmlpull.v1.XmlSerializer; import java.io.File; @@ -91,7 +91,6 @@ public class Renewal extends AppCompatActivity { private int LocationId; private int RenewalId; private String RenewalUUID; - private int result; private EditText PolicyValue; private ListAdapter adapter; @@ -165,7 +164,11 @@ protected void onCreate(Bundle savedInstanceState) { BindSpinnerProduct(); } else { spProduct.setVisibility(View.GONE); - assignNextFreeCn(etProductCode.getText().toString()); + + if (ca.IsBulkCNUsed()) { + assignNextFreeCn(etProductCode.getText().toString()); + } + BindSpinnerPayers(); } @@ -265,17 +268,15 @@ private String GetSelectedProduct() { private void WriteXML() { try { - //Create All directories - File MyDir = new File(global.getMainDirectory()); //Create File name Date date = Calendar.getInstance().getTime(); String d = AppInformation.DateTimeInfo.getDefaultFileDatetimeFormatter().format(date); - FileName = "RenPol_" + d + "_" + etCHFID.getText().toString() + "_" + etReceiptNo.getText().toString() + ".xml"; + FileName = "Renewal_" + d + "_" + etCHFID.getText().toString() + "_" + etReceiptNo.getText().toString() + ".xml"; d = AppInformation.DateTimeInfo.getDefaultDateFormatter().format(date); String PayerId = GetSelectedPayer(); //Create XML file - File policyXML = new File(MyDir, FileName); + File policyXML = new File(global.getSubdirectory("Renewal"), FileName); FileOutputStream fos = new FileOutputStream(policyXML); @@ -341,17 +342,16 @@ private void WriteXML() { } public String WriteJSON() { - File MyDir = new File(global.getMainDirectory()); Date date = Calendar.getInstance().getTime(); String d = AppInformation.DateTimeInfo.getDefaultFileDatetimeFormatter().format(date); - FileName = "RenPolJSON_" + d + "_" + etCHFID.getText().toString() + "_" + etReceiptNo.getText().toString() + ".txt"; + FileName = "RenewalJSON_" + d + "_" + etCHFID.getText().toString() + "_" + etReceiptNo.getText().toString() + ".json"; String PayerId = GetSelectedPayer(); String ProductCode = GetSelectedProduct(); d = AppInformation.DateTimeInfo.getDefaultDateFormatter().format(date); //Create XML file - File policyJSON = new File(MyDir, FileName); + File policyJSON = new File(global.getSubdirectory("Renewal"), FileName); JSONObject FullObject = new JSONObject(); @@ -663,7 +663,7 @@ private void showInfoDialog(int resId) { } private void assignNextFreeCn(String productCode) { - if (!StringUtil.equals(productCode, "0")) { + if (!StringUtils.equals(productCode, "0")) { String controlNumber = sqlHandler.getNextFreeCn(etOfficer.getText().toString(), productCode); if (controlNumber != null) { etControlNumber.setText(controlNumber); diff --git a/app/src/main/java/org/openimis/imispolicies/SQLHandler.java b/app/src/main/java/org/openimis/imispolicies/SQLHandler.java index b960182e..775abd61 100644 --- a/app/src/main/java/org/openimis/imispolicies/SQLHandler.java +++ b/app/src/main/java/org/openimis/imispolicies/SQLHandler.java @@ -474,7 +474,7 @@ public void onOpen(SQLiteDatabase db) { private void openDatabase() { String dbPath = mContext.getDatabasePath(DBNAME).getPath(); - String dbOfflinePath = global.getMainDirectory() + File.separator + OFFLINEDBNAME; + String dbOfflinePath = global.getAppDirectory() + File.separator + OFFLINEDBNAME; if (mDatabase != null && mDatabase.isOpen()) { return; } diff --git a/app/src/main/java/org/openimis/imispolicies/tools/ImageManager.java b/app/src/main/java/org/openimis/imispolicies/tools/ImageManager.java index 679639b2..4c7820c8 100644 --- a/app/src/main/java/org/openimis/imispolicies/tools/ImageManager.java +++ b/app/src/main/java/org/openimis/imispolicies/tools/ImageManager.java @@ -4,6 +4,7 @@ import android.content.Context; import org.openimis.imispolicies.Global; +import org.openimis.imispolicies.util.FileUtils; import java.io.File; @@ -23,14 +24,14 @@ protected ImageManager(Context context) { } public File getNewestInsureeImage(String insureeNumber) { - return Util.FileUtil.getNewestFileStartingWith( + return FileUtils.getNewestFileStartingWith( new File(Global.getGlobal().getImageFolder()), insureeNumber + "_" ); } public File[] getInsureeImages(String insureeNumber) { - return Util.FileUtil.getFilesStartingWith( + return FileUtils.getFilesStartingWith( new File(Global.getGlobal().getImageFolder()), insureeNumber + "_" ); diff --git a/app/src/main/java/org/openimis/imispolicies/tools/StorageManager.java b/app/src/main/java/org/openimis/imispolicies/tools/StorageManager.java index 0503f708..475b70f3 100644 --- a/app/src/main/java/org/openimis/imispolicies/tools/StorageManager.java +++ b/app/src/main/java/org/openimis/imispolicies/tools/StorageManager.java @@ -1,29 +1,18 @@ package org.openimis.imispolicies.tools; import android.app.Activity; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.provider.DocumentsContract; import android.support.annotation.NonNull; -import android.support.v4.content.FileProvider; import android.util.Log; -import org.openimis.imispolicies.BuildConfig; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; - public class StorageManager { protected static final String LOG_TAG = "STORAGEMANAGER"; - protected static final String FILE_PROVIDER_NAME = String.format("%s.fileprovider", BuildConfig.APPLICATION_ID); protected final Context context; - protected final ContentResolver contentResolver; public static StorageManager of(Context context) { return new StorageManager(context); @@ -31,7 +20,6 @@ public static StorageManager of(Context context) { protected StorageManager(Context context) { this.context = context; - this.contentResolver = this.context.getContentResolver(); } public void requestOpenDirectory(int requestCode, Uri initialUri) { @@ -93,109 +81,4 @@ public void requestCreateFile(int requestCode, @NonNull String mimeType, String public void requestCreateFile(int requestCode, @NonNull String mimeType, String initialFileName) { requestCreateFile(requestCode, mimeType, initialFileName, null); } - - /** - * Copy the content of the provided uri to provided file. The file location must allow creating - * a file and writing to a file. - * - * @param uri Uri of the file to copy. The Uri should allow read and should be openable - * @param targetFile File to be used as output stream - * @return File object pointing to created file - */ - public File copyUriContentToFile(@NonNull Uri uri, @NonNull File targetFile) { - if (!targetFile.exists()) { - Util.FileUtil.createFileWithSubdirectories(targetFile); - } - try (InputStream is = contentResolver.openInputStream(uri)) { - Util.FileUtil.writeToFile(targetFile, is); - } catch (IOException e) { - Log.e(LOG_TAG, "Error while opening streams", e); - } - - return targetFile; - } - - - /** - * Create a content URI for a file owned by the app. The directory path have to be specified in - * res/xml/paths.xml to be eligible to provide content. Uri returned by this method can be shared - * with other apps with intents. - * - * @param file File to be specified by generated content URI - * @return Content URI for a given file - */ - public Uri createUriForFile(@NonNull File file) { - Uri uri = FileProvider.getUriForFile(context, FILE_PROVIDER_NAME, file); - if (uri == null) { - org.openimis.imispolicies.tools.Log.w(LOG_TAG, "Failed to create temp photo URI"); - } - return uri; - } - - /** - * Copy the content of the provided uri to cache directory under provided path. The caller is - * responsible to delete the file after use. The file can be deleted by Android or the user - * without notifying the app. - * - * @param uri Uri of the file to copy. The Uri should allow read and should be openable - * @param targetPath Path inside cache directory to save the file (cache location will be appended) - * @return File object pointing to created file - */ - public File copyUriContentToCache(@NonNull Uri uri, @NonNull String targetPath) { - return copyUriContentToFile(uri, new File(context.getCacheDir(), targetPath)); - } - - /** - * Create a temporary file inside app's cache directory under specified path. This file should be - * manually deleted, but the cache directory can be emptied by the user or by the Android to save - * storage space. If the file exists, it will be deleted and recreated. - * - * @param targetPath Path to the temporary file inside cache, the cache root will be appended - * @return Created temp file - */ - public File createTempFile(@NonNull String targetPath) { - return createTempFile(targetPath, false); - } - - /** - * Create a temporary file inside app's cache directory under specified path. This file should be - * manually deleted, but the cache directory can be emptied by the user or by the Android to save - * storage space. If the file exists, it will be deleted and recreated. - * - * @param targetPath Path to the temporary file inside cache, the cache root will be appended - * @param preparePathOly Set to true if the abstract path should point to non-existing file - * (i.e. to forward the file object to a library to create the file) - * @return Created temp file - */ - public File createTempFile(@NonNull String targetPath, boolean preparePathOly) { - File tempFile = new File(context.getCacheDir(), targetPath); - - if (tempFile.exists() && tempFile.delete()) { - org.openimis.imispolicies.tools.Log.i(LOG_TAG, "Leftover temp file deleted: " + tempFile.getAbsolutePath()); - } - - if (preparePathOly) { - File parentFile = tempFile.getParentFile(); - if (parentFile != null) { - return Util.FileUtil.createDirectoryWithSubdirectories(parentFile) ? tempFile : null; - } else { - return null; - } - } else { - return Util.FileUtil.createFileWithSubdirectories(tempFile) ? tempFile : null; - } - } - - /** - * Convenience method for removing a temp file form specified path inside cache directory created - * by createTempFile(String). The file can also be safely deleted with returned File object. - * - * @param targetPath Path to the temporary file inside cache, the cache root will be appended - * @return true if file under targetPath does not exist or was successfully deleted, false if file - * exists and delete failed - */ - public boolean removeTempFile(@NonNull String targetPath) { - File tempFile = new File(context.getCacheDir(), targetPath); - return !tempFile.exists() || tempFile.delete(); - } } diff --git a/app/src/main/java/org/openimis/imispolicies/tools/Util.java b/app/src/main/java/org/openimis/imispolicies/tools/Util.java deleted file mode 100644 index b700137a..00000000 --- a/app/src/main/java/org/openimis/imispolicies/tools/Util.java +++ /dev/null @@ -1,371 +0,0 @@ -package org.openimis.imispolicies.tools; - -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.database.Cursor; -import android.net.Uri; -import android.provider.OpenableColumns; -import android.support.annotation.NonNull; -import android.text.TextUtils; -import android.widget.Toast; - -import org.json.JSONException; -import org.json.JSONObject; -import org.openimis.imispolicies.R; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; - -public class Util { - public static class StringUtil { - /** - * @param string String to be checked - * @return is string null or empty - */ - public static boolean isEmpty(CharSequence string) { - return isEmpty(string, false); - } - - /** - * @param string String to be checked - * @param checkNullString Should "null" string be considered empty - * @return is string null or empty - */ - public static boolean isEmpty(CharSequence string, boolean checkNullString) { - return string == null - || string.equals("") - || (string.equals("null") && checkNullString); - } - - public static boolean equals(CharSequence s1, CharSequence s2) { - return TextUtils.equals(s1, s2); - } - } - - public static class JsonUtil { - /** - * @param object Json object - * @param field field to be checked - * @return if the field does not exists, is null or empty - */ - public static boolean isStringEmpty(JSONObject object, @NonNull String field) { - return isStringEmpty(object, field, false); - } - - /** - * @param object Json object - * @param field field to be checked - * @param checkNullString Should "null" string be considered empty - * @return if the field does not exists, is null or empty - */ - public static boolean isStringEmpty(JSONObject object, @NonNull String field, boolean checkNullString) { - try { - return object == null - || !object.has(field) - || StringUtil.isEmpty(object.getString(field), checkNullString); - } catch (JSONException e) { - return true; - } - } - } - - public static class AndroidUtil { - public static ProgressDialog showProgressDialog(Context context, int titleResId, int messageResId) { - return ProgressDialog.show( - context, - context.getResources().getString(titleResId), - context.getResources().getString(messageResId) - ); - } - - public static void showToast(Context context, int messageResId) { - Toast.makeText(context, messageResId, Toast.LENGTH_SHORT).show(); - } - - public static void showToast(Context context, CharSequence message) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); - } - - public static AlertDialog showDialog(Context context, int messageResId) { - return new AlertDialog.Builder(context) - .setMessage(messageResId) - .setCancelable(false) - .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { - }).show(); - } - - public static AlertDialog showDialog(Context context, CharSequence message) { - return new AlertDialog.Builder(context) - .setMessage(message) - .setCancelable(false) - .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { - }).show(); - } - - public static AlertDialog showConfirmDialog(Context context, int messageResId, DialogInterface.OnClickListener onPositive) { - return new AlertDialog.Builder(context) - .setMessage(messageResId) - .setCancelable(false) - .setPositiveButton(R.string.Ok, onPositive) - .setNegativeButton(R.string.Cancel, (dialogInterface, i) -> { - }).show(); - } - } - - public static class UriUtil { - private static final String LOG_TAG = "UriUtil"; - - public static String getDisplayName(@NonNull Context context, @NonNull Uri uri) { - try (Cursor c = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) { - c.moveToFirst(); - c.moveToFirst(); - return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME)); - } catch (Exception e) { - Log.e(LOG_TAG, "Reading file name from URI failed", e); - } - return null; - } - - public static void writeToUri(@NonNull Context context, @NonNull Uri uri, @NonNull InputStream is) { - try (OutputStream os = context.getContentResolver().openOutputStream(uri, "w")) { - StreamUtil.bufferedStreamCopy(is, os); - } catch (IOException e) { - Log.e(LOG_TAG, "Writing to uri failed: " + uri, e); - } - } - - /** - * Copy byte content of the file to location described by the uri. The uri must be openable and - * allow writing. The file must be openable and readable. - * - * @param file File to be read - * @param uri Uri pointing to writable location - */ - public static void copyFileToUri(@NonNull Context context, @NonNull File file, @NonNull Uri uri) { - try (InputStream is = new FileInputStream(file)) { - Util.UriUtil.writeToUri(context, uri, is); - } catch (IOException e) { - android.util.Log.e(LOG_TAG, "Error while opening streams", e); - } - } - - /** - * Copy byte content of the file to location described by the uri. The uri must be openable and - * allow writing. The file must be openable and readable. - * - * @param filePath Path to file to be read - * @param uri Uri pointing to writable location - */ - public static void copyFileToUri(@NonNull Context context, @NonNull String filePath, @NonNull Uri uri) { - copyFileToUri(context, new File(filePath), uri); - } - } - - public static class StreamUtil { - private static final int buffSize = 8192; - private static final String LOG_TAG = "StreamUtil"; - - /** - * Only use this method if you can be sure the content of the input stream - * can be read, is a UTF-8 text and will fit in memory (as String). - * - * @param is input stream to be read - * @return content of the input stream or null if IO exception occurs - */ - public static String readInputStreamAsUTF8String(@NonNull InputStream is) { - try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { - StringBuilder stringBuilder = null; - String inputStr; - while ((inputStr = streamReader.readLine()) != null) { - if(stringBuilder != null) { - stringBuilder.append("\n"); - stringBuilder.append(inputStr); - } else { - stringBuilder = new StringBuilder(inputStr); - } - } - return stringBuilder != null? stringBuilder.toString(): null; - } catch (IOException e) { - Log.e(LOG_TAG, "Error while reading input stream", e); - } - - return null; - } - - /** - * Copy input stream to output stream using a byte buffer (8kb by default) - * - * @param is source stream - * @param os target stream - * @throws IOException thrown on read/write error - */ - public static void bufferedStreamCopy(@NonNull InputStream is, @NonNull OutputStream os) throws IOException { - byte[] buffer = new byte[buffSize]; - - int read; - while ((read = is.read(buffer)) >= 0) { - os.write(buffer, 0, read); - } - } - } - - public static class FileUtil { - private static final String LOG_TAG = "FileUtil"; - - /** - * Replaces the extension of the file - * - * @param filename filename ending with extension - * @param targetExtension target extension, including a dot (to protect from replacing - * a substring inside filename that match current extension) - * @return filename with replaced extension, or null if the filename does not end with - * extension - */ - public static String replaceFilenameExtension(@NonNull String filename, @NonNull String targetExtension) { - try { - String currentExtension = filename.substring(filename.lastIndexOf('.')); - return filename.replace(currentExtension, targetExtension); - } catch (IndexOutOfBoundsException e) { - Log.e(LOG_TAG, "Filename does not have an extension at the end", e); - } - return null; - } - - public static boolean createFileWithSubdirectories(@NonNull File file) { - try { - File parent = file.getParentFile(); - - if (parent != null && !parent.exists() && !parent.mkdirs()) { - Log.e(LOG_TAG, "Creating parent directories failed for path: " + file.getAbsolutePath()); - } - - if (!file.createNewFile()) { - Log.e(LOG_TAG, "File already exists: " + file.getAbsolutePath()); - } - - return true; - } catch (Exception e) { - Log.e(LOG_TAG, "Error while creating file: " + file.getAbsolutePath(), e); - } - - return false; - } - - public static boolean createDirectoryWithSubdirectories(@NonNull File directory) { - try { - if (!directory.exists() && !directory.mkdirs()) { - Log.e(LOG_TAG, "Creating directory failed for path: " + directory.getAbsolutePath()); - } - return true; - } catch (Exception e) { - Log.e(LOG_TAG, "Error while creating directory: " + directory.getAbsolutePath(), e); - } - - return false; - } - - public static void deleteFiles(File[] files) { - for (File file : files) { - if (!file.delete()) { - Log.w(LOG_TAG, "Delete file failed: " + file.getAbsolutePath()); - } - } - } - - public static void writeToFile(@NonNull File file, @NonNull InputStream is) { - try (OutputStream os = new FileOutputStream(file)) { - StreamUtil.bufferedStreamCopy(is, os); - } catch (IOException e) { - Log.e(LOG_TAG, "Error while writing to file: " + file.getAbsolutePath(), e); - } - } - - public static File[] getFilesStartingWith(@NonNull File directory, @NonNull String filenamePrefix) { - if (!directory.exists()) { - android.util.Log.e(LOG_TAG, "Directory does not exists: " + directory); - return null; - } - - if (!directory.isDirectory()) { - android.util.Log.e(LOG_TAG, "Provided path is not a directory: " + directory); - return null; - } - - return directory.listFiles((dir, filename) -> filename.startsWith(filenamePrefix)); - } - - public static File getNewestFileStartingWith(@NonNull File directory, @NonNull String filenamePrefix) { - File[] files = getFilesStartingWith(directory, filenamePrefix); - if (files == null || files.length == 0) { - return null; - } - Arrays.sort(files, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified())); - return files[files.length - 1]; - } - - public static String readFileAsUTF8String(@NonNull File file) { - try { - InputStream is = new FileInputStream(file); - return StreamUtil.readInputStreamAsUTF8String(is); - } catch (IOException e) { - Log.e(LOG_TAG, "Opening input stream failed for file: " + file.getAbsolutePath(), e); - } - return null; - } - - public static File createOrCheckDirectory(File directory) { - if (directory.exists() || directory.mkdirs()) { - return directory; - } else { - return null; - } - } - - public static File zipDirectories(File outputFile, String zipPassword, File... directories) { - ArrayList filesToAdd = new ArrayList<>(); - for (File directory : directories) { - if (!directory.exists() || !directory.isDirectory()) { - Log.w(LOG_TAG, "Provided file is not a directory: " + directory); - continue; - } - - File[] files = directory.listFiles(); - if (files == null) { - Log.w(LOG_TAG, "Reading a directory filed: " + directory); - continue; - } - - Collections.addAll(filesToAdd, files); - } - - return Compressor.zip(filesToAdd, outputFile, zipPassword); - } - - public static int getFileCount(File directory) { - if (!directory.exists() || !directory.isDirectory()) { - Log.e(LOG_TAG, "Not a directory: " + directory.getAbsolutePath()); - return -1; - } - - File[] files = directory.listFiles(File::isFile); - if (files != null) { - return files.length; - } else { - Log.e(LOG_TAG, "Counting files failed: " + directory.getAbsolutePath()); - return -1; - } - } - } -} diff --git a/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java b/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java new file mode 100644 index 00000000..c407911c --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java @@ -0,0 +1,52 @@ +package org.openimis.imispolicies.util; + +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.Context; +import android.content.DialogInterface; +import android.widget.Toast; + +import org.openimis.imispolicies.R; + +public class AndroidUtils { + public static ProgressDialog showProgressDialog(Context context, int titleResId, int messageResId) { + return ProgressDialog.show( + context, + context.getResources().getString(titleResId), + context.getResources().getString(messageResId) + ); + } + + public static void showToast(Context context, int messageResId) { + Toast.makeText(context, messageResId, Toast.LENGTH_SHORT).show(); + } + + public static void showToast(Context context, CharSequence message) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } + + public static AlertDialog showDialog(Context context, int messageResId) { + return new AlertDialog.Builder(context) + .setMessage(messageResId) + .setCancelable(false) + .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { + }).show(); + } + + public static AlertDialog showDialog(Context context, CharSequence message) { + return new AlertDialog.Builder(context) + .setMessage(message) + .setCancelable(false) + .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { + }).show(); + } + + public static AlertDialog showConfirmDialog(Context context, int messageResId, DialogInterface.OnClickListener onPositive) { + return new AlertDialog.Builder(context) + .setMessage(messageResId) + .setCancelable(false) + .setPositiveButton(R.string.Ok, onPositive) + .setNegativeButton(R.string.Cancel, (dialogInterface, i) -> { + }).show(); + } + } \ No newline at end of file diff --git a/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java b/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java new file mode 100644 index 00000000..fcd2a57d --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java @@ -0,0 +1,204 @@ +package org.openimis.imispolicies.util; + +import android.content.Context; +import android.support.annotation.NonNull; + +import org.openimis.imispolicies.tools.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class FileUtils { + private static final String LOG_TAG = "FileUtil"; + + /** + * Replaces the extension of the file + * + * @param filename filename ending with extension + * @param targetExtension target extension, including a dot (to protect from replacing + * a substring inside filename that match current extension) + * @return filename with replaced extension, or null if the filename does not end with + * extension + */ + public static String replaceFilenameExtension(@NonNull String filename, @NonNull String targetExtension) { + try { + String currentExtension = filename.substring(filename.lastIndexOf('.')); + return filename.replace(currentExtension, targetExtension); + } catch (IndexOutOfBoundsException e) { + Log.e(LOG_TAG, "Filename does not have an extension at the end", e); + } + return null; + } + + public static boolean createFileWithSubdirectories(@NonNull File file) { + try { + File parent = file.getParentFile(); + + if (parent != null && !parent.exists() && !parent.mkdirs()) { + Log.e(LOG_TAG, "Creating parent directories failed for path: " + file.getAbsolutePath()); + } + + if (!file.createNewFile()) { + Log.e(LOG_TAG, "File already exists: " + file.getAbsolutePath()); + } + + return true; + } catch (Exception e) { + Log.e(LOG_TAG, "Error while creating file: " + file.getAbsolutePath(), e); + } + + return false; + } + + public static boolean createDirectoryWithSubdirectories(@NonNull File directory) { + try { + if (!directory.exists() && !directory.mkdirs()) { + Log.e(LOG_TAG, "Creating directory failed for path: " + directory.getAbsolutePath()); + } + return true; + } catch (Exception e) { + Log.e(LOG_TAG, "Error while creating directory: " + directory.getAbsolutePath(), e); + } + + return false; + } + + public static void deleteFiles(File[] files) { + for (File file : files) { + deleteFile(file); + } + } + + public static void deleteFile(File file) { + if (file.exists()) { + if (!file.delete()) { + Log.w(LOG_TAG, "Delete file failed: " + file.getAbsolutePath()); + } + } else { + Log.w(LOG_TAG, "File does not exists: " + file.getAbsolutePath()); + } + } + + public static void writeToFile(@NonNull File file, @NonNull InputStream is) { + try (OutputStream os = new FileOutputStream(file)) { + StreamUtils.bufferedStreamCopy(is, os); + } catch (IOException e) { + Log.e(LOG_TAG, "Error while writing to file: " + file.getAbsolutePath(), e); + } + } + + public static File[] getFilesStartingWith(@NonNull File directory, @NonNull String filenamePrefix) { + if (!directory.exists()) { + android.util.Log.e(LOG_TAG, "Directory does not exists: " + directory); + return null; + } + + if (!directory.isDirectory()) { + android.util.Log.e(LOG_TAG, "Provided path is not a directory: " + directory); + return null; + } + + return directory.listFiles((dir, filename) -> filename.startsWith(filenamePrefix)); + } + + public static File getNewestFileStartingWith(@NonNull File directory, @NonNull String filenamePrefix) { + File[] files = getFilesStartingWith(directory, filenamePrefix); + if (files == null || files.length == 0) { + return null; + } + Arrays.sort(files, (f1, f2) -> Long.compare(f1.lastModified(), f2.lastModified())); + return files[files.length - 1]; + } + + public static String readFileAsUTF8String(@NonNull File file) { + try { + InputStream is = new FileInputStream(file); + return StreamUtils.readInputStreamAsUTF8String(is); + } catch (IOException e) { + Log.e(LOG_TAG, "Opening input stream failed for file: " + file.getAbsolutePath(), e); + } + return null; + } + + public static File createOrCheckDirectory(File directory) { + if (directory.exists() || directory.mkdirs()) { + return directory; + } else { + return null; + } + } + + public static int getFileCount(File directory) { + if (!directory.exists() || !directory.isDirectory()) { + Log.e(LOG_TAG, "Not a directory: " + directory.getAbsolutePath()); + return -1; + } + + File[] files = directory.listFiles(File::isFile); + if (files != null) { + return files.length; + } else { + Log.e(LOG_TAG, "Counting files failed: " + directory.getAbsolutePath()); + return -1; + } + } + + /** + * Create a temporary file inside app's cache directory under specified path. This file should be + * manually deleted, but the cache directory can be emptied by the user or by the Android to save + * storage space. If the file exists, it will be deleted and recreated. + * + * @param targetPath Path to the temporary file inside cache, the cache root will be appended + * @return Created temp file + */ + public static File createTempFile(@NonNull Context context, @NonNull String targetPath) { + return createTempFile(context, targetPath, false); + } + + /** + * Create a temporary file inside app's cache directory under specified path. This file should be + * manually deleted, but the cache directory can be emptied by the user or by the Android to save + * storage space. If the file exists, it will be deleted and recreated. + * + * @param targetPath Path to the temporary file inside cache, the cache root will be appended + * @param preparePathOly Set to true if the abstract path should point to non-existing file + * (i.e. to forward the file object to a library to create the file) + * @return Created temp file + */ + public static File createTempFile(@NonNull Context context, @NonNull String targetPath, boolean preparePathOly) { + File tempFile = new File(context.getCacheDir(), targetPath); + + if (tempFile.exists() && tempFile.delete()) { + org.openimis.imispolicies.tools.Log.i(LOG_TAG, "Leftover temp file deleted: " + tempFile.getAbsolutePath()); + } + + if (preparePathOly) { + File parentFile = tempFile.getParentFile(); + if (parentFile != null) { + return FileUtils.createDirectoryWithSubdirectories(parentFile) ? tempFile : null; + } else { + return null; + } + } else { + return FileUtils.createFileWithSubdirectories(tempFile) ? tempFile : null; + } + } + + /** + * Convenience method for removing a temp file form specified path inside cache directory created + * by createTempFile(String). The file can also be safely deleted with returned File object. + * + * @param targetPath Path to the temporary file inside cache, the cache root will be appended + * @return true if file under targetPath does not exist or was successfully deleted, false if file + * exists and delete failed + */ + public static boolean removeTempFile(@NonNull Context context, @NonNull String targetPath) { + File tempFile = new File(context.getCacheDir(), targetPath); + return !tempFile.exists() || tempFile.delete(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/openimis/imispolicies/util/JsonUtils.java b/app/src/main/java/org/openimis/imispolicies/util/JsonUtils.java new file mode 100644 index 00000000..aab05b94 --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/util/JsonUtils.java @@ -0,0 +1,33 @@ +package org.openimis.imispolicies.util; + +import android.support.annotation.NonNull; + +import org.json.JSONException; +import org.json.JSONObject; + +public class JsonUtils { + /** + * @param object Json object + * @param field field to be checked + * @return if the field does not exists, is null or empty + */ + public static boolean isStringEmpty(JSONObject object, @NonNull String field) { + return isStringEmpty(object, field, false); + } + + /** + * @param object Json object + * @param field field to be checked + * @param checkNullString Should "null" string be considered empty + * @return if the field does not exists, is null or empty + */ + public static boolean isStringEmpty(JSONObject object, @NonNull String field, boolean checkNullString) { + try { + return object == null + || !object.has(field) + || StringUtils.isEmpty(object.getString(field), checkNullString); + } catch (JSONException e) { + return true; + } + } +} diff --git a/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java b/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java new file mode 100644 index 00000000..3abd6d74 --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java @@ -0,0 +1,60 @@ +package org.openimis.imispolicies.util; + +import android.support.annotation.NonNull; + +import org.openimis.imispolicies.tools.Log; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +public class StreamUtils { + private static final int buffSize = 8192; + private static final String LOG_TAG = "STREAMUTIL"; + + /** + * Only use this method if you can be sure the content of the input stream + * can be read, is a UTF-8 text and will fit in memory (as String). + * + * @param is input stream to be read + * @return content of the input stream or null if IO exception occurs + */ + public static String readInputStreamAsUTF8String(@NonNull InputStream is) { + try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + StringBuilder stringBuilder = null; + String inputStr; + while ((inputStr = streamReader.readLine()) != null) { + if(stringBuilder != null) { + stringBuilder.append("\n"); + stringBuilder.append(inputStr); + } else { + stringBuilder = new StringBuilder(inputStr); + } + } + return stringBuilder != null? stringBuilder.toString(): null; + } catch (IOException e) { + Log.e(LOG_TAG, "Error while reading input stream", e); + } + + return null; + } + + /** + * Copy input stream to output stream using a byte buffer (8kb by default) + * + * @param is source stream + * @param os target stream + * @throws IOException thrown on read/write error + */ + public static void bufferedStreamCopy(@NonNull InputStream is, @NonNull OutputStream os) throws IOException { + byte[] buffer = new byte[buffSize]; + + int read; + while ((read = is.read(buffer)) >= 0) { + os.write(buffer, 0, read); + } + } + } \ No newline at end of file diff --git a/app/src/main/java/org/openimis/imispolicies/util/StringUtils.java b/app/src/main/java/org/openimis/imispolicies/util/StringUtils.java new file mode 100644 index 00000000..e22b948e --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/util/StringUtils.java @@ -0,0 +1,31 @@ +package org.openimis.imispolicies.util; + +import android.text.TextUtils; + +public class StringUtils { + /** + * @param string String to be checked + * @return is string null or empty + */ + public static boolean isEmpty(CharSequence string) { + return isEmpty(string, false); + } + + /** + * @param string String to be checked + * @param checkNullString Should "null" string be considered empty (case insensitive) + * @return is string null or empty + */ + public static boolean isEmpty(CharSequence string, boolean checkNullString) { + return string == null + || string.equals("") + || (string.toString().equalsIgnoreCase("null") && checkNullString); + } + + /** + * Null-safe equality check (true if both are null) + */ + public static boolean equals(CharSequence s1, CharSequence s2) { + return TextUtils.equals(s1, s2); + } +} diff --git a/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java b/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java new file mode 100644 index 00000000..657daad2 --- /dev/null +++ b/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java @@ -0,0 +1,127 @@ +package org.openimis.imispolicies.util; + +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.provider.OpenableColumns; +import android.support.annotation.NonNull; +import android.support.v4.content.FileProvider; + +import org.openimis.imispolicies.BuildConfig; +import org.openimis.imispolicies.tools.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class UriUtils { + private static final String LOG_TAG = "URIUTIL"; + public static final String FILE_PROVIDER_NAME = String.format("%s.fileprovider", BuildConfig.APPLICATION_ID); + + public static String getDisplayName(@NonNull Context context, @NonNull Uri uri) { + try (Cursor c = context.getContentResolver().query(uri, new String[]{OpenableColumns.DISPLAY_NAME}, null, null, null)) { + c.moveToFirst(); + c.moveToFirst(); + return c.getString(c.getColumnIndex(OpenableColumns.DISPLAY_NAME)); + } catch (Exception e) { + Log.e(LOG_TAG, "Reading file name from URI failed", e); + } + return null; + } + + + public static void writeToUri(@NonNull Context context, @NonNull Uri uri, @NonNull InputStream is) { + try (OutputStream os = context.getContentResolver().openOutputStream(uri, "w")) { + StreamUtils.bufferedStreamCopy(is, os); + } catch (IOException e) { + Log.e(LOG_TAG, "Writing to uri failed: " + uri, e); + } + } + + /** + * Copy byte content of the file to location described by the uri. The uri must be openable and + * allow writing. The file must be openable and readable. + * + * @param file File to be read + * @param uri Uri pointing to writable location + */ + public static void copyFileToUri(@NonNull Context context, @NonNull File file, @NonNull Uri uri) { + try (InputStream is = new FileInputStream(file)) { + writeToUri(context, uri, is); + } catch (IOException e) { + android.util.Log.e(LOG_TAG, "Error while opening streams", e); + } + } + + /** + * Copy byte content of the file to location described by the uri. The uri must be openable and + * allow writing. The file must be openable and readable. + * + * @param filePath Path to file to be read + * @param uri Uri pointing to writable location + */ + public static void copyFileToUri(@NonNull Context context, @NonNull String filePath, @NonNull Uri uri) { + copyFileToUri(context, new File(filePath), uri); + } + + /** + * Copy the content of the provided uri to provided file. The file location must allow creating + * a file and writing to a file. + * + * @param uri Uri of the file to copy. The Uri should allow read and should be openable + * @param targetFile File to be used as output stream + * @return File object pointing to created file + */ + public static File copyUriContentToFile(@NonNull Context context, @NonNull Uri uri, @NonNull File targetFile) { + if (!targetFile.exists()) { + FileUtils.createFileWithSubdirectories(targetFile); + } + try (InputStream is = context.getContentResolver().openInputStream(uri)) { + FileUtils.writeToFile(targetFile, is); + } catch (IOException e) { + android.util.Log.e(LOG_TAG, "Error while opening streams", e); + } + + return targetFile; + } + + + /** + * Create a content URI for a file owned by the app. The directory path have to be specified in + * res/xml/paths.xml to be eligible to provide content. Uri returned by this method can be shared + * with other apps with intents. + * + * @param file File to be specified by generated content URI + * @return Content URI for a given file + */ + public static Uri createUriForFile(@NonNull Context context, @NonNull File file) { + Uri uri = FileProvider.getUriForFile(context, FILE_PROVIDER_NAME, file); + if (uri == null) { + org.openimis.imispolicies.tools.Log.w(LOG_TAG, "Failed to create temp photo URI"); + } + return uri; + } + + /** + * Copy the content of the provided uri to cache directory under provided path. The caller is + * responsible to delete the file after use. The file can be deleted by Android or the user + * without notifying the app. + * + * @param uri Uri of the file to copy. The Uri should allow read and should be openable + * @param targetPath Path inside cache directory to save the file (cache location will be appended) + * @return File object pointing to created file + */ + public static File copyUriContentToCache(@NonNull Context context, @NonNull Uri uri, @NonNull String targetPath) { + File outputFile = FileUtils.createTempFile(context, targetPath, false); + if (outputFile != null) { + return copyUriContentToFile(context, uri, outputFile); + } else { + Log.e(LOG_TAG, "Creating temp file failed for: " + targetPath); + return null; + } + + + } +} \ No newline at end of file diff --git a/app/src/main/java/org/openimis/imispolicies/tools/Compressor.java b/app/src/main/java/org/openimis/imispolicies/util/ZipUtils.java similarity index 56% rename from app/src/main/java/org/openimis/imispolicies/tools/Compressor.java rename to app/src/main/java/org/openimis/imispolicies/util/ZipUtils.java index 06008a18..2619b343 100644 --- a/app/src/main/java/org/openimis/imispolicies/tools/Compressor.java +++ b/app/src/main/java/org/openimis/imispolicies/util/ZipUtils.java @@ -1,15 +1,39 @@ -package org.openimis.imispolicies.tools; +package org.openimis.imispolicies.util; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.util.Zip4jConstants; +import org.openimis.imispolicies.tools.Log; + import java.io.File; import java.util.ArrayList; +import java.util.Collections; + +public class ZipUtils { + private static final String LOG_TAG = "ZIPUTILS"; + + public static File zipDirectories(File outputFile, String zipPassword, File... directories) { + ArrayList filesToAdd = new ArrayList<>(); + for (File directory : directories) { + if (!directory.exists() || !directory.isDirectory()) { + Log.w(LOG_TAG, "Provided file is not a directory: " + directory); + continue; + } + + File[] files = directory.listFiles(); + if (files == null) { + Log.w(LOG_TAG, "Reading a directory filed: " + directory); + continue; + } + + Collections.addAll(filesToAdd, files); + } + + return zipFiles(filesToAdd, outputFile, zipPassword); + } -public class Compressor { - private static final String LOG_TAG = "COMPRESSOR"; - public static File zip(ArrayList filesToAdd, File destinationFile, String password) { + public static File zipFiles(ArrayList filesToAdd, File destinationFile, String password) { try { ZipParameters parameters = new ZipParameters(); parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); @@ -31,7 +55,7 @@ public static File zip(ArrayList filesToAdd, File destinationFile, String return null; } - public static void zip(String targetPath, String destinationFilePath, String password) { + public static void zipPath(String targetPath, String destinationFilePath, String password) { try { ZipParameters parameters = new ZipParameters(); parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); @@ -57,7 +81,20 @@ public static void zip(String targetPath, String destinationFilePath, String pas } } - public static void unzip(String targetZipFilePath, String destinationFolderPath, String password) { + public static void unzipFile(File targetZipFile, File destinationFolder, String password) { + try { + ZipFile zipFile = new ZipFile(targetZipFile); + if (zipFile.isEncrypted()) { + zipFile.setPassword(password); + } + zipFile.extractAll(destinationFolder.getPath()); + + } catch (Exception e) { + Log.e(LOG_TAG, "Error while decompressing", e); + } + } + + public static void unzipPath(String targetZipFilePath, String destinationFolderPath, String password) { try { ZipFile zipFile = new ZipFile(targetZipFilePath); if (zipFile.isEncrypted()) { @@ -66,7 +103,7 @@ public static void unzip(String targetZipFilePath, String destinationFolderPath, zipFile.extractAll(destinationFolderPath); } catch (Exception e) { - Log.e(LOG_TAG, "Error while compressing", e); + Log.e(LOG_TAG, "Error while decompressing", e); } } } diff --git a/app/src/main/res/xml/paths.xml b/app/src/main/res/xml/paths.xml index 3acd7ab6..f6e484ca 100644 --- a/app/src/main/res/xml/paths.xml +++ b/app/src/main/res/xml/paths.xml @@ -1,4 +1,5 @@ + diff --git a/build.gradle b/build.gradle index adff27eb..920f19f1 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ buildscript { } } dependencies { - classpath 'com.android.tools.build:gradle:7.0.3' + classpath 'com.android.tools.build:gradle:7.1.3' classpath "org.ajoberstar.grgit:grgit-gradle:4.1.0" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a734ceb6..6ab5cdd7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip From 416d666e485bfa97a8be632767d8780b7cf37116 Mon Sep 17 00:00:00 2001 From: malinowskikam Date: Fri, 20 May 2022 11:06:12 +0200 Subject: [PATCH 2/3] OP-768 Removed getMainDirectory method --- .../org/openimis/imispolicies/Global.java | 12 --- .../imispolicies/util/AndroidUtils.java | 82 +++++++++---------- .../openimis/imispolicies/util/FileUtils.java | 2 +- .../imispolicies/util/StreamUtils.java | 82 +++++++++---------- .../openimis/imispolicies/util/UriUtils.java | 2 +- 5 files changed, 84 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/org/openimis/imispolicies/Global.java b/app/src/main/java/org/openimis/imispolicies/Global.java index 0dccd566..11b44b4a 100644 --- a/app/src/main/java/org/openimis/imispolicies/Global.java +++ b/app/src/main/java/org/openimis/imispolicies/Global.java @@ -224,18 +224,6 @@ private String createOrCheckDirectory(String path) { } } - public String getMainDirectory() { - if (MainDirectory == null || "".equals(MainDirectory)) { - String documentsDir = createOrCheckDirectory(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).toString()); - MainDirectory = createOrCheckDirectory(documentsDir + File.separator + APP_DIR); - - if ("".equals(documentsDir) || "".equals(MainDirectory)) { - Log.w(FILE_IO_LOG_TAG, "Main directory could not be created"); - } - } - return MainDirectory; - } - public String getAppDirectory() { if (AppDirectory == null || "".equals(AppDirectory)) { AppDirectory = createOrCheckDirectory(getApplicationInfo().dataDir + File.separator); diff --git a/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java b/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java index c407911c..ffa73f97 100644 --- a/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java +++ b/app/src/main/java/org/openimis/imispolicies/util/AndroidUtils.java @@ -9,44 +9,44 @@ import org.openimis.imispolicies.R; public class AndroidUtils { - public static ProgressDialog showProgressDialog(Context context, int titleResId, int messageResId) { - return ProgressDialog.show( - context, - context.getResources().getString(titleResId), - context.getResources().getString(messageResId) - ); - } - - public static void showToast(Context context, int messageResId) { - Toast.makeText(context, messageResId, Toast.LENGTH_SHORT).show(); - } - - public static void showToast(Context context, CharSequence message) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); - } - - public static AlertDialog showDialog(Context context, int messageResId) { - return new AlertDialog.Builder(context) - .setMessage(messageResId) - .setCancelable(false) - .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { - }).show(); - } - - public static AlertDialog showDialog(Context context, CharSequence message) { - return new AlertDialog.Builder(context) - .setMessage(message) - .setCancelable(false) - .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { - }).show(); - } - - public static AlertDialog showConfirmDialog(Context context, int messageResId, DialogInterface.OnClickListener onPositive) { - return new AlertDialog.Builder(context) - .setMessage(messageResId) - .setCancelable(false) - .setPositiveButton(R.string.Ok, onPositive) - .setNegativeButton(R.string.Cancel, (dialogInterface, i) -> { - }).show(); - } - } \ No newline at end of file + public static ProgressDialog showProgressDialog(Context context, int titleResId, int messageResId) { + return ProgressDialog.show( + context, + context.getResources().getString(titleResId), + context.getResources().getString(messageResId) + ); + } + + public static void showToast(Context context, int messageResId) { + Toast.makeText(context, messageResId, Toast.LENGTH_SHORT).show(); + } + + public static void showToast(Context context, CharSequence message) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); + } + + public static AlertDialog showDialog(Context context, int messageResId) { + return new AlertDialog.Builder(context) + .setMessage(messageResId) + .setCancelable(false) + .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { + }).show(); + } + + public static AlertDialog showDialog(Context context, CharSequence message) { + return new AlertDialog.Builder(context) + .setMessage(message) + .setCancelable(false) + .setPositiveButton(R.string.Ok, (dialogInterface, i) -> { + }).show(); + } + + public static AlertDialog showConfirmDialog(Context context, int messageResId, DialogInterface.OnClickListener onPositive) { + return new AlertDialog.Builder(context) + .setMessage(messageResId) + .setCancelable(false) + .setPositiveButton(R.string.Ok, onPositive) + .setNegativeButton(R.string.Cancel, (dialogInterface, i) -> { + }).show(); + } +} diff --git a/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java b/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java index fcd2a57d..0dcb3d97 100644 --- a/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java +++ b/app/src/main/java/org/openimis/imispolicies/util/FileUtils.java @@ -201,4 +201,4 @@ public static boolean removeTempFile(@NonNull Context context, @NonNull String t File tempFile = new File(context.getCacheDir(), targetPath); return !tempFile.exists() || tempFile.delete(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java b/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java index 3abd6d74..623b3b48 100644 --- a/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java +++ b/app/src/main/java/org/openimis/imispolicies/util/StreamUtils.java @@ -12,49 +12,49 @@ import java.nio.charset.StandardCharsets; public class StreamUtils { - private static final int buffSize = 8192; - private static final String LOG_TAG = "STREAMUTIL"; - - /** - * Only use this method if you can be sure the content of the input stream - * can be read, is a UTF-8 text and will fit in memory (as String). - * - * @param is input stream to be read - * @return content of the input stream or null if IO exception occurs - */ - public static String readInputStreamAsUTF8String(@NonNull InputStream is) { - try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { - StringBuilder stringBuilder = null; - String inputStr; - while ((inputStr = streamReader.readLine()) != null) { - if(stringBuilder != null) { - stringBuilder.append("\n"); - stringBuilder.append(inputStr); - } else { - stringBuilder = new StringBuilder(inputStr); - } + private static final int buffSize = 8192; + private static final String LOG_TAG = "STREAMUTIL"; + + /** + * Only use this method if you can be sure the content of the input stream + * can be read, is a UTF-8 text and will fit in memory (as String). + * + * @param is input stream to be read + * @return content of the input stream or null if IO exception occurs + */ + public static String readInputStreamAsUTF8String(@NonNull InputStream is) { + try (BufferedReader streamReader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { + StringBuilder stringBuilder = null; + String inputStr; + while ((inputStr = streamReader.readLine()) != null) { + if (stringBuilder != null) { + stringBuilder.append("\n"); + stringBuilder.append(inputStr); + } else { + stringBuilder = new StringBuilder(inputStr); } - return stringBuilder != null? stringBuilder.toString(): null; - } catch (IOException e) { - Log.e(LOG_TAG, "Error while reading input stream", e); } - - return null; + return stringBuilder != null ? stringBuilder.toString() : null; + } catch (IOException e) { + Log.e(LOG_TAG, "Error while reading input stream", e); } - /** - * Copy input stream to output stream using a byte buffer (8kb by default) - * - * @param is source stream - * @param os target stream - * @throws IOException thrown on read/write error - */ - public static void bufferedStreamCopy(@NonNull InputStream is, @NonNull OutputStream os) throws IOException { - byte[] buffer = new byte[buffSize]; - - int read; - while ((read = is.read(buffer)) >= 0) { - os.write(buffer, 0, read); - } + return null; + } + + /** + * Copy input stream to output stream using a byte buffer (8kb by default) + * + * @param is source stream + * @param os target stream + * @throws IOException thrown on read/write error + */ + public static void bufferedStreamCopy(@NonNull InputStream is, @NonNull OutputStream os) throws IOException { + byte[] buffer = new byte[buffSize]; + + int read; + while ((read = is.read(buffer)) >= 0) { + os.write(buffer, 0, read); } - } \ No newline at end of file + } +} diff --git a/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java b/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java index 657daad2..6f138fb7 100644 --- a/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java +++ b/app/src/main/java/org/openimis/imispolicies/util/UriUtils.java @@ -124,4 +124,4 @@ public static File copyUriContentToCache(@NonNull Context context, @NonNull Uri } -} \ No newline at end of file +} From 619a8f2666dfa08b7674022ba07a0f6bcfb1f3e7 Mon Sep 17 00:00:00 2001 From: malinowskikam Date: Fri, 20 May 2022 11:59:52 +0200 Subject: [PATCH 3/3] OP-768 Bumped target SDK to 32 (Android 12) --- app/build.gradle | 6 ++--- app/src/main/AndroidManifest.xml | 45 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a04c7082..23acf4aa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -20,7 +20,7 @@ static def getDate() { } android { - compileSdkVersion 30 + compileSdkVersion 32 buildToolsVersion '30.0.3' if ( keystorePropertiesFile.exists() ) { signingConfigs { @@ -34,8 +34,8 @@ android { } defaultConfig { applicationId "org.openimis.imispolicies" - minSdkVersion 21 - targetSdkVersion 30 + minSdkVersion 19 + targetSdkVersion 32 versionCode gitVersionCode versionName gitVersionName testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 689251bf..8e669e9e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -64,6 +64,7 @@ @@ -133,6 +134,50 @@ + + + + + + + + + + + + \ No newline at end of file