diff --git a/app/src/main/assets/models.jar b/app/src/main/assets/models.jar index e33fdc4..095e7a2 100644 Binary files a/app/src/main/assets/models.jar and b/app/src/main/assets/models.jar differ diff --git a/app/src/main/java/org/worldbank/transport/driver/activities/RecordListActivity.java b/app/src/main/java/org/worldbank/transport/driver/activities/RecordListActivity.java index a98c17f..cb9c65a 100644 --- a/app/src/main/java/org/worldbank/transport/driver/activities/RecordListActivity.java +++ b/app/src/main/java/org/worldbank/transport/driver/activities/RecordListActivity.java @@ -1,14 +1,18 @@ package org.worldbank.transport.driver.activities; +import android.Manifest; import android.app.Dialog; import android.content.DialogInterface; import android.content.Intent; +import android.content.pm.PackageManager; import android.database.Cursor; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.FloatingActionButton; +import android.support.v4.app.ActivityCompat; import android.support.v4.app.DialogFragment; +import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; @@ -51,6 +55,7 @@ public class RecordListActivity extends AppCompatActivity implements CheckSchema PostRecordsTask.PostRecordsListener, UpdateSchemaTask.UpdateSchemaCallbackListener { private static final String LOG_LABEL = "RecordListActivity"; + private static final int WRITE_EXTERNAL_STORAGE_CODE = 1; private static final DateFormat sourceDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.US); @@ -172,8 +177,25 @@ public boolean onItemLongClick(AdapterView parent, View view, int position, l return true; } }); + + if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_EXTERNAL_STORAGE_CODE); + } } + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == WRITE_EXTERNAL_STORAGE_CODE) { + if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + showToast(getString(R.string.storage_permission_granted), false); + } else { + showToast(getString(R.string.storage_permission_not_granted), true); + } + } + } + + @Override protected void onPostResume() { Log.d(LOG_LABEL, "in onPostResume for record list; refresh list"); diff --git a/app/src/main/java/org/worldbank/transport/driver/controls/DriverImageController.java b/app/src/main/java/org/worldbank/transport/driver/controls/DriverImageController.java index 577eb4e..919d720 100644 --- a/app/src/main/java/org/worldbank/transport/driver/controls/DriverImageController.java +++ b/app/src/main/java/org/worldbank/transport/driver/controls/DriverImageController.java @@ -1,17 +1,35 @@ package org.worldbank.transport.driver.controls; import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.media.ExifInterface; +import android.media.ThumbnailUtils; +import android.os.Environment; +import android.util.Log; +import android.widget.ImageView; import com.azavea.androidvalidatedforms.controllers.ImageController; +import com.azavea.androidvalidatedforms.tasks.ResizeImageTask; import org.jsonschema2pojo.media.SerializableMedia; +import org.worldbank.transport.driver.R; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; /** * Subclass image controller to get and set path on holder class used for Gson serialization. * * Created by kathrynkillebrew on 2/4/16. */ public class DriverImageController extends ImageController { + public DriverImageController(Context ctx, String name, String labelText, boolean isRequired) { super(ctx, name, labelText, isRequired); } @@ -23,6 +41,11 @@ protected Object getModelValue() { if (current != null && current.getClass().equals(SerializableMedia.class)) { return ((SerializableMedia)current).path; } + Bitmap placeholderBitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.placeholder); + final int thumbSize = 64; + Bitmap thumbImage = ThumbnailUtils.extractThumbnail(placeholderBitmap, thumbSize, thumbSize); + File placeholderFile = storeImage(thumbImage); + setModelValue(placeholderFile.getAbsolutePath()); return null; } @@ -32,10 +55,129 @@ protected void setModelValue(String newImagePath) { SerializableMedia media = null; if (newImagePath != null && !newImagePath.isEmpty()) { + // Using custom resize Function + Bitmap bitmap = resizeBitMapImage(newImagePath, 768, 768); + Bitmap rotatedBitmap = rotateImage(bitmap, newImagePath); + File rescaledImageFile = storeImage(rotatedBitmap); media = new SerializableMedia(); - media.path = newImagePath; + media.path = rescaledImageFile.getAbsolutePath(); } getModel().setValue(getName(), media); } + + private Bitmap rotateImage(Bitmap bitmap, String path) { + try { + ExifInterface exif = new ExifInterface(path); + int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); + Matrix matrix = new Matrix(); + switch (orientation) { + case ExifInterface.ORIENTATION_FLIP_HORIZONTAL: + matrix.setScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_180: + matrix.setRotate(180); + break; + case ExifInterface.ORIENTATION_FLIP_VERTICAL: + matrix.setRotate(180); + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_TRANSPOSE: + matrix.setRotate(90); + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_90: + matrix.setRotate(90); + break; + case ExifInterface.ORIENTATION_TRANSVERSE: + matrix.setRotate(-90); + matrix.postScale(-1, 1); + break; + case ExifInterface.ORIENTATION_ROTATE_270: + matrix.setRotate(-90); + break; + default: + // no rotation needed + return bitmap; + } + + // rotate image + Bitmap oriented = Bitmap.createBitmap(bitmap, 0, 0, + bitmap.getWidth(), bitmap.getHeight(), matrix, true); + bitmap.recycle(); + return oriented; + + } catch (IOException e) { + Log.e("TAG", "Failed to get EXIF information to rotate image"); + e.printStackTrace(); + return bitmap; + } catch (OutOfMemoryError e) { + Log.e("TAG", "Ran out of memory rotating image! Need to downscale further?"); + e.printStackTrace(); + return bitmap; + } + } + + + private Bitmap resizeBitMapImage(String filePath, int targetWidth, int targetHeight) { + Bitmap outputImage = null; + BitmapFactory.Options options = new BitmapFactory.Options(); + outputImage = BitmapFactory.decodeFile(filePath, options); + int width = outputImage.getWidth(); + int height = outputImage.getHeight(); + if(width<= targetWidth && height<=targetHeight){ + return outputImage; + } + if (width > height) { // LANDSCAPE IMAGE + Float widthRatio = (float)width/(float)targetWidth; + targetHeight = (int)(height/widthRatio); + } else { // PORTRAIT IMAGE + Float heightRatio = ((float)height/(float)targetHeight); + targetWidth = (int)(width/heightRatio); + } + outputImage = Bitmap.createScaledBitmap(outputImage, targetWidth, targetHeight, true); + return outputImage; + } + + private File storeImage(Bitmap image) { + File pictureFile = getOutputMediaFile(); + if (pictureFile == null) { + Log.d("TAG", + "Error creating media file, check storage permissions: ");// e.getMessage()); + } + try { + FileOutputStream fos = new FileOutputStream(pictureFile); + image.compress(Bitmap.CompressFormat.JPEG, 90, fos); + fos.close(); + } catch (FileNotFoundException e) { + Log.e("TAG", "File not found: " + e.getMessage()); + } catch (IOException e) { + Log.e("TAG", "Error accessing file: " + e.getMessage()); + } + return pictureFile; + } + + /** Create a File for saving an image or video */ + private File getOutputMediaFile(){ + String appName = getContext().getApplicationInfo().loadLabel(getContext().getPackageManager()).toString(); + + File picturePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + + // This location works best if you want the created images to be shared + // between applications and persist after your app has been uninstalled. + File mediaStorageDir = new File(picturePath, appName); + + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + Log.e("TAG", "failed to create directory"); + return null; + } + } + + String timeStamp = new SimpleDateFormat("ddMMyyyy_HHmm").format(new Date()); + File mediaFile; + String mImageName="MI_"+ timeStamp +".jpg"; + mediaFile = new File(mediaStorageDir.getPath() + File.separator + mImageName); + return mediaFile; + } } diff --git a/app/src/main/java/org/worldbank/transport/driver/datastore/RecordDatabaseManager.java b/app/src/main/java/org/worldbank/transport/driver/datastore/RecordDatabaseManager.java index 8687b17..701087a 100644 --- a/app/src/main/java/org/worldbank/transport/driver/datastore/RecordDatabaseManager.java +++ b/app/src/main/java/org/worldbank/transport/driver/datastore/RecordDatabaseManager.java @@ -74,7 +74,7 @@ public RecordDatabaseManager(Context context, boolean amTesting) { writableDb = dbHelper.getWritableDatabase(); readableDb = dbHelper.getReadableDatabase(); - storeDateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + storeDateFormat.setTimeZone(TimeZone.getDefault()); } /** diff --git a/app/src/main/java/org/worldbank/transport/driver/staticmodels/DriverConstantFields.java b/app/src/main/java/org/worldbank/transport/driver/staticmodels/DriverConstantFields.java index 8854e53..42a5754 100644 --- a/app/src/main/java/org/worldbank/transport/driver/staticmodels/DriverConstantFields.java +++ b/app/src/main/java/org/worldbank/transport/driver/staticmodels/DriverConstantFields.java @@ -63,19 +63,10 @@ public class DriverConstantFields { public enum WeatherEnum { - CLEAR_DAY("clear-day"), - CLEAR_NIGHT("clear-night"), - CLOUDY("cloudy"), - NIGHT("fog"), - HAIL("hail"), - PARTLY_CLOUDY_DAY("partly-cloudy-day"), - PARTLY_CLOUDY_NIGHT("partly-cloudy-night"), - RAIN("rain"), - SLEET("sleet"), - SNOW("snow"), - THUNDERSTORM("thunderstorm"), - TORNADO("tornado"), - WIND("wind"); + FAIR("Fair"), + RAIN("Rain"), + WIND("Wind"), + FOG("Fog"); private final String value; private final static Map CONSTANTS = new HashMap<>(); @@ -107,10 +98,10 @@ public static DriverConstantFields.WeatherEnum fromValue(String value) { public enum LightEnum { - DAWN("dawn"), - DAY("day"), - DUSK("dusk"), - NIGHT("night"); + DAY("Day"), + DAWN_DUSK("Dawn_dusk"), + NIGHT_LIT("Night_lit"), + NIGHT_UNLIT("Night_unlit"); private final String value; private final static Map CONSTANTS = new HashMap<>(); diff --git a/app/src/main/java/org/worldbank/transport/driver/tasks/PostRecordsTask.java b/app/src/main/java/org/worldbank/transport/driver/tasks/PostRecordsTask.java index 7b1c29a..41ab342 100644 --- a/app/src/main/java/org/worldbank/transport/driver/tasks/PostRecordsTask.java +++ b/app/src/main/java/org/worldbank/transport/driver/tasks/PostRecordsTask.java @@ -3,6 +3,7 @@ import android.content.Context; import android.database.Cursor; import android.os.AsyncTask; +import android.os.Environment; import android.util.Log; import com.google.gson.Gson; @@ -24,6 +25,7 @@ import java.io.BufferedOutputStream; import java.io.BufferedWriter; +import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -32,6 +34,8 @@ import java.net.URL; import java.util.Set; +import static org.worldbank.transport.driver.staticmodels.DriverApp.getContext; + /** * Upload records to server, then delete them from the local database. * @@ -70,7 +74,7 @@ public PostRecordsTask(PostRecordsListener listener, DriverUserInfo userInfo) { this(listener, userInfo, new UploadRecordUrlBuilder(), DriverApp.getDatabaseManager()); } - // Invoke this constructor directly in test. + // Invoke this constructor directly in test. public PostRecordsTask(PostRecordsListener listener, DriverUserInfo userInfo, UploadRecordUrl uploadRecordUrl, RecordDatabaseManager databaseManager) { @@ -273,6 +277,22 @@ protected void onPostExecute(Integer failed) { if (caller != null) { caller.recordUploadFinished(failed); } + // Delete the data images should all records be successfully uploaded + if (failed == 0) { + String appName = getContext().getApplicationInfo().loadLabel(getContext().getPackageManager()).toString(); + File picturePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); + File mediaStorageDir = new File(picturePath, appName); + if (mediaStorageDir.isDirectory()) + { + String[] children = mediaStorageDir.list(); + for (String child : children) { + File file = new File(mediaStorageDir, child); + if (file.getName().contains("MI_")) { + file.delete(); + } + } + } + } } @Override diff --git a/app/src/main/res/drawable-mdpi/placeholder.jpg b/app/src/main/res/drawable-mdpi/placeholder.jpg new file mode 100644 index 0000000..46a9cdf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/placeholder.jpg differ diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index b4591c0..680926a 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -104,23 +104,18 @@ الإضاءه - يوم صافي - ليله صافيه - غائم - ضباب - وابل - نهار شبه غائم - ليلة شبه غائمة - مطر - مطر ثلجي - ثلج - عاصفة رعدية - عاصفة - رياح + Fair + Rain + Wind + Fog - الفجر - نهار - الغسق - ليل + Day + Dawn/Dusk + Night (lit) + Night (unlit) + + + Storage Permission Granted + Storage Permission Not Granted! Please enable storage permission from settings diff --git a/app/src/main/res/values-bn/strings.xml b/app/src/main/res/values-bn/strings.xml new file mode 100644 index 0000000..ad019a2 --- /dev/null +++ b/app/src/main/res/values-bn/strings.xml @@ -0,0 +1,124 @@ + + + ড্রাইভার + + + আপলোড রেকর্ড + স্কিমা আপডেট করুন + প্রস্থান + + + সাইন ইন করুন + ব্যবহারকারীর নাম + পাসওয়ার্ড + সাইন ইন করুন + সাইন ইন করুন + এই ব্যবহারকারীর নামটি অবৈধ৷ + এই পাসওয়ার্ড খুব ছোট + এই পাসওয়ার্ডটি ভুল + ঘরটি অবশ্যই পূরণ করতে হবে + ব্যবহারকারী নাম বা পাসওয়ার্ড ভুল. + লগ ইন করার সময় সার্ভারের ত্রুটি৷ অনুগ্রহ করে অন্য সময়ে আবার চেষ্টা করুন৷ + ব্যবহারকারীর অ্যাকাউন্টে রেকর্ড যোগ করার অ্যাক্সেস নেই। লেখার অ্যাক্সেস পেতে অনুগ্রহ করে একজন সিস্টেম প্রশাসকের সাথে যোগাযোগ করুন। + লগ ইন করার সময় অজানা ত্রুটি৷ + লগ ইন করার সময় নেটওয়ার্ক ত্রুটি৷ + + + আপনি লগ আউট করতে চান? + লগ আউট করার পরে রেকর্ড প্রবেশ বা পরিচালনা করতে আপনাকে আবার লগ ইন করতে হবে। লগ ইন করার জন্য ইন্টারনেট অ্যাক্সেস প্রয়োজন হবে। আপনি কি নিশ্চিত আপনি এখন লগ আউট করতে চান? + + + নেটওয়ার্ক উপলব্ধ নেই. + + + স্কিমা সংস্করণ পরীক্ষা করা সার্ভার ত্রুটি. অন্য সময়ে আবার চেষ্টা করুন. + বর্তমান স্কিমা আপ-টু-ডেট। + স্কিমা আপডেট করা হচ্ছে না। নতুন স্কিমা উপলব্ধ, কিন্তু সমস্ত রেকর্ড আপলোড বা মুছে না হওয়া পর্যন্ত ব্যবহার করা যাবে না। + + + স্কিমা আপডেট করার সময় ত্রুটির সম্মুখীন হয়েছে৷ অন্য সময়ে আবার চেষ্টা করুন. + স্কিমা আপডেট এখনও প্রস্তুত নয়। কয়েক মিনিটের মধ্যে আবার আপডেট করার চেষ্টা করুন. + স্কিমা আপডেট করা হয়েছে। + + + সার্ভার ত্রুটি রেকর্ড আপলোড. + রেকর্ড আপলোড করা হয়েছে, এখন স্কিমা আপডেটের জন্য পরীক্ষা করা হচ্ছে… + %1$d রেকর্ড আপলোড করতে ব্যর্থ হয়েছে৷ + আপলোড করার জন্য কোন রেকর্ড পাওয়া যায়নি। + + + রেকর্ড মুছে ফেলা হয়েছে। + রেকর্ড মুছে ফেলতে ব্যর্থ! + রেকর্ড মুছবেন? + আপনি কি নিশ্চিত আপনি এই রেকর্ড মুছে ফেলতে চান? + + + সংরক্ষণ + পরবর্তী + পেছনে + মুছে ফেলা + সংরক্ষণ + রেকর্ড মুছুন + + + রেকর্ড ফর্ম আইটেম ছবি + কোনো আইটেম এখনো যোগ করা হয়নি. একটি আইটেম যোগ করতে রাউন্ড বোতামে আলতো চাপুন। + + + একক রেকর্ড আপলোড করুন + আপনি কি এখন এই রেকর্ড আপলোড করার বিষয়ে নিশ্চিত? + ডিভাইসে কোনো রেকর্ড নেই। একটি রেকর্ড যোগ করতে বৃত্তাকার বোতামটি আলতো চাপুন। + + + রেকর্ড সংরক্ষিত + রেকর্ড সংরক্ষণ করতে ব্যর্থ! + আইটেম মুছে ফেলা হয়েছে + আইটেম মুছে ফেলতে ব্যর্থ! + এগিয়ে যাওয়ার আগে ইনপুট সংশোধন করুন + + + রেকর্ড সংরক্ষণ করবেন? + প্রস্থান করার আগে রেকর্ডে পরিবর্তনগুলি সংরক্ষণ করবেন? + + + ডিভাইস অবস্থান অনুমতি প্রয়োজন. অবস্থান ছাড়া রেকর্ড প্রবেশ করা যাবে না. + রেকর্ডের জন্য একটি সঠিক অবস্থান পেতে GPS সক্রিয় করা আবশ্যক। + ডিভাইসে জিপিএস নেই, যা অবস্থান পেতে প্রয়োজন। একটি ভিন্ন ডিভাইস ব্যবহার করুন. + জিপিএস ফিক্স পাওয়া গেছে। অবস্থান পাওয়া যাচ্ছে… + অবস্থান পাওয়া গেছে। + GPS ফিক্সের জন্য অপেক্ষা করা হচ্ছে… + লোকেশন চেক চলছে না। + + + অবস্থান নির্ধারণ করা হয়নি! + অবস্থান ছাড়া রেকর্ড আপলোড করা যাবে না। জিপিএস পড়ার জন্য অপেক্ষা করতে চান? + অবস্থান এখনও আপডেট করা হচ্ছে + বর্তমানে লোকেশন রিডিং উন্নত করা হচ্ছে। এটি 20 সেকেন্ডের কম সময় নেবে। অপেক্ষা করা চালিয়ে যাবেন? + + হ্যাঁ + না + + + ঘটেছে + অবস্থান + আবহাওয়া + আলো + + + + ভাল + বৃষ্টি + ঝড় + কুয়াশা + + + দিন + ভোর/সন্ধ্যা + আলোকিত সড়ক (রাতে) + অনালোকিত সড়ক (রাতে) + + + স্টোরেজ অনুমতি দেওয়া হয়েছে + স্টোরেজ অনুমতি দেওয়া হয়নি! সেটিংস থেকে স্টোরেজ অনুমতি সক্ষম করুন + + diff --git a/app/src/main/res/values-lo/strings.xml b/app/src/main/res/values-lo/strings.xml index 40813cd..9f0cd07 100644 --- a/app/src/main/res/values-lo/strings.xml +++ b/app/src/main/res/values-lo/strings.xml @@ -70,21 +70,20 @@ ສະຖານທີ່ຕັ້ງ ສະພາບອາກາດ ແສງໄຟ - ອາກາດຕອນກາງເວັນປອດໂປ່ງ - ອາກາດຕອນກາງຄືນປອດໂປ່ງ - ມີເມກ - ໝອກ - ໝາກເຫັບ - ອາກາດກາງເວັນມີໝອກບາງສ່ວນ - ອາກາດກາງຄືນມີໝອກບາງສ່ວນ - ຝົນ - ນ້ຳຄ້າງ - ຫິມມະ - ຟ້າລົມຝົນຕົກ - ພາຍຸໝູນ/ພາຍຸໂທນາໂດ - ລົມ - ຕອນເຊົ້າ - ຕອນກາງເວັນ - ຕອນແລງ - ຕອນກາງຄືນ + + + Fair + Rain + Wind + Fog + + + Day + Dawn/Dusk + Night (lit) + Night (unlit) + + + Storage Permission Granted + Storage Permission Not Granted! Please enable storage permission from settings \ No newline at end of file diff --git a/app/src/main/res/values-th/strings.xml b/app/src/main/res/values-th/strings.xml index 3e89958..52a3fa4 100644 --- a/app/src/main/res/values-th/strings.xml +++ b/app/src/main/res/values-th/strings.xml @@ -70,21 +70,20 @@ ที่ตั้ง สภาพอากาศ เบา - ท้องฟ้าแจ่มใส - กลางวัน - ท้องฟ้าแจ่มใส - กลางคืน - มีเมฆมาก - หมอก - ลูกเห็บ -  มีเมฆมากบางส่วน - กลางวัน -  มีเมฆมากบางส่วน - กลางคืน -  ฝน -  หิมะฝน -  หิมะ -  ฝนฟ้าคะนอง -  พายุทอร์นาโด -  ลม - รุ่งอรุณ - กลางวัน - พลบค่ำ - กลางคืน + + + Fair + Rain + Wind + Fog + + + Day + Dawn/Dusk + Night (lit) + Night (unlit) + + + Storage Permission Granted + Storage Permission Not Granted! Please enable storage permission from settings \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 97cc4d8..2613773 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -114,23 +114,18 @@ Light - Clear day - Clear night - Cloudy - Fog - Hail - Partly cloudy day - Partly cloudy night - Rain - Sleet - Snow - Thunderstorm - Tornado - Wind + Fair + Rain + Wind + Fog - Dawn - Day - Dusk - Night + Day + Dawn/Dusk + Night (lit) + Night (unlit) + + + Storage Permission Granted + Storage Permission Not Granted! Please enable storage permission from settings