From 0290bc9a2d9b831befb2c654b1886a49a3632239 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 20 Apr 2019 17:38:08 +0100 Subject: [PATCH 01/27] Fixes issue with exporting plant --- .../main/java/me/anon/lib/helper/ExportHelper.java | 12 +++++++++++- .../main/java/me/anon/lib/helper/StatsHelper.java | 3 ++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/me/anon/lib/helper/ExportHelper.java b/app/src/main/java/me/anon/lib/helper/ExportHelper.java index 732024e9..3a395b7e 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -31,7 +31,10 @@ import java.io.OutputStream; import java.text.DateFormat; import java.util.Date; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import java.util.SortedMap; import me.anon.grow.R; @@ -100,6 +103,7 @@ public class ExportHelper long waterDifference = 0L; long lastWater = 0L; int totalWater = 0, totalFlush = 0; + final Set additiveNames = new HashSet<>(); for (Action action : plant.getActions()) { @@ -118,6 +122,12 @@ public class ExportHelper waterDifference += Math.abs(action.getDate() - lastWater); } + List actionAdditives = ((Water)action).getAdditives(); + for (Additive additive : actionAdditives) + { + additiveNames.add(additive.getDescription()); + } + totalWater++; lastWater = action.getDate(); } @@ -362,7 +372,7 @@ else if (action instanceof StageChange) additives.measure(widthMeasureSpec, heightMeasureSpec); additives.requestLayout(); additives.layout(0, 0, width, height); - StatsHelper.setAdditiveData(plant, additives, null); + StatsHelper.setAdditiveData(plant, additives, additiveNames); additives.getData().setDrawValues(true); try diff --git a/app/src/main/java/me/anon/lib/helper/StatsHelper.java b/app/src/main/java/me/anon/lib/helper/StatsHelper.java index 34a2bca9..b4474cc5 100644 --- a/app/src/main/java/me/anon/lib/helper/StatsHelper.java +++ b/app/src/main/java/me/anon/lib/helper/StatsHelper.java @@ -1,6 +1,7 @@ package me.anon.lib.helper; import android.graphics.Color; +import android.support.annotation.NonNull; import android.support.v4.graphics.ColorUtils; import android.support.v4.util.Pair; import android.widget.TextView; @@ -114,7 +115,7 @@ public static void styleDataset(LineDataSet data, int colour) data.setValueFormatter(formatter); } - public static void setAdditiveData(Plant plant, LineChart chart, Set checkedAdditives) + public static void setAdditiveData(Plant plant, LineChart chart, @NonNull Set checkedAdditives) { ArrayList actions = plant.getActions(); ArrayList>> vals = new ArrayList<>(); From a2b62136f34c4c20585ac43cdd1452ed51aef9fb Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 20 Apr 2019 17:40:50 +0100 Subject: [PATCH 02/27] Adds additive names to grow log export --- app/src/main/java/me/anon/lib/helper/ExportHelper.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/src/main/java/me/anon/lib/helper/ExportHelper.java b/app/src/main/java/me/anon/lib/helper/ExportHelper.java index 3a395b7e..99c89bc4 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -208,6 +208,14 @@ public class ExportHelper plantDetails.append(" - *Average input temperature*: ").append(aveTemp[2]); plantDetails.append(NEW_LINE); + if (!additiveNames.isEmpty()) + { + plantDetails.append("##Additives used"); + plantDetails.append(NEW_LINE); + TextUtils.join(NEW_LINE + " - ", additiveNames); + plantDetails.append(NEW_LINE); + } + plantDetails.append("##Timeline"); plantDetails.append(NEW_LINE); From b9e964689d246a3b98769d51e06a239c044628a4 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 20 Apr 2019 19:21:06 +0100 Subject: [PATCH 03/27] Fixes issue with previous feed dialog not being sorted and tidies up UI --- .../fragment/ActionSelectDialogFragment.java | 7 ++++++- .../anon/grow/fragment/PlantListFragment.java | 1 + .../me/anon/grow/fragment/WateringFragment.java | 17 ++++++++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java b/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java index ac18d56b..a18fa29c 100644 --- a/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java @@ -19,6 +19,7 @@ import me.anon.grow.R; import me.anon.lib.Views; import me.anon.model.Action; +import me.anon.view.ActionHolder; import me.anon.view.ImageActionHolder; /** @@ -58,6 +59,11 @@ public ActionSelectDialogFragment(ArrayList actions) super.onBindViewHolder(vh, index); int padding = (int)getResources().getDimension(R.dimen.padding_8dp); vh.itemView.setPadding(padding, padding, padding, padding); + + if (vh instanceof ActionHolder) + { + ((ActionHolder)vh).getCard().setCardBackgroundColor(0xffffff); + } } }; @@ -66,7 +72,6 @@ public ActionSelectDialogFragment(ArrayList actions) adapter.setActions(null, actions, exclude); } - @SuppressLint("ValidFragment") public ActionSelectDialogFragment() { diff --git a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java index 2638ec50..c6bf2bea 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java @@ -321,6 +321,7 @@ private synchronized void saveCurrentState() { if (resultCode != Activity.RESULT_CANCELED) { + adapter.notifyDataSetChanged(); SnackBar.show(getActivity(), "Watering added", new SnackBarListener() { @Override public void onSnackBarStarted(Object o) diff --git a/app/src/main/java/me/anon/grow/fragment/WateringFragment.java b/app/src/main/java/me/anon/grow/fragment/WateringFragment.java index d6604aa2..a585b69d 100644 --- a/app/src/main/java/me/anon/grow/fragment/WateringFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/WateringFragment.java @@ -29,6 +29,8 @@ import java.text.DateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; import java.util.Date; import java.util.GregorianCalendar; @@ -217,12 +219,24 @@ else if (item.getItemId() == R.id.action_populate_previous) } } + Collections.sort(items, new Comparator() + { + @Override + public int compare(Action o1, Action o2) + { + if (o1.getDate() < o2.getDate()) return 1; + if (o1.getDate() > o2.getDate()) return -1; + return 0; + } + }); + ActionSelectDialogFragment actionSelectDialogFragment = new ActionSelectDialogFragment(items); actionSelectDialogFragment.setOnActionSelectedListener(new ActionSelectDialogFragment.OnActionSelectedListener() { @Override public void onActionSelected(Action action) { water = (Water)new Kryo().copy(action); + water.setDate(System.currentTimeMillis()); setUi(); } }); @@ -409,7 +423,8 @@ private void setUi() } }); - if (plants.size() == 1) +// if (plants.size() == 1) + if (water != null) { if (water.getPh() != null) { From c2332f4c13710f7dac8e2045704e0d20b1060a9e Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 20 Apr 2019 19:25:31 +0100 Subject: [PATCH 04/27] Sets strain as optional --- .../me/anon/grow/fragment/PlantDetailsFragment.java | 6 ------ app/src/main/java/me/anon/model/Plant.java | 10 +++++++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java index 6edd47b3..e1b0a4f1 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -928,7 +928,6 @@ else if (item.getItemId() == R.id.export) public void save() { name.setError(null); - strain.setError(null); if (!TextUtils.isEmpty(name.getText())) { @@ -944,11 +943,6 @@ public void save() { plant.setStrain(strain.getText().toString().trim()); } - else - { - strain.setError("strain can not be empty"); - return; - } plant.setMediumDetails(mediumDetails.getText().toString()); diff --git a/app/src/main/java/me/anon/model/Plant.java b/app/src/main/java/me/anon/model/Plant.java index 084bca6f..70eef4da 100644 --- a/app/src/main/java/me/anon/model/Plant.java +++ b/app/src/main/java/me/anon/model/Plant.java @@ -28,11 +28,11 @@ public class Plant { private String id = UUID.randomUUID().toString(); private String name; - private String strain; + private String strain = null; private long plantDate = System.currentTimeMillis(); private boolean clone = false; private PlantMedium medium = PlantMedium.SOIL; - private String mediumDetails; + private String mediumDetails = null; private ArrayList images = new ArrayList<>(); private ArrayList actions = new ArrayList<>(); @@ -216,7 +216,11 @@ public String generateLongSummary(Context context) Unit deliveryUnit = Unit.getSelectedDeliveryUnit(context); String summary = ""; - summary += getStrain() + " - "; + + if (getStrain() != null) + { + summary += getStrain() + " - "; + } if (getStage() == PlantStage.HARVESTED) { From 3d57e87acd27f6061d8b3e0fc771d70581413479 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 20 Apr 2019 19:34:19 +0100 Subject: [PATCH 05/27] Disable sound for export notification and adds big text style --- .../java/me/anon/grow/fragment/PlantDetailsFragment.java | 3 +++ app/src/main/java/me/anon/lib/helper/ExportHelper.java | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java index e1b0a4f1..2696d40e 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -770,6 +770,9 @@ else if (item.getItemId() == R.id.export) .setTicker("Export of " + plant.getName() + " complete") .setContentTitle("Export Complete") .setContentIntent(PendingIntent.getActivity(getActivity(), 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText("Exported " + plant.getName() + " to " + file.getAbsolutePath()) + ) .setSmallIcon(R.drawable.ic_stat_done) .setPriority(NotificationCompat.PRIORITY_HIGH) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) diff --git a/app/src/main/java/me/anon/lib/helper/ExportHelper.java b/app/src/main/java/me/anon/lib/helper/ExportHelper.java index 99c89bc4..791fee2b 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; +import android.graphics.Color; import android.os.AsyncTask; import android.os.Build; import android.os.Environment; @@ -518,6 +519,10 @@ protected static void copyImagesAndFinish(Context context, final Plant plant, fi if (Build.VERSION.SDK_INT >= 26) { NotificationChannel channel = new NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(null, null); + channel.enableLights(false); + channel.setLightColor(Color.BLUE); + channel.enableVibration(false); notificationManager.createNotificationChannel(channel); } @@ -537,6 +542,7 @@ protected static void copyImagesAndFinish(Context context, final Plant plant, fi @Override protected void onProgressUpdate(Integer... values) { + exportNotification.setPriority(NotificationCompat.PRIORITY_LOW); exportNotification.setProgress(values[1], values[0], false); notificationManager.notify(plantIndex, exportNotification.build()); } From 73174bb687499231583cf899e60dc2e9ed7fe266 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 20 Apr 2019 20:23:51 +0100 Subject: [PATCH 06/27] Adds multiple plant export functionality --- .../grow/fragment/PlantDetailsFragment.java | 90 +- .../anon/grow/fragment/PlantListFragment.java | 120 ++- .../java/me/anon/lib/helper/ExportHelper.java | 820 +++++++++--------- app/src/main/res/menu/plant_list_menu.xml | 7 + 4 files changed, 537 insertions(+), 500 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java index 2696d40e..ea6bdf88 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -1,26 +1,16 @@ package me.anon.grow.fragment; import android.Manifest; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Fragment; -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.ProgressDialog; +import android.app.*; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; +import android.graphics.Color; import android.media.MediaScannerConnection; import android.net.Uri; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.ParcelFileDescriptor; +import android.os.*; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.support.annotation.Nullable; @@ -29,52 +19,12 @@ import android.support.v7.widget.CardView; import android.text.Html; import android.text.TextUtils; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.widget.Button; -import android.widget.CheckBox; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.Toast; - +import android.view.*; +import android.widget.*; import com.esotericsoftware.kryo.Kryo; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.DateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.Locale; -import java.util.UUID; - import me.anon.controller.provider.PlantWidgetProvider; -import me.anon.grow.AddWateringActivity; -import me.anon.grow.BuildConfig; -import me.anon.grow.EditWateringActivity; -import me.anon.grow.EventsActivity; -import me.anon.grow.MainApplication; -import me.anon.grow.PlantDetailsActivity; -import me.anon.grow.R; -import me.anon.grow.StatisticsActivity; -import me.anon.grow.ViewPhotosActivity; -import me.anon.lib.DateRenderer; -import me.anon.lib.ExportCallback; -import me.anon.lib.SnackBar; -import me.anon.lib.SnackBarListener; -import me.anon.lib.Views; +import me.anon.grow.*; +import me.anon.lib.*; import me.anon.lib.helper.AddonHelper; import me.anon.lib.helper.ExportHelper; import me.anon.lib.helper.FabAnimator; @@ -83,13 +33,11 @@ import me.anon.lib.manager.PlantManager; import me.anon.lib.task.AsyncCallback; import me.anon.lib.task.EncryptTask; -import me.anon.model.EmptyAction; -import me.anon.model.NoteAction; -import me.anon.model.Plant; -import me.anon.model.PlantMedium; -import me.anon.model.PlantStage; -import me.anon.model.StageChange; -import me.anon.model.Water; +import me.anon.model.*; + +import java.io.*; +import java.text.DateFormat; +import java.util.*; /** * // TODO: Add class description @@ -735,10 +683,14 @@ else if (item.getItemId() == R.id.export) if (Build.VERSION.SDK_INT >= 26) { NotificationChannel channel = new NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(null, null); + channel.enableLights(false); + channel.setLightColor(Color.BLUE); + channel.enableVibration(false); notificationManager.createNotificationChannel(channel); } - notificationManager.cancel(plantIndex); + notificationManager.cancel(0); Notification exportNotification = new NotificationCompat.Builder(getActivity(), "export") .setContentText("Exporting grow log for " + plant.getName()) @@ -749,16 +701,16 @@ else if (item.getItemId() == R.id.export) .setSmallIcon(R.drawable.ic_stat_name) .build(); - notificationManager.notify(plantIndex, exportNotification); + notificationManager.notify(0, exportNotification); - ExportHelper.exportPlant(getActivity(), plant, new ExportCallback() + ExportHelper.exportPlants(getActivity(), new ArrayList(Arrays.asList(plant)), plant.getName().replaceAll("[^a-zA-Z0-9]+", "-"), new ExportCallback() { @Override public void onCallback(Context context, File file) { if (file != null && file.exists() && getActivity() != null) { NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(plantIndex); + notificationManager.cancel(0); Intent openIntent = new Intent(Intent.ACTION_VIEW); Uri apkURI = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", file); @@ -778,7 +730,7 @@ else if (item.getItemId() == R.id.export) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setAutoCancel(true) .build(); - notificationManager.notify(plantIndex, finishNotification); + notificationManager.notify(0, finishNotification); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { diff --git a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java index c6bf2bea..a0869a12 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java @@ -1,52 +1,45 @@ package me.anon.grow.fragment; -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Fragment; +import android.app.*; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.Color; import android.graphics.Rect; +import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; +import android.support.v4.app.NotificationCompat; +import android.support.v4.content.FileProvider; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.text.Html; import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - +import android.view.*; +import android.widget.Toast; import com.esotericsoftware.kryo.Kryo; - -import java.util.ArrayList; -import java.util.Arrays; - import me.anon.controller.adapter.PlantAdapter; import me.anon.controller.adapter.SimpleItemTouchHelperCallback; -import me.anon.grow.AddPlantActivity; -import me.anon.grow.AddWateringActivity; -import me.anon.grow.MainActivity; -import me.anon.grow.MainApplication; -import me.anon.grow.R; +import me.anon.grow.*; +import me.anon.lib.ExportCallback; import me.anon.lib.SnackBar; import me.anon.lib.SnackBarListener; import me.anon.lib.Views; import me.anon.lib.event.GardenChangeEvent; import me.anon.lib.helper.BusHelper; +import me.anon.lib.helper.ExportHelper; import me.anon.lib.helper.FabAnimator; import me.anon.lib.manager.GardenManager; import me.anon.lib.manager.PlantManager; -import me.anon.model.EmptyAction; -import me.anon.model.Garden; -import me.anon.model.NoteAction; -import me.anon.model.Plant; -import me.anon.model.PlantStage; +import me.anon.model.*; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; /** * // TODO: Add class description @@ -357,6 +350,7 @@ private synchronized void saveCurrentState() if (garden != null) { + menu.findItem(R.id.export_garden).setVisible(true); menu.findItem(R.id.edit_garden).setVisible(true); menu.findItem(R.id.delete_garden).setVisible(true); } @@ -393,6 +387,36 @@ private synchronized void saveCurrentState() return true; } + else if (item.getItemId() == R.id.export_garden) + { + Toast.makeText(getActivity(), "Exporting grow log of garden...", Toast.LENGTH_SHORT).show(); + NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); + + if (Build.VERSION.SDK_INT >= 26) + { + NotificationChannel channel = new NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_DEFAULT); + channel.setSound(null, null); + channel.enableLights(false); + channel.setLightColor(Color.BLUE); + channel.enableVibration(false); + + notificationManager.createNotificationChannel(channel); + } + + NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(getActivity(), "export"); + notifBuilder + .setContentText("Exporting garden grow log") + .setContentTitle("Exporting") + .setContentIntent(PendingIntent.getActivity(getActivity(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT)) + .setTicker("Exporting garden grow log") + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setSmallIcon(R.drawable.ic_stat_name) + .build(); + + notificationManager.notify(0, notifBuilder.build()); + ArrayList export = new ArrayList<>(adapter.getPlants()); + exportPlants(export); + } else if (item.getItemId() == R.id.delete_garden) { new AlertDialog.Builder(getActivity()) @@ -474,6 +498,52 @@ else if (item.getItemId() == R.id.delete_garden) return super.onOptionsItemSelected(item); } + private void exportPlants(final ArrayList plants) + { + final Garden currentGarden = garden; + + ExportHelper.exportPlants(getActivity(), plants, garden.getName().replaceAll("[^a-zA-Z0-9]+", "-"), new ExportCallback() + { + @Override public void onCallback(Context context, File file) + { + if (file != null && file.exists() && getActivity() != null) + { + NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancel(0); + + Intent openIntent = new Intent(Intent.ACTION_VIEW); + Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file); + openIntent.setDataAndType(apkURI, "application/zip"); + openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + + Notification finishNotification = new NotificationCompat.Builder(context, "export") + .setContentText("Exported " + currentGarden.getName() + " to " + file.getAbsolutePath()) + .setTicker("Export of " + currentGarden.getName() + " complete") + .setContentTitle("Export Complete") + .setContentIntent(PendingIntent.getActivity(context, 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) + .setStyle(new NotificationCompat.BigTextStyle() + .bigText("Exported " + currentGarden.getName() + " to " + file.getAbsolutePath()) + ) + .setSmallIcon(R.drawable.ic_stat_done) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setAutoCancel(true) + .build(); + notificationManager.notify(0, finishNotification); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + new PlantDetailsFragment.MediaScannerWrapper(context, file.getParent(), "application/zip").scan(); + } + else + { + context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file))); + } + } + } + }); + } + private void filter() { ArrayList plantList = PlantManager.getInstance().getSortedPlantList(garden); @@ -488,7 +558,7 @@ private void filter() } } - if ((garden == null && plants.size() < plantList.size()) || garden != null) + if (garden != null || plants.size() < plantList.size()) { adapter.setShowOnly(plants); } diff --git a/app/src/main/java/me/anon/lib/helper/ExportHelper.java b/app/src/main/java/me/anon/lib/helper/ExportHelper.java index 791fee2b..38d34e93 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -17,39 +17,20 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; - import com.github.mikephil.charting.charts.LineChart; - +import me.anon.grow.R; +import me.anon.lib.ExportCallback; +import me.anon.lib.Unit; +import me.anon.lib.manager.PlantManager; +import me.anon.model.*; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.util.Zip4jConstants; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.FileWriter; -import java.io.OutputStream; +import java.io.*; import java.text.DateFormat; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedMap; - -import me.anon.grow.R; -import me.anon.lib.ExportCallback; -import me.anon.lib.Unit; -import me.anon.lib.manager.PlantManager; -import me.anon.model.Action; -import me.anon.model.Additive; -import me.anon.model.EmptyAction; -import me.anon.model.NoteAction; -import me.anon.model.Plant; -import me.anon.model.PlantStage; -import me.anon.model.StageChange; -import me.anon.model.Water; +import java.util.*; import static me.anon.lib.Unit.ML; @@ -62,14 +43,8 @@ public class ExportHelper /** * Creates a grow log for given plant and zips them into a compressed file - * - * @param context - * @param plant - * @param callback - * - * @return */ - @Nullable public static void exportPlant(final Context context, @NonNull final Plant plant, final ExportCallback callback) + @Nullable public static void exportPlants(final Context context, @NonNull final ArrayList plants, String name, final ExportCallback callback) { String folderPath = ""; Unit measureUnit = Unit.getSelectedMeasurementUnit(context); @@ -85,416 +60,428 @@ public class ExportHelper folderPath = Environment.DIRECTORY_DOWNLOADS; } + ArrayList outputs = new ArrayList<>(); File exportFolder = new File(folderPath + "/GrowLogs/"); exportFolder.mkdirs(); - // temp folder to write to - final File tempFolder = new File(exportFolder.getAbsolutePath() + "/" + plant.getId()); + final File finalFile = new File(exportFolder.getAbsolutePath() + "/" + name + ".zip"); - if (tempFolder.exists()) + if (finalFile.exists()) { - deleteRecursive(tempFolder); + finalFile.delete(); } - tempFolder.mkdir(); - - long startDate = plant.getPlantDate(); - long endDate = System.currentTimeMillis(); - long feedDifference = 0L; - long waterDifference = 0L; - long lastWater = 0L; - int totalWater = 0, totalFlush = 0; - final Set additiveNames = new HashSet<>(); - - for (Action action : plant.getActions()) + try { - if (action instanceof StageChange) - { - if (((StageChange)action).getNewStage() == PlantStage.HARVESTED) - { - endDate = action.getDate(); - } - } + final ZipFile outFile = new ZipFile(finalFile); + final ZipParameters params = new ZipParameters(); + params.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + params.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); - if (action.getClass() == Water.class) + for (Plant plant : plants) { - if (lastWater != 0) + // temp folder to write to + final File tempFolder = new File(exportFolder.getAbsolutePath() + "/" + plant.getId()); + outputs.add(tempFolder); + + if (tempFolder.exists()) { - waterDifference += Math.abs(action.getDate() - lastWater); + deleteRecursive(tempFolder); } - List actionAdditives = ((Water)action).getAdditives(); - for (Additive additive : actionAdditives) + tempFolder.mkdir(); + + long startDate = plant.getPlantDate(); + long endDate = System.currentTimeMillis(); + long feedDifference = 0L; + long waterDifference = 0L; + long lastWater = 0L; + int totalWater = 0, totalFlush = 0; + final Set additiveNames = new HashSet<>(); + + for (Action action : plant.getActions()) { - additiveNames.add(additive.getDescription()); - } + if (action instanceof StageChange) + { + if (((StageChange)action).getNewStage() == PlantStage.HARVESTED) + { + endDate = action.getDate(); + } + } - totalWater++; - lastWater = action.getDate(); - } + if (action.getClass() == Water.class) + { + if (lastWater != 0) + { + waterDifference += Math.abs(action.getDate() - lastWater); + } - if (action instanceof EmptyAction && ((EmptyAction)action).getAction() == Action.ActionName.FLUSH) - { - totalFlush++; - } - } + List actionAdditives = ((Water)action).getAdditives(); + for (Additive additive : actionAdditives) + { + additiveNames.add(additive.getDescription()); + } - long seconds = ((endDate - startDate) / 1000); - double days = (double)seconds * 0.0000115741d; + totalWater++; + lastWater = action.getDate(); + } - StringBuffer plantDetails = new StringBuffer(1000); - plantDetails.append("#").append(plant.getName()).append(" Grow Log"); - plantDetails.append(NEW_LINE); - plantDetails.append("*Strain*: ").append(plant.getStrain()); - plantDetails.append(NEW_LINE); - plantDetails.append("*Is clone?*: ").append(plant.isClone()); - plantDetails.append(NEW_LINE); - plantDetails.append("*Medium*: ").append(plant.getMedium().getPrintString()); - plantDetails.append(NEW_LINE); + if (action instanceof EmptyAction && ((EmptyAction)action).getAction() == Action.ActionName.FLUSH) + { + totalFlush++; + } + } - plantDetails.append("##Stages"); - plantDetails.append(NEW_LINE); + long seconds = ((endDate - startDate) / 1000); + double days = (double)seconds * 0.0000115741d; - SortedMap stages = plant.calculateStageTime(); - Map plantStages = plant.getStages(); + StringBuffer plantDetails = new StringBuffer(1000); + plantDetails.append("#").append(plant.getName()).append(" Grow Log"); + plantDetails.append(NEW_LINE); + plantDetails.append("*Strain*: ").append(plant.getStrain()); + plantDetails.append(NEW_LINE); + plantDetails.append("*Is clone?*: ").append(plant.isClone()); + plantDetails.append(NEW_LINE); + plantDetails.append("*Medium*: ").append(plant.getMedium().getPrintString()); + plantDetails.append(NEW_LINE); - for (PlantStage plantStage : stages.keySet()) - { - plantDetails.append("- *").append(plantStage.getPrintString()).append("*: "); - plantDetails.append(printableDate(context, plantStages.get(plantStage).getDate())); + plantDetails.append("##Stages"); + plantDetails.append(NEW_LINE); - if (plantStage != PlantStage.PLANTED && plantStage != PlantStage.HARVESTED) - { - plantDetails.append(" (").append((int)TimeHelper.toDays(stages.get(plantStage))).append(" days)"); - } + SortedMap stages = plant.calculateStageTime(); + Map plantStages = plant.getStages(); - plantDetails.append(NEW_LINE); - } + for (PlantStage plantStage : stages.keySet()) + { + plantDetails.append("- *").append(plantStage.getPrintString()).append("*: "); + plantDetails.append(printableDate(context, plantStages.get(plantStage).getDate())); - plantDetails.append("##General stats"); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Total grow time*: ").append(String.format("%1$,.2f days", days)); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Total waters*: ").append(String.valueOf(totalWater)); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Total flushes*: ").append(String.valueOf(totalFlush)); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Average time between waterings*: ").append(String.format("%1$,.2f days", (TimeHelper.toDays(waterDifference) / (double)totalWater))); - plantDetails.append(NEW_LINE); - - String[] avePh = new String[3]; - StatsHelper.setInputData(plant, null, avePh); - plantDetails.append(" - *Minimum input pH*: ").append(avePh[0]); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Maximum input pH*: ").append(avePh[1]); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Average input pH*: ").append(avePh[2]); - plantDetails.append(NEW_LINE); - - String[] avePpm = new String[3]; - StatsHelper.setPpmData(plant, null, avePpm, usingEc); - plantDetails.append(" - *Minimum input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[0]); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Maximum input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[1]); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Average input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[2]); - plantDetails.append(NEW_LINE); - - String[] aveTemp = new String[3]; - StatsHelper.setTempData(plant, null, aveTemp); - plantDetails.append(" - *Minimum input temperature*: ").append(aveTemp[0]); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Maximum input temperature*: ").append(aveTemp[1]); - plantDetails.append(NEW_LINE); - plantDetails.append(" - *Average input temperature*: ").append(aveTemp[2]); - plantDetails.append(NEW_LINE); - - if (!additiveNames.isEmpty()) - { - plantDetails.append("##Additives used"); - plantDetails.append(NEW_LINE); - TextUtils.join(NEW_LINE + " - ", additiveNames); - plantDetails.append(NEW_LINE); - } + if (plantStage != PlantStage.PLANTED && plantStage != PlantStage.HARVESTED) + { + plantDetails.append(" (").append((int)TimeHelper.toDays(stages.get(plantStage))).append(" days)"); + } - plantDetails.append("##Timeline"); - plantDetails.append(NEW_LINE); + plantDetails.append(NEW_LINE); + } - for (Action action : plant.getActions()) - { - plantDetails.append("###").append(printableDate(context, action.getDate())); - plantDetails.append(NEW_LINE); + plantDetails.append("##General stats"); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Total grow time*: ").append(String.format("%1$,.2f days", days)); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Total waters*: ").append(String.valueOf(totalWater)); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Total flushes*: ").append(String.valueOf(totalFlush)); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Average time between waterings*: ").append(String.format("%1$,.2f days", (TimeHelper.toDays(waterDifference) / (double)totalWater))); + plantDetails.append(NEW_LINE); - if (action.getClass() == Water.class) - { - plantDetails.append("*Type*: Watering"); + String[] avePh = new String[3]; + StatsHelper.setInputData(plant, null, avePh); + plantDetails.append(" - *Minimum input pH*: ").append(avePh[0]); plantDetails.append(NEW_LINE); - } - else if (action instanceof EmptyAction && ((EmptyAction)action).getAction() != null) - { - plantDetails.append(((EmptyAction)action).getAction().getPrintString()); + plantDetails.append(" - *Maximum input pH*: ").append(avePh[1]); plantDetails.append(NEW_LINE); - } - else if (action instanceof NoteAction) - { - plantDetails.append("*Note*"); + plantDetails.append(" - *Average input pH*: ").append(avePh[2]); plantDetails.append(NEW_LINE); - } - else if (action instanceof StageChange) - { - plantDetails.append("*Changed state to*: ").append(((StageChange)action).getNewStage().getPrintString()); + + String[] avePpm = new String[3]; + StatsHelper.setPpmData(plant, null, avePpm, usingEc); + plantDetails.append(" - *Minimum input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[0]); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Maximum input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[1]); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Average input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[2]); plantDetails.append(NEW_LINE); - } - if (Water.class.isAssignableFrom(action.getClass())) - { - boolean newLine = false; + String[] aveTemp = new String[3]; + StatsHelper.setTempData(plant, null, aveTemp); + plantDetails.append(" - *Minimum input temperature*: ").append(aveTemp[0]); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Maximum input temperature*: ").append(aveTemp[1]); + plantDetails.append(NEW_LINE); + plantDetails.append(" - *Average input temperature*: ").append(aveTemp[2]); + plantDetails.append(NEW_LINE); - if (((Water)action).getPh() != null) + if (!additiveNames.isEmpty()) { - plantDetails.append("*In pH*: "); - plantDetails.append(((Water)action).getPh()); - plantDetails.append(", "); - newLine = true; + plantDetails.append("##Additives used"); + plantDetails.append(NEW_LINE); + TextUtils.join(NEW_LINE + " - ", additiveNames); + plantDetails.append(NEW_LINE); } - if (((Water)action).getRunoff() != null) - { - plantDetails.append("*Out pH*: "); - plantDetails.append(((Water)action).getRunoff()); - plantDetails.append(", "); - newLine = true; - } + plantDetails.append("##Timeline"); + plantDetails.append(NEW_LINE); - if (((Water)action).getPpm() != null) + for (Action action : plant.getActions()) { - plantDetails.append("*PPM*: "); - plantDetails.append(((Water)action).getPpm()); - plantDetails.append(", "); - newLine = true; + plantDetails.append("###").append(printableDate(context, action.getDate())); + plantDetails.append(NEW_LINE); + + if (action.getClass() == Water.class) + { + plantDetails.append("*Type*: Watering"); + plantDetails.append(NEW_LINE); + } + else if (action instanceof EmptyAction && ((EmptyAction)action).getAction() != null) + { + plantDetails.append(((EmptyAction)action).getAction().getPrintString()); + plantDetails.append(NEW_LINE); + } + else if (action instanceof NoteAction) + { + plantDetails.append("*Note*"); + plantDetails.append(NEW_LINE); + } + else if (action instanceof StageChange) + { + plantDetails.append("*Changed state to*: ").append(((StageChange)action).getNewStage().getPrintString()); + plantDetails.append(NEW_LINE); + } + + if (Water.class.isAssignableFrom(action.getClass())) + { + boolean newLine = false; + + if (((Water)action).getPh() != null) + { + plantDetails.append("*In pH*: "); + plantDetails.append(((Water)action).getPh()); + plantDetails.append(", "); + newLine = true; + } + + if (((Water)action).getRunoff() != null) + { + plantDetails.append("*Out pH*: "); + plantDetails.append(((Water)action).getRunoff()); + plantDetails.append(", "); + newLine = true; + } + + if (((Water)action).getPpm() != null) + { + plantDetails.append("*PPM*: "); + plantDetails.append(((Water)action).getPpm()); + plantDetails.append(", "); + newLine = true; + } + + if (((Water)action).getAmount() != null) + { + plantDetails.append("*Amount*: "); + plantDetails.append(ML.to(deliveryUnit, ((Water)action).getAmount())); + plantDetails.append(deliveryUnit.getLabel()); + plantDetails.append(", "); + newLine = true; + } + + if (((Water)action).getTemp() != null) + { + plantDetails.append("*Temp*: "); + plantDetails.append(((Water)action).getTemp()); + plantDetails.append("ºC, "); + newLine = true; + } + + if (((Water)action).getAdditives().size() > 0) + { + plantDetails.append(NEW_LINE); + plantDetails.append("*Additives:*"); + plantDetails.append("\r\n"); + + for (Additive additive : ((Water)action).getAdditives()) + { + if (additive == null || additive.getAmount() == null) continue; + + double converted = ML.to(measureUnit, additive.getAmount()); + String amountStr = converted == Math.floor(converted) ? String.valueOf((int)converted) : String.valueOf(converted); + + plantDetails.append("\r\n"); + plantDetails.append(" - "); + plantDetails.append(additive.getDescription()); + plantDetails.append(" - "); + plantDetails.append(amountStr); + plantDetails.append(measureUnit.getLabel()); + plantDetails.append("/"); + plantDetails.append(deliveryUnit.getLabel()); + } + } + + if (newLine) + { + plantDetails.delete(plantDetails.length() - 2, plantDetails.length() - 1); + plantDetails.append(NEW_LINE); + } + } + + if (!TextUtils.isEmpty(action.getNotes())) + { + plantDetails.append(action.getNotes()); + plantDetails.append(NEW_LINE); + } } - if (((Water)action).getAmount() != null) + plantDetails.append("##Raw plant data"); + plantDetails.append(NEW_LINE); + plantDetails.append("```").append("\r\n").append(GsonHelper.parse(plant)).append("\r\n").append("```"); + plantDetails.append(NEW_LINE); + plantDetails.append("Generated using [Grow Tracker](https://github.com/7LPdWcaW/GrowTracker-Android)"); + + // Write the log + try { - plantDetails.append("*Amount*: "); - plantDetails.append(ML.to(deliveryUnit, ((Water)action).getAmount())); - plantDetails.append(deliveryUnit.getLabel()); - plantDetails.append(", "); - newLine = true; + FileWriter fileWriter = new FileWriter(tempFolder.getAbsolutePath() + "/growlog.md", false); + fileWriter.write(plantDetails.toString()); + fileWriter.flush(); + fileWriter.close(); } - - if (((Water)action).getTemp() != null) + catch (Exception e) { - plantDetails.append("*Temp*: "); - plantDetails.append(((Water)action).getTemp()); - plantDetails.append("ºC, "); - newLine = true; + e.printStackTrace(); } - if (((Water)action).getAdditives().size() > 0) + try { - plantDetails.append(NEW_LINE); - plantDetails.append("*Additives:*"); - plantDetails.append("\r\n"); - - for (Additive additive : ((Water)action).getAdditives()) + if (plants.size() > 0) { - if (additive == null || additive.getAmount() == null) continue; - - double converted = ML.to(measureUnit, additive.getAmount()); - String amountStr = converted == Math.floor(converted) ? String.valueOf((int)converted) : String.valueOf(converted); - - plantDetails.append("\r\n"); - plantDetails.append(" - "); - plantDetails.append(additive.getDescription()); - plantDetails.append(" - "); - plantDetails.append(amountStr); - plantDetails.append(measureUnit.getLabel()); - plantDetails.append("/"); - plantDetails.append(deliveryUnit.getLabel()); + params.setRootFolderInZip(plant.getName()); } - } - if (newLine) - { - plantDetails.delete(plantDetails.length() - 2, plantDetails.length() - 1); - plantDetails.append(NEW_LINE); - } - } + outFile.addFile(new File(tempFolder.getAbsolutePath() + "/" + plant.getName() + " - growlog.md"), params); - if (!TextUtils.isEmpty(action.getNotes())) - { - plantDetails.append(action.getNotes()); - plantDetails.append(NEW_LINE); - } - } + if (new File(tempFolder.getAbsolutePath() + "/images/").exists()) + { + outFile.addFolder(new File(tempFolder.getAbsolutePath() + "/images/"), params); + } - plantDetails.append("##Raw plant data"); - plantDetails.append(NEW_LINE); - plantDetails.append("```").append("\r\n").append(GsonHelper.parse(plant)).append("\r\n").append("```"); - plantDetails.append(NEW_LINE); - plantDetails.append("Generated using [Grow Tracker](https://github.com/7LPdWcaW/GrowTracker-Android)"); + int width = 512 + (totalWater * 10); + int height = 512; + int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + + LineChart additives = new LineChart(context); + additives.setLayoutParams(new ViewGroup.LayoutParams(width, height)); + additives.setMinimumWidth(width); + additives.setMinimumHeight(height); + additives.measure(widthMeasureSpec, heightMeasureSpec); + additives.requestLayout(); + additives.layout(0, 0, width, height); + StatsHelper.setAdditiveData(plant, additives, additiveNames); + additives.getData().setDrawValues(true); - // Write the log - try - { - FileWriter fileWriter = new FileWriter(tempFolder.getAbsolutePath() + "/growlog.md", false); - fileWriter.write(plantDetails.toString()); - fileWriter.flush(); - fileWriter.close(); - } - catch (Exception e) - { - e.printStackTrace(); - } + try + { + OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/additives.png"); + additives.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); - try - { - // Create stats charts and save images - final File finalFile = new File(exportFolder.getAbsolutePath() + "/" + plant.getName().replaceAll("[^a-zA-Z0-9]+", "-") + ".zip"); + stream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } - if (finalFile.exists()) - { - finalFile.delete(); - } + LineChart inputPh = new LineChart(context); + inputPh.setLayoutParams(new ViewGroup.LayoutParams(width, height)); + inputPh.setMinimumWidth(width); + inputPh.setMinimumHeight(height); + inputPh.measure(widthMeasureSpec, heightMeasureSpec); + inputPh.requestLayout(); + inputPh.layout(0, 0, width, height); + StatsHelper.setInputData(plant, inputPh, null); + inputPh.getData().setDrawValues(true); - final ZipFile outFile = new ZipFile(finalFile); - final ZipParameters params = new ZipParameters(); - params.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); - params.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/growlog.md"), params); + try + { + OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/input-ph.png"); + inputPh.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); - if (new File(tempFolder.getAbsolutePath() + "/images/").exists()) - { - outFile.addFolder(new File(tempFolder.getAbsolutePath() + "/images/"), params); - } + stream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } - int width = 512 + (totalWater * 10); - int height = 512; - int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); - int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); - - LineChart additives = new LineChart(context); - additives.setLayoutParams(new ViewGroup.LayoutParams(width, height)); - additives.setMinimumWidth(width); - additives.setMinimumHeight(height); - additives.measure(widthMeasureSpec, heightMeasureSpec); - additives.requestLayout(); - additives.layout(0, 0, width, height); - StatsHelper.setAdditiveData(plant, additives, additiveNames); - additives.getData().setDrawValues(true); - - try - { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/additives.png"); - additives.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + LineChart ppm = new LineChart(context); + ppm.setLayoutParams(new ViewGroup.LayoutParams(width, height)); + ppm.setMinimumWidth(width); + ppm.setMinimumHeight(height); + ppm.measure(widthMeasureSpec, heightMeasureSpec); + ppm.requestLayout(); + ppm.layout(0, 0, width, height); + StatsHelper.setPpmData(plant, ppm, null, usingEc); + ppm.getData().setDrawValues(true); - stream.close(); - } - catch (Exception e) - { - e.printStackTrace(); - } + try + { + OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + (usingEc ? "ec" : "ppm") + ".png"); + ppm.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); - LineChart inputPh = new LineChart(context); - inputPh.setLayoutParams(new ViewGroup.LayoutParams(width, height)); - inputPh.setMinimumWidth(width); - inputPh.setMinimumHeight(height); - inputPh.measure(widthMeasureSpec, heightMeasureSpec); - inputPh.requestLayout(); - inputPh.layout(0, 0, width, height); - StatsHelper.setInputData(plant, inputPh, null); - inputPh.getData().setDrawValues(true); - - try - { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/input-ph.png"); - inputPh.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + stream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } - stream.close(); - } - catch (Exception e) - { - e.printStackTrace(); - } + LineChart temp = new LineChart(context); + temp.setLayoutParams(new ViewGroup.LayoutParams(width, height)); + temp.setMinimumWidth(width); + temp.setMinimumHeight(height); + temp.measure(widthMeasureSpec, heightMeasureSpec); + temp.requestLayout(); + temp.layout(0, 0, width, height); + StatsHelper.setTempData(plant, temp, null); + temp.getData().setDrawValues(true); - LineChart ppm = new LineChart(context); - ppm.setLayoutParams(new ViewGroup.LayoutParams(width, height)); - ppm.setMinimumWidth(width); - ppm.setMinimumHeight(height); - ppm.measure(widthMeasureSpec, heightMeasureSpec); - ppm.requestLayout(); - ppm.layout(0, 0, width, height); - StatsHelper.setPpmData(plant, ppm, null, usingEc); - ppm.getData().setDrawValues(true); - - try - { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + (usingEc ? "ec" : "ppm") + ".png"); - ppm.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + try + { + OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/temp.png"); + temp.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); - stream.close(); - } - catch (Exception e) - { - e.printStackTrace(); - } + stream.close(); + } + catch (Exception e) + { + e.printStackTrace(); + } - LineChart temp = new LineChart(context); - temp.setLayoutParams(new ViewGroup.LayoutParams(width, height)); - temp.setMinimumWidth(width); - temp.setMinimumHeight(height); - temp.measure(widthMeasureSpec, heightMeasureSpec); - temp.requestLayout(); - temp.layout(0, 0, width, height); - StatsHelper.setTempData(plant, temp, null); - temp.getData().setDrawValues(true); - - try - { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/temp.png"); - temp.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + if (new File(tempFolder.getAbsolutePath() + "/additives.png").exists()) + { + outFile.addFile(new File(tempFolder.getAbsolutePath() + "/additives.png"), params); + } - stream.close(); - } - catch (Exception e) - { - e.printStackTrace(); - } + if (new File(tempFolder.getAbsolutePath() + "/input-ph.png").exists()) + { + outFile.addFile(new File(tempFolder.getAbsolutePath() + "/input-ph.png"), params); + } - try - { - if (new File(tempFolder.getAbsolutePath() + "/additives.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/additives.png"), params); - } + if (new File(tempFolder.getAbsolutePath() + "/ppm.png").exists()) + { + outFile.addFile(new File(tempFolder.getAbsolutePath() + "/ppm.png"), params); + } - if (new File(tempFolder.getAbsolutePath() + "/input-ph.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/input-ph.png"), params); - } + if (new File(tempFolder.getAbsolutePath() + "/ec.png").exists()) + { + outFile.addFile(new File(tempFolder.getAbsolutePath() + "/ec.png"), params); + } - if (new File(tempFolder.getAbsolutePath() + "/ppm.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/ppm.png"), params); + if (new File(tempFolder.getAbsolutePath() + "/temp.png").exists()) + { + outFile.addFile(new File(tempFolder.getAbsolutePath() + "/temp.png"), params); + } } - - if (new File(tempFolder.getAbsolutePath() + "/ec.png").exists()) + catch (Exception e) { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/ec.png"), params); + e.printStackTrace(); } - if (new File(tempFolder.getAbsolutePath() + "/temp.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/temp.png"), params); - } - } - catch (Exception e) - { - e.printStackTrace(); + } - copyImagesAndFinish(context, plant, tempFolder, finalFile, callback); + copyImagesAndFinish(context, plants, outputs, outFile, callback); } catch (ZipException e) { @@ -502,7 +489,7 @@ else if (action instanceof StageChange) } } - protected static void copyImagesAndFinish(Context context, final Plant plant, final File tempFolder, final File finalFile, final ExportCallback callback) + protected static void copyImagesAndFinish(Context context, final ArrayList plant, final ArrayList outputs, final ZipFile finalFile, final ExportCallback callback) { final Context appContext = context.getApplicationContext(); new AsyncTask() @@ -529,74 +516,95 @@ protected static void copyImagesAndFinish(Context context, final Plant plant, fi notificationManager = (NotificationManager)appContext.getSystemService(Context.NOTIFICATION_SERVICE); exportNotification = new NotificationCompat.Builder(appContext, "export") - .setContentText("Exporting grow log for " + plant.getName()) + .setContentText("Exporting grow log for " + (plant.size() == 1 ? plant.get(0).getName() : "multiple plants")) .setContentTitle("Exporting") .setContentIntent(PendingIntent.getActivity(appContext, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT)) - .setTicker("Exporting grow log for " + plant.getName()) + .setTicker("Exporting grow log for " + (plant.size() == 1 ? plant.get(0).getName() : "multiple plants")) .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setSmallIcon(R.drawable.ic_stat_name); - notificationManager.notify(plantIndex, exportNotification.build()); + notificationManager.notify(0, exportNotification.build()); } @Override protected void onProgressUpdate(Integer... values) { exportNotification.setPriority(NotificationCompat.PRIORITY_LOW); exportNotification.setProgress(values[1], values[0], false); - notificationManager.notify(plantIndex, exportNotification.build()); + notificationManager.notify(0, exportNotification.build()); } @Override protected File doInBackground(Plant... params) { - Plant plant = params[0]; + if (params.length != outputs.size()) + { + throw new IllegalArgumentException("plant size did not match output size"); + } - // Copy images to dir - int count = 0, total = plant.getImages().size(); - for (String filePath : plant.getImages()) + int total = 0; + for (Plant plant : plant) { - try - { - File currentImage = new File(filePath); - long fileDate = Long.parseLong(currentImage.getName().replaceAll("[^0-9]", "")); + total += plant.getImages().size(); + } + + int count = 0; + + for (int index = 0; index < params.length; index++) + { + Plant plant = params[index]; + File tempFolder = outputs.get(index); - if (fileDate == 0) + // Copy images to dir + for (String filePath : plant.getImages()) + { + try { - fileDate = currentImage.lastModified(); - } + File currentImage = new File(filePath); + long fileDate = Long.parseLong(currentImage.getName().replaceAll("[^0-9]", "")); + + if (fileDate == 0) + { + fileDate = currentImage.lastModified(); + } + + File imageFolderPath = new File(tempFolder.getAbsolutePath() + "/images/" + dateFolder(appContext, fileDate) + "/"); + imageFolderPath.mkdirs(); - File imageFolderPath = new File(tempFolder.getAbsolutePath() + "/images/" + dateFolder(appContext, fileDate) + "/"); - imageFolderPath.mkdirs(); + FileInputStream fis = new FileInputStream(currentImage); + FileOutputStream fos = new FileOutputStream(new File(imageFolderPath.getAbsolutePath() + "/" + fileDate + ".jpg")); - FileInputStream fis = new FileInputStream(currentImage); - FileOutputStream fos = new FileOutputStream(new File(imageFolderPath.getAbsolutePath() + "/" + fileDate + ".jpg")); + byte[] buffer = new byte[8192]; + int len = 0; - byte[] buffer = new byte[8192]; - int len = 0; + while ((len = fis.read(buffer)) != -1) + { + fos.write(buffer, 0, len); + } - while ((len = fis.read(buffer)) != -1) + fis.close(); + fos.flush(); + fos.close(); + + final ZipParameters zipParams = new ZipParameters(); + zipParams.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + zipParams.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL); + finalFile.addFolder(imageFolderPath, zipParams); + } + catch (Exception e) { - fos.write(buffer, 0, len); + e.printStackTrace(); } - fis.close(); - fos.flush(); - fos.close(); - } - catch (Exception e) - { - e.printStackTrace(); + publishProgress(++count, total); } - publishProgress(++count, total); + deleteRecursive(tempFolder); } - deleteRecursive(tempFolder); - callback.onCallback(appContext, finalFile); - + callback.onCallback(appContext, finalFile.getFile()); return null; } - }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, plant); + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, plant.toArray(new Plant[0])); } /** diff --git a/app/src/main/res/menu/plant_list_menu.xml b/app/src/main/res/menu/plant_list_menu.xml index b9946ea8..4ac16497 100644 --- a/app/src/main/res/menu/plant_list_menu.xml +++ b/app/src/main/res/menu/plant_list_menu.xml @@ -63,6 +63,13 @@ + + Date: Mon, 22 Apr 2019 17:05:50 +0100 Subject: [PATCH 07/27] Reworks image saving and exporting --- .../grow/fragment/PlantDetailsFragment.java | 123 +++++----- .../anon/grow/fragment/PlantListFragment.java | 85 +++---- .../java/me/anon/lib/helper/ExportHelper.java | 213 ++++++++---------- .../me/anon/lib/helper/NotificationHelper.kt | 83 +++++++ 4 files changed, 272 insertions(+), 232 deletions(-) create mode 100644 app/src/main/java/me/anon/lib/helper/NotificationHelper.kt diff --git a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java index ea6bdf88..67f37a11 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -1,43 +1,93 @@ package me.anon.grow.fragment; import android.Manifest; -import android.app.*; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Fragment; +import android.app.NotificationManager; +import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; -import android.graphics.Color; import android.media.MediaScannerConnection; import android.net.Uri; -import android.os.*; +import android.os.AsyncTask; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.ParcelFileDescriptor; import android.preference.PreferenceManager; import android.provider.MediaStore; import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; import android.support.v4.content.FileProvider; import android.support.v7.widget.CardView; import android.text.Html; import android.text.TextUtils; -import android.view.*; -import android.widget.*; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; + import com.esotericsoftware.kryo.Kryo; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.DateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; +import java.util.UUID; + import me.anon.controller.provider.PlantWidgetProvider; -import me.anon.grow.*; -import me.anon.lib.*; +import me.anon.grow.AddWateringActivity; +import me.anon.grow.BuildConfig; +import me.anon.grow.EditWateringActivity; +import me.anon.grow.EventsActivity; +import me.anon.grow.MainApplication; +import me.anon.grow.PlantDetailsActivity; +import me.anon.grow.R; +import me.anon.grow.StatisticsActivity; +import me.anon.grow.ViewPhotosActivity; +import me.anon.lib.DateRenderer; +import me.anon.lib.ExportCallback; +import me.anon.lib.SnackBar; +import me.anon.lib.SnackBarListener; +import me.anon.lib.Views; import me.anon.lib.helper.AddonHelper; import me.anon.lib.helper.ExportHelper; import me.anon.lib.helper.FabAnimator; +import me.anon.lib.helper.NotificationHelper; import me.anon.lib.helper.PermissionHelper; import me.anon.lib.manager.GardenManager; import me.anon.lib.manager.PlantManager; import me.anon.lib.task.AsyncCallback; import me.anon.lib.task.EncryptTask; -import me.anon.model.*; - -import java.io.*; -import java.text.DateFormat; -import java.util.*; +import me.anon.model.EmptyAction; +import me.anon.model.NoteAction; +import me.anon.model.Plant; +import me.anon.model.PlantMedium; +import me.anon.model.PlantStage; +import me.anon.model.StageChange; +import me.anon.model.Water; /** * // TODO: Add class description @@ -680,28 +730,8 @@ else if (item.getItemId() == R.id.export) Toast.makeText(getActivity(), "Exporting grow log...", Toast.LENGTH_SHORT).show(); NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); - if (Build.VERSION.SDK_INT >= 26) - { - NotificationChannel channel = new NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_DEFAULT); - channel.setSound(null, null); - channel.enableLights(false); - channel.setLightColor(Color.BLUE); - channel.enableVibration(false); - notificationManager.createNotificationChannel(channel); - } - - notificationManager.cancel(0); - - Notification exportNotification = new NotificationCompat.Builder(getActivity(), "export") - .setContentText("Exporting grow log for " + plant.getName()) - .setContentTitle("Exporting") - .setContentIntent(PendingIntent.getActivity(getActivity(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT)) - .setTicker("Exporting grow log for " + plant.getName()) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setSmallIcon(R.drawable.ic_stat_name) - .build(); - - notificationManager.notify(0, exportNotification); + NotificationHelper.createExportChannel(getActivity()); + NotificationHelper.sendExportNotification(getActivity(), "Exporting grow log for " + plant.getName(), "Exporting grow log for " + plant.getName()); ExportHelper.exportPlants(getActivity(), new ArrayList(Arrays.asList(plant)), plant.getName().replaceAll("[^a-zA-Z0-9]+", "-"), new ExportCallback() { @@ -709,28 +739,7 @@ else if (item.getItemId() == R.id.export) { if (file != null && file.exists() && getActivity() != null) { - NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(0); - - Intent openIntent = new Intent(Intent.ACTION_VIEW); - Uri apkURI = FileProvider.getUriForFile(getActivity(), getActivity().getApplicationContext().getPackageName() + ".provider", file); - openIntent.setDataAndType(apkURI, "application/zip"); - openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - - Notification finishNotification = new NotificationCompat.Builder(getActivity(), "export") - .setContentText("Exported " + plant.getName() + " to " + file.getAbsolutePath()) - .setTicker("Export of " + plant.getName() + " complete") - .setContentTitle("Export Complete") - .setContentIntent(PendingIntent.getActivity(getActivity(), 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) - .setStyle(new NotificationCompat.BigTextStyle() - .bigText("Exported " + plant.getName() + " to " + file.getAbsolutePath()) - ) - .setSmallIcon(R.drawable.ic_stat_done) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setAutoCancel(true) - .build(); - notificationManager.notify(0, finishNotification); + NotificationHelper.sendExportCompleteNotification(context, "Export of " + plant.getName() + " complete", "Exported " + plant.getName() + " to " + file.getAbsolutePath(), file); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { diff --git a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java index a0869a12..325a1f5e 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java @@ -1,30 +1,44 @@ package me.anon.grow.fragment; -import android.app.*; +import android.app.Activity; +import android.app.AlertDialog; +import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.graphics.Color; import android.graphics.Rect; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; -import android.support.v4.app.NotificationCompat; -import android.support.v4.content.FileProvider; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.text.Html; import android.util.TypedValue; -import android.view.*; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; import android.widget.Toast; + import com.esotericsoftware.kryo.Kryo; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; + import me.anon.controller.adapter.PlantAdapter; import me.anon.controller.adapter.SimpleItemTouchHelperCallback; -import me.anon.grow.*; +import me.anon.grow.AddPlantActivity; +import me.anon.grow.AddWateringActivity; +import me.anon.grow.MainActivity; +import me.anon.grow.MainApplication; +import me.anon.grow.R; import me.anon.lib.ExportCallback; import me.anon.lib.SnackBar; import me.anon.lib.SnackBarListener; @@ -33,13 +47,14 @@ import me.anon.lib.helper.BusHelper; import me.anon.lib.helper.ExportHelper; import me.anon.lib.helper.FabAnimator; +import me.anon.lib.helper.NotificationHelper; import me.anon.lib.manager.GardenManager; import me.anon.lib.manager.PlantManager; -import me.anon.model.*; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; +import me.anon.model.EmptyAction; +import me.anon.model.Garden; +import me.anon.model.NoteAction; +import me.anon.model.Plant; +import me.anon.model.PlantStage; /** * // TODO: Add class description @@ -390,30 +405,9 @@ private synchronized void saveCurrentState() else if (item.getItemId() == R.id.export_garden) { Toast.makeText(getActivity(), "Exporting grow log of garden...", Toast.LENGTH_SHORT).show(); - NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); - - if (Build.VERSION.SDK_INT >= 26) - { - NotificationChannel channel = new NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_DEFAULT); - channel.setSound(null, null); - channel.enableLights(false); - channel.setLightColor(Color.BLUE); - channel.enableVibration(false); - - notificationManager.createNotificationChannel(channel); - } + NotificationHelper.createExportChannel(getActivity()); + NotificationHelper.sendExportNotification(getActivity(), "Exporting garden grow log", "Exporting " + garden.getName()); - NotificationCompat.Builder notifBuilder = new NotificationCompat.Builder(getActivity(), "export"); - notifBuilder - .setContentText("Exporting garden grow log") - .setContentTitle("Exporting") - .setContentIntent(PendingIntent.getActivity(getActivity(), 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT)) - .setTicker("Exporting garden grow log") - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setSmallIcon(R.drawable.ic_stat_name) - .build(); - - notificationManager.notify(0, notifBuilder.build()); ArrayList export = new ArrayList<>(adapter.getPlants()); exportPlants(export); } @@ -508,28 +502,7 @@ private void exportPlants(final ArrayList plants) { if (file != null && file.exists() && getActivity() != null) { - NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); - notificationManager.cancel(0); - - Intent openIntent = new Intent(Intent.ACTION_VIEW); - Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file); - openIntent.setDataAndType(apkURI, "application/zip"); - openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - - Notification finishNotification = new NotificationCompat.Builder(context, "export") - .setContentText("Exported " + currentGarden.getName() + " to " + file.getAbsolutePath()) - .setTicker("Export of " + currentGarden.getName() + " complete") - .setContentTitle("Export Complete") - .setContentIntent(PendingIntent.getActivity(context, 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) - .setStyle(new NotificationCompat.BigTextStyle() - .bigText("Exported " + currentGarden.getName() + " to " + file.getAbsolutePath()) - ) - .setSmallIcon(R.drawable.ic_stat_done) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - .setAutoCancel(true) - .build(); - notificationManager.notify(0, finishNotification); + NotificationHelper.sendExportCompleteNotification(context, "Export of " + currentGarden.getName() + " complete", "Exported " + currentGarden.getName() + " to " + file.getAbsolutePath(), file); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { diff --git a/app/src/main/java/me/anon/lib/helper/ExportHelper.java b/app/src/main/java/me/anon/lib/helper/ExportHelper.java index 38d34e93..8c16df66 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -1,14 +1,11 @@ package me.anon.lib.helper; -import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; -import android.graphics.Color; import android.os.AsyncTask; -import android.os.Build; import android.os.Environment; import android.preference.PreferenceManager; import android.support.annotation.NonNull; @@ -17,20 +14,39 @@ import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; + import com.github.mikephil.charting.charts.LineChart; -import me.anon.grow.R; -import me.anon.lib.ExportCallback; -import me.anon.lib.Unit; -import me.anon.lib.manager.PlantManager; -import me.anon.model.*; + import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; import net.lingala.zip4j.model.ZipParameters; import net.lingala.zip4j.util.Zip4jConstants; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; import java.text.DateFormat; -import java.util.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; + +import me.anon.grow.R; +import me.anon.lib.ExportCallback; +import me.anon.lib.Unit; +import me.anon.lib.manager.PlantManager; +import me.anon.model.Action; +import me.anon.model.Additive; +import me.anon.model.EmptyAction; +import me.anon.model.NoteAction; +import me.anon.model.Plant; +import me.anon.model.PlantStage; +import me.anon.model.StageChange; +import me.anon.model.Water; import static me.anon.lib.Unit.ML; @@ -46,6 +62,7 @@ public class ExportHelper */ @Nullable public static void exportPlants(final Context context, @NonNull final ArrayList plants, String name, final ExportCallback callback) { + String exportInt = "" + System.currentTimeMillis(); String folderPath = ""; Unit measureUnit = Unit.getSelectedMeasurementUnit(context); Unit deliveryUnit = Unit.getSelectedDeliveryUnit(context); @@ -60,11 +77,10 @@ public class ExportHelper folderPath = Environment.DIRECTORY_DOWNLOADS; } - ArrayList outputs = new ArrayList<>(); - File exportFolder = new File(folderPath + "/GrowLogs/"); + File exportFolder = new File(folderPath + "/GrowLogs/" + exportInt); exportFolder.mkdirs(); - final File finalFile = new File(exportFolder.getAbsolutePath() + "/" + name + ".zip"); + final File finalFile = new File(folderPath + "/GrowLogs/" + name + ".zip"); if (finalFile.exists()) { @@ -81,15 +97,7 @@ public class ExportHelper for (Plant plant : plants) { // temp folder to write to - final File tempFolder = new File(exportFolder.getAbsolutePath() + "/" + plant.getId()); - outputs.add(tempFolder); - - if (tempFolder.exists()) - { - deleteRecursive(tempFolder); - } - - tempFolder.mkdir(); + final String zipPathPrefix = plants.size() == 1 ? "" : plant.getName() + "/"; long startDate = plant.getPlantDate(); long endDate = System.currentTimeMillis(); @@ -206,7 +214,8 @@ public class ExportHelper { plantDetails.append("##Additives used"); plantDetails.append(NEW_LINE); - TextUtils.join(NEW_LINE + " - ", additiveNames); + plantDetails.append(" - "); + plantDetails.append(TextUtils.join(NEW_LINE + " - ", additiveNames)); plantDetails.append(NEW_LINE); } @@ -331,10 +340,12 @@ else if (action instanceof StageChange) // Write the log try { - FileWriter fileWriter = new FileWriter(tempFolder.getAbsolutePath() + "/growlog.md", false); - fileWriter.write(plantDetails.toString()); - fileWriter.flush(); - fileWriter.close(); + ZipParameters parameters = new ZipParameters(); + parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + parameters.setFileNameInZip(zipPathPrefix + "growlog.md"); + parameters.setSourceExternalStream(true); + + outFile.addStream(new ByteArrayInputStream(plantDetails.toString().getBytes("UTF-8")), parameters); } catch (Exception e) { @@ -343,24 +354,18 @@ else if (action instanceof StageChange) try { - if (plants.size() > 0) - { - params.setRootFolderInZip(plant.getName()); - } - - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/" + plant.getName() + " - growlog.md"), params); - - if (new File(tempFolder.getAbsolutePath() + "/images/").exists()) - { - outFile.addFolder(new File(tempFolder.getAbsolutePath() + "/images/"), params); - } +// if (plants.size() > 0) +// { +// params.setRootFolderInZip(plant.getName()); +// } - int width = 512 + (totalWater * 10); + int width = 1024 + (totalWater * 20); int height = 512; int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); LineChart additives = new LineChart(context); + additives.setPadding(100, 100, 100, 100); additives.setLayoutParams(new ViewGroup.LayoutParams(width, height)); additives.setMinimumWidth(width); additives.setMinimumHeight(height); @@ -372,8 +377,15 @@ else if (action instanceof StageChange) try { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/additives.png"); - additives.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + ZipParameters parameters = new ZipParameters(); + parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + parameters.setFileNameInZip(zipPathPrefix + "additives.jpg"); + parameters.setSourceExternalStream(true); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + additives.getChartBitmap().compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + ByteArrayInputStream stream = new ByteArrayInputStream(outputStream.toByteArray()); + outFile.addStream(stream, parameters); stream.close(); } @@ -383,6 +395,7 @@ else if (action instanceof StageChange) } LineChart inputPh = new LineChart(context); + inputPh.setPadding(100, 100, 100, 100); inputPh.setLayoutParams(new ViewGroup.LayoutParams(width, height)); inputPh.setMinimumWidth(width); inputPh.setMinimumHeight(height); @@ -394,8 +407,15 @@ else if (action instanceof StageChange) try { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/input-ph.png"); - inputPh.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + ZipParameters parameters = new ZipParameters(); + parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + parameters.setFileNameInZip(zipPathPrefix + "input-ph.jpg"); + parameters.setSourceExternalStream(true); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + inputPh.getChartBitmap().compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + ByteArrayInputStream stream = new ByteArrayInputStream(outputStream.toByteArray()); + outFile.addStream(stream, parameters); stream.close(); } @@ -405,6 +425,7 @@ else if (action instanceof StageChange) } LineChart ppm = new LineChart(context); + ppm.setPadding(100, 100, 100, 100); ppm.setLayoutParams(new ViewGroup.LayoutParams(width, height)); ppm.setMinimumWidth(width); ppm.setMinimumHeight(height); @@ -416,8 +437,15 @@ else if (action instanceof StageChange) try { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + (usingEc ? "ec" : "ppm") + ".png"); - ppm.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + ZipParameters parameters = new ZipParameters(); + parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + parameters.setFileNameInZip(zipPathPrefix + (usingEc ? "ec" : "ppm") + ".jpg"); + parameters.setSourceExternalStream(true); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ppm.getChartBitmap().compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + ByteArrayInputStream stream = new ByteArrayInputStream(outputStream.toByteArray()); + outFile.addStream(stream, parameters); stream.close(); } @@ -427,6 +455,7 @@ else if (action instanceof StageChange) } LineChart temp = new LineChart(context); + temp.setPadding(100, 100, 100, 100); temp.setLayoutParams(new ViewGroup.LayoutParams(width, height)); temp.setMinimumWidth(width); temp.setMinimumHeight(height); @@ -438,8 +467,15 @@ else if (action instanceof StageChange) try { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/temp.png"); - temp.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); + ZipParameters parameters = new ZipParameters(); + parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); + parameters.setFileNameInZip(zipPathPrefix + "temp.jpg"); + parameters.setSourceExternalStream(true); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + temp.getChartBitmap().compress(Bitmap.CompressFormat.JPEG, 100, outputStream); + ByteArrayInputStream stream = new ByteArrayInputStream(outputStream.toByteArray()); + outFile.addStream(stream, parameters); stream.close(); } @@ -447,41 +483,14 @@ else if (action instanceof StageChange) { e.printStackTrace(); } - - if (new File(tempFolder.getAbsolutePath() + "/additives.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/additives.png"), params); - } - - if (new File(tempFolder.getAbsolutePath() + "/input-ph.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/input-ph.png"), params); - } - - if (new File(tempFolder.getAbsolutePath() + "/ppm.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/ppm.png"), params); - } - - if (new File(tempFolder.getAbsolutePath() + "/ec.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/ec.png"), params); - } - - if (new File(tempFolder.getAbsolutePath() + "/temp.png").exists()) - { - outFile.addFile(new File(tempFolder.getAbsolutePath() + "/temp.png"), params); - } } catch (Exception e) { e.printStackTrace(); } - - } - copyImagesAndFinish(context, plants, outputs, outFile, callback); + copyImagesAndFinish(context, plants, outFile, callback); } catch (ZipException e) { @@ -489,7 +498,7 @@ else if (action instanceof StageChange) } } - protected static void copyImagesAndFinish(Context context, final ArrayList plant, final ArrayList outputs, final ZipFile finalFile, final ExportCallback callback) + protected static void copyImagesAndFinish(Context context, final ArrayList plant, final ZipFile finalFile, final ExportCallback callback) { final Context appContext = context.getApplicationContext(); new AsyncTask() @@ -501,17 +510,7 @@ protected static void copyImagesAndFinish(Context context, final ArrayList= 26) - { - NotificationChannel channel = new NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_DEFAULT); - channel.setSound(null, null); - channel.enableLights(false); - channel.setLightColor(Color.BLUE); - channel.enableVibration(false); - notificationManager.createNotificationChannel(channel); - } + NotificationHelper.createExportChannel(appContext); notificationManager = (NotificationManager)appContext.getSystemService(Context.NOTIFICATION_SERVICE); @@ -520,27 +519,21 @@ protected static void copyImagesAndFinish(Context context, final ArrayList= 26) + { + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val channel = NotificationChannel("export", "Export status", NotificationManager.IMPORTANCE_HIGH) + channel.setSound(null, null) + channel.enableLights(false) + channel.enableVibration(false) + channel.lightColor = Color.GREEN + + notificationManager.createNotificationChannel(channel) + } + } + + @JvmStatic + public fun sendExportNotification(context: Context, title: String, message: String) + { + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(0) + + val exportNotification = NotificationCompat.Builder(context, "export") + .setContentText(title) + .setContentTitle("Exporting") + .setContentIntent(PendingIntent.getActivity(context, 0, Intent(), PendingIntent.FLAG_UPDATE_CURRENT)) + .setTicker(message) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setSmallIcon(R.drawable.ic_stat_name) + .setSound(null) + .build() + + notificationManager.notify(0, exportNotification) + } + + @JvmStatic + public fun sendExportCompleteNotification(context: Context, title: String, message: String, file: File) + { + val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.cancel(0) + + val openIntent = Intent(Intent.ACTION_VIEW) + val apkURI = FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", file) + openIntent.setDataAndType(apkURI, "application/zip") + openIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + + val finishNotification = NotificationCompat.Builder(context, "export") + .setContentText(message) + .setTicker(title) + .setContentTitle("Export Complete") + .setContentIntent(PendingIntent.getActivity(context, 0, openIntent, PendingIntent.FLAG_UPDATE_CURRENT)) + .setStyle(NotificationCompat.BigTextStyle() + .bigText(message) + ) + .setSmallIcon(R.drawable.ic_stat_done) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setAutoCancel(true) + .setSound(null) + .build() + + notificationManager.notify(0, finishNotification) + } +} From b884d99c81c9c00f08418df4427e8d59b60f02a9 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Mon, 22 Apr 2019 19:21:06 +0100 Subject: [PATCH 08/27] Makes export a service --- app/src/main/AndroidManifest.xml | 1 + .../grow/fragment/PlantDetailsFragment.java | 27 +------ .../anon/grow/fragment/PlantListFragment.java | 30 +------- .../me/anon/grow/service/ExportService.kt | 70 +++++++++++++++++++ .../java/me/anon/lib/helper/ExportHelper.java | 5 -- .../me/anon/lib/helper/NotificationHelper.kt | 2 +- 6 files changed, 77 insertions(+), 58 deletions(-) create mode 100644 app/src/main/java/me/anon/grow/service/ExportService.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 3ba199b3..dbd7254a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -24,6 +24,7 @@ /> + diff --git a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java index 67f37a11..ecb78e0d 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; -import android.app.NotificationManager; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; @@ -67,6 +66,7 @@ import me.anon.grow.R; import me.anon.grow.StatisticsActivity; import me.anon.grow.ViewPhotosActivity; +import me.anon.grow.service.ExportService; import me.anon.lib.DateRenderer; import me.anon.lib.ExportCallback; import me.anon.lib.SnackBar; @@ -728,30 +728,7 @@ else if (item.getItemId() == R.id.duplicate) else if (item.getItemId() == R.id.export) { Toast.makeText(getActivity(), "Exporting grow log...", Toast.LENGTH_SHORT).show(); - NotificationManager notificationManager = (NotificationManager)getActivity().getSystemService(Context.NOTIFICATION_SERVICE); - - NotificationHelper.createExportChannel(getActivity()); - NotificationHelper.sendExportNotification(getActivity(), "Exporting grow log for " + plant.getName(), "Exporting grow log for " + plant.getName()); - - ExportHelper.exportPlants(getActivity(), new ArrayList(Arrays.asList(plant)), plant.getName().replaceAll("[^a-zA-Z0-9]+", "-"), new ExportCallback() - { - @Override public void onCallback(Context context, File file) - { - if (file != null && file.exists() && getActivity() != null) - { - NotificationHelper.sendExportCompleteNotification(context, "Export of " + plant.getName() + " complete", "Exported " + plant.getName() + " to " + file.getAbsolutePath(), file); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - { - new MediaScannerWrapper(getActivity(), file.getAbsolutePath(), "application/zip").scan(); - } - else - { - getActivity().sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file))); - } - } - } - }); + ExportService.export(getActivity(),new ArrayList(Arrays.asList(plant)), plant.getName().replaceAll("[^a-zA-Z0-9]+", "-"), plant.getName()); return true; } diff --git a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java index 325a1f5e..522e6b1b 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java @@ -3,12 +3,9 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.Fragment; -import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Rect; -import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; @@ -28,7 +25,6 @@ import com.esotericsoftware.kryo.Kryo; -import java.io.File; import java.util.ArrayList; import java.util.Arrays; @@ -39,13 +35,12 @@ import me.anon.grow.MainActivity; import me.anon.grow.MainApplication; import me.anon.grow.R; -import me.anon.lib.ExportCallback; +import me.anon.grow.service.ExportService; import me.anon.lib.SnackBar; import me.anon.lib.SnackBarListener; import me.anon.lib.Views; import me.anon.lib.event.GardenChangeEvent; import me.anon.lib.helper.BusHelper; -import me.anon.lib.helper.ExportHelper; import me.anon.lib.helper.FabAnimator; import me.anon.lib.helper.NotificationHelper; import me.anon.lib.manager.GardenManager; @@ -494,27 +489,8 @@ else if (item.getItemId() == R.id.delete_garden) private void exportPlants(final ArrayList plants) { - final Garden currentGarden = garden; - - ExportHelper.exportPlants(getActivity(), plants, garden.getName().replaceAll("[^a-zA-Z0-9]+", "-"), new ExportCallback() - { - @Override public void onCallback(Context context, File file) - { - if (file != null && file.exists() && getActivity() != null) - { - NotificationHelper.sendExportCompleteNotification(context, "Export of " + currentGarden.getName() + " complete", "Exported " + currentGarden.getName() + " to " + file.getAbsolutePath(), file); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) - { - new PlantDetailsFragment.MediaScannerWrapper(context, file.getParent(), "application/zip").scan(); - } - else - { - context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file))); - } - } - } - }); + Toast.makeText(getActivity(), "Exporting grow log...", Toast.LENGTH_SHORT).show(); + ExportService.export(getActivity(), plants, garden.getName().replaceAll("[^a-zA-Z0-9]+", "-"), garden.getName()); } private void filter() diff --git a/app/src/main/java/me/anon/grow/service/ExportService.kt b/app/src/main/java/me/anon/grow/service/ExportService.kt new file mode 100644 index 00000000..a0d21935 --- /dev/null +++ b/app/src/main/java/me/anon/grow/service/ExportService.kt @@ -0,0 +1,70 @@ +package me.anon.grow.service + +import android.app.Service +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.IBinder +import com.google.gson.reflect.TypeToken +import me.anon.grow.fragment.PlantDetailsFragment +import me.anon.lib.ExportCallback +import me.anon.lib.helper.ExportHelper +import me.anon.lib.helper.GsonHelper +import me.anon.lib.helper.NotificationHelper +import me.anon.model.Plant +import java.io.File + +/** + * Service for exporting + */ +class ExportService : Service() +{ + companion object + { + @JvmStatic + public fun export(context: Context, plants: ArrayList, title: String, name: String) + { + val plantStr = GsonHelper.getGson().toJson(plants, object : TypeToken>(){}.type) + val intent = Intent(context, ExportService::class.java) + intent.putExtra("plants", plantStr) + intent.putExtra("title", title) + intent.putExtra("name", name) + context.startService(intent) + } + } + + override fun onBind(intent: Intent?): IBinder? = null + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int + { + intent?.let { + val plants = GsonHelper.parse>(it.extras.getString("plants", "[]"), object : TypeToken>(){}.type) as ArrayList + val title = it.getStringExtra("title") + val name = it.getStringExtra("name") + + NotificationHelper.createExportChannel(this) + NotificationHelper.sendExportNotification(this, "Exporting grow log for $name", "Exporting grow log for $name") + + ExportHelper.exportPlants(this, plants, title, object : ExportCallback() + { + override fun onCallback(context: Context, file: File) + { + super.onCallback(context, file) + NotificationHelper.sendExportCompleteNotification(context, "Export of $name complete", "Exported $name to ${file.absolutePath}", file) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) + { + PlantDetailsFragment.MediaScannerWrapper(context, file.parent, "application/zip").scan() + } + else + { + context.sendBroadcast(Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.fromFile(file))) + } + } + }) + } + + return START_NOT_STICKY + } +} diff --git a/app/src/main/java/me/anon/lib/helper/ExportHelper.java b/app/src/main/java/me/anon/lib/helper/ExportHelper.java index 8c16df66..f1490406 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -354,11 +354,6 @@ else if (action instanceof StageChange) try { -// if (plants.size() > 0) -// { -// params.setRootFolderInZip(plant.getName()); -// } - int width = 1024 + (totalWater * 20); int height = 512; int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); diff --git a/app/src/main/java/me/anon/lib/helper/NotificationHelper.kt b/app/src/main/java/me/anon/lib/helper/NotificationHelper.kt index 09195a53..0c7726f3 100644 --- a/app/src/main/java/me/anon/lib/helper/NotificationHelper.kt +++ b/app/src/main/java/me/anon/lib/helper/NotificationHelper.kt @@ -13,7 +13,7 @@ import me.anon.grow.R import java.io.File /** - * // TODO: Add class description + * Helper class for sending notification */ object NotificationHelper { From 814672bd8b25ff1b586d109db303680f24bc54e6 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 11 May 2019 13:25:26 +0100 Subject: [PATCH 09/27] Fixes issue with layout text overlaying --- app/src/main/res/layout/action_item.xml | 85 ++++++++++++++----------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/app/src/main/res/layout/action_item.xml b/app/src/main/res/layout/action_item.xml index 8fe0f598..2f866e7f 100644 --- a/app/src/main/res/layout/action_item.xml +++ b/app/src/main/res/layout/action_item.xml @@ -3,6 +3,7 @@ - - - + android:id="@+id/overflow" + android:src="@drawable/ic_overflow" + android:background="@null" + android:adjustViewBounds="true" + android:tint="#333333" + android:padding="12dp" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintTop_toTopOf="parent" + /> - - + + + - + Date: Sat, 11 May 2019 14:18:08 +0100 Subject: [PATCH 10/27] Adds backup size to selector and adds null/empty check --- .../anon/grow/fragment/SettingsFragment.java | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java index ecd57895..4f0b606f 100644 --- a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java @@ -607,18 +607,26 @@ class BackupData String plantsPath; String gardenPath; String schedulePath; + long size = 0; @Override public String toString() { DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(getActivity()); DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(getActivity()); - return dateFormat.format(date) + " " + timeFormat.format(date); + return dateFormat.format(date) + " " + timeFormat.format(date) + " (" + lengthToString(size, false) + ")"; } } // get list of backups File backupPath = new File(Environment.getExternalStorageDirectory(), "/backups/GrowTracker/"); String[] backupFiles = backupPath.list(); + + if (backupFiles == null || backupFiles.length == 0) + { + Toast.makeText(getActivity(), "There are no backups to restore from", Toast.LENGTH_LONG).show(); + return false; + } + Arrays.sort(backupFiles); final ArrayList backups = new ArrayList(); @@ -650,6 +658,7 @@ class BackupData BackupData backupData = new BackupData(); backupData.plantsPath = backupPath.getPath() + "/" + backup; backupData.date = date; + backupData.size = backupPath.length(); backups.add(backupData); continue; } @@ -670,19 +679,23 @@ class BackupData current = new BackupData(); } + File file = new File(backupPath.getPath() + "/" + backup); if (backup.contains("plants")) { current.plantsPath = backupPath.getPath() + "/" + backup; + current.size += file.length(); } if (backup.contains("gardens")) { current.gardenPath = backupPath.getPath() + "/" + backup; + current.size += file.length(); } if (backup.contains("schedules")) { current.schedulePath = backupPath.getPath() + "/" + backup; + current.size += file.length(); } } @@ -756,4 +769,17 @@ class BackupData populateAddons(); } } + + public String lengthToString(long bytes, boolean si) + { + int unit = si ? 1000 : 1024; + if (bytes < unit) + { + return bytes + " B"; + } + + int exp = (int)(Math.log(bytes) / Math.log(unit)); + String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); + return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); + } } From da81a17e4a5749180216dc39fa365d029ee37c2a Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:14:39 +0100 Subject: [PATCH 11/27] Prevents backing up after failed restore --- .../me/anon/lib/manager/GardenManager.java | 10 +++---- .../me/anon/lib/manager/PlantManager.java | 27 +++++++++++++------ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/me/anon/lib/manager/GardenManager.java b/app/src/main/java/me/anon/lib/manager/GardenManager.java index 552e8615..db305754 100644 --- a/app/src/main/java/me/anon/lib/manager/GardenManager.java +++ b/app/src/main/java/me/anon/lib/manager/GardenManager.java @@ -56,7 +56,7 @@ public void load() { if (FileManager.getInstance().fileExists(FILES_DIR + "/gardens.json")) { - String plantData; + String gardenData; if (MainApplication.isEncrypted()) { @@ -65,19 +65,19 @@ public void load() return; } - plantData = EncryptionHelper.decrypt(MainApplication.getKey(), FileManager.getInstance().readFile(FILES_DIR + "/gardens.json")); + gardenData = EncryptionHelper.decrypt(MainApplication.getKey(), FileManager.getInstance().readFile(FILES_DIR + "/gardens.json")); } else { - plantData = FileManager.getInstance().readFileAsString(FILES_DIR + "/gardens.json"); + gardenData = FileManager.getInstance().readFileAsString(FILES_DIR + "/gardens.json"); } try { - if (!TextUtils.isEmpty(plantData)) + if (!TextUtils.isEmpty(gardenData)) { mGardens.clear(); - mGardens.addAll((ArrayList)GsonHelper.parse(plantData, new TypeToken>(){}.getType())); + mGardens.addAll((ArrayList)GsonHelper.parse(gardenData, new TypeToken>(){}.getType())); } } catch (JsonSyntaxException e) diff --git a/app/src/main/java/me/anon/lib/manager/PlantManager.java b/app/src/main/java/me/anon/lib/manager/PlantManager.java index 72191dd9..2609a014 100644 --- a/app/src/main/java/me/anon/lib/manager/PlantManager.java +++ b/app/src/main/java/me/anon/lib/manager/PlantManager.java @@ -183,6 +183,11 @@ public void upsert(int index, Plant plant) } public boolean load() + { + return load(false); + } + + public boolean load(boolean fromRestore) { if (MainApplication.isFailsafe()) { @@ -227,21 +232,27 @@ public boolean load() { e.printStackTrace(); - File backupPath = BackupHelper.backupJson(); - Toast.makeText(context, "There is a syntax error in your app data. Your data has been backed up to " + backupPath.getPath() + ". Please fix before re-opening the app.\n" + e.getMessage(), Toast.LENGTH_LONG).show(); + if (!fromRestore) + { + File backupPath = BackupHelper.backupJson(); + Toast.makeText(context, "There is a syntax error in your app data. Your data has been backed up to " + backupPath.getPath() + ". Please fix before re-opening the app.\n" + e.getMessage(), Toast.LENGTH_LONG).show(); - // prevent save - MainApplication.setFailsafe(true); + // prevent save + MainApplication.setFailsafe(true); + } } catch (Exception e) { e.printStackTrace(); - File backupPath = BackupHelper.backupJson(); - Toast.makeText(context, "There is a problem loading your app data. Your data has been backed up to " + backupPath.getPath(), Toast.LENGTH_LONG).show(); + if (!fromRestore) + { + File backupPath = BackupHelper.backupJson(); + Toast.makeText(context, "There is a problem loading your app data. Your data has been backed up to " + backupPath.getPath(), Toast.LENGTH_LONG).show(); - // prevent save - MainApplication.setFailsafe(true); + // prevent save + MainApplication.setFailsafe(true); + } } } From b48dffeeeaabc58c99f25da1c1db25780b3d2c02 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:14:45 +0100 Subject: [PATCH 12/27] Adds overrides --- app/src/main/java/me/anon/lib/SnackBar.kt | 47 ++++++++++++++++++----- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/me/anon/lib/SnackBar.kt b/app/src/main/java/me/anon/lib/SnackBar.kt index 237a1e2c..fa0d36ba 100644 --- a/app/src/main/java/me/anon/lib/SnackBar.kt +++ b/app/src/main/java/me/anon/lib/SnackBar.kt @@ -20,32 +20,47 @@ class SnackBar { @JvmStatic public fun show(context: Activity, @StringRes messageRes: Int, @StringRes actionTextRes: Int = -1, - listener: SnackBarListener + listener: SnackBarListener? ) { show(context, context.getString(messageRes), if (actionTextRes != -1) context.getString(actionTextRes) else "", + Snackbar.LENGTH_LONG, listener ) } @JvmStatic - public fun show(context: Activity, message: String, listener: SnackBarListener) + public fun show(context: Activity, message: String, listener: SnackBarListener?) { - show(context, message, "", listener) + show(context, message, "", Snackbar.LENGTH_LONG, listener) + } + + @JvmStatic + public fun show(context: Activity, message: String, length: Int, listener: SnackBarListener?) + { + show(context, message, "", length, listener) } @JvmStatic public fun show(context: Activity, message: String, actionText: String = "", - listener: SnackBarListener + listener: SnackBarListener? + ) + { + show(context, message, actionText, Snackbar.LENGTH_LONG, listener) + } + + @JvmStatic + public fun show(context: Activity, message: String, actionText: String = "", length: Int = Snackbar.LENGTH_LONG, + listener: SnackBarListener? ) { - SnackBar().show(context, message, actionText, { - listener.onSnackBarStarted(0) + SnackBar().show(context, message, actionText, Snackbar.LENGTH_LONG, { + listener?.onSnackBarStarted(0) }, { - listener.onSnackBarFinished(0) + listener?.onSnackBarFinished(0) }, { - listener.onSnackBarAction(0) + listener?.onSnackBarAction(0) }) } } @@ -63,17 +78,28 @@ class SnackBar { show(context, context.getString(messageRes), if (actionTextRes != -1) context.getString(actionTextRes) else "", + Snackbar.LENGTH_LONG, start, end, action ) } - public fun show(context: Activity, message: String, actionText: String = "", + public fun show(context: Activity, message: String, actionText: String = "", length: Int = Snackbar.LENGTH_LONG, start: () -> kotlin.Unit = {}, end: () -> kotlin.Unit = {}, action: () -> kotlin.Unit = {} ) { - val snackbar = Snackbar.make(context.findViewById(android.R.id.content), message, Snackbar.LENGTH_SHORT) + val snackbar = Snackbar.make(context.findViewById(android.R.id.content), message, length) + var actionText = actionText + var action = action + + if (length == Snackbar.LENGTH_INDEFINITE) + { + actionText = "Dismiss" + action = { + snackbar.dismiss() + } + } if (actionText.isNotEmpty()) { @@ -95,6 +121,7 @@ class SnackBar } }) + snackbar.show() } } From a010d1a05a43874e70b1f040b35c8a161daf03dd Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:14:52 +0100 Subject: [PATCH 13/27] Adds boolean extentions --- .../main/java/me/anon/lib/ext/BooleanUtils.kt | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 app/src/main/java/me/anon/lib/ext/BooleanUtils.kt diff --git a/app/src/main/java/me/anon/lib/ext/BooleanUtils.kt b/app/src/main/java/me/anon/lib/ext/BooleanUtils.kt new file mode 100644 index 00000000..dc5e2387 --- /dev/null +++ b/app/src/main/java/me/anon/lib/ext/BooleanUtils.kt @@ -0,0 +1,28 @@ +package me.anon.lib.ext + +import android.view.View + +public fun Boolean?.asVisibility(): Int +{ + return when (this) + { + true -> View.VISIBLE + else -> View.GONE + } +} + +public fun Boolean?.toValue(trueValue: Any?, falseValue: Any?): R +{ + return when (this) + { + true -> trueValue as R + false, null -> falseValue as R + } +} + +/** + * Ternary implementation + * Usage: t ?: + */ +@Suppress("FunctionName") +public infix fun Boolean?.T(value: T): T? = if (this == true) value else null From 20b1e5c362710801c77b4604e720e7cfbf75d66f Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:15:12 +0100 Subject: [PATCH 14/27] Prevent backup in failsafe mode and file extension for encrypted files --- .../main/java/me/anon/lib/helper/BackupHelper.kt | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/me/anon/lib/helper/BackupHelper.kt b/app/src/main/java/me/anon/lib/helper/BackupHelper.kt index 6c8abb5c..b664d6b8 100644 --- a/app/src/main/java/me/anon/lib/helper/BackupHelper.kt +++ b/app/src/main/java/me/anon/lib/helper/BackupHelper.kt @@ -1,6 +1,8 @@ package me.anon.lib.helper import android.os.Environment +import me.anon.grow.MainApplication +import me.anon.lib.ext.T import me.anon.lib.manager.FileManager import me.anon.lib.manager.PlantManager import java.io.File @@ -14,14 +16,18 @@ object BackupHelper public var FILES_PATH = Environment.getExternalStorageDirectory().absolutePath + "/backups/GrowTracker" @JvmStatic - public fun backupJson(): File + public fun backupJson(): File? { + if (MainApplication.isFailsafe()) return null + + val isEncrypted = MainApplication.isEncrypted() val time = System.currentTimeMillis() val backupPath = File(FILES_PATH) + val ext = isEncrypted T "dat" ?: "bak" backupPath.mkdirs() - FileManager.getInstance().copyFile("${PlantManager.FILES_DIR}/plants.json", "$FILES_PATH/$time.plants.json.bak") - FileManager.getInstance().copyFile("${PlantManager.FILES_DIR}/schedules.json", "$FILES_PATH/$time.schedules.json.bak") - FileManager.getInstance().copyFile("${PlantManager.FILES_DIR}/gardens.json", "$FILES_PATH/$time.gardens.json.bak") + FileManager.getInstance().copyFile("${PlantManager.FILES_DIR}/plants.json", "$FILES_PATH/$time.plants.json.$ext") + FileManager.getInstance().copyFile("${PlantManager.FILES_DIR}/schedules.json", "$FILES_PATH/$time.schedules.json.$ext") + FileManager.getInstance().copyFile("${PlantManager.FILES_DIR}/gardens.json", "$FILES_PATH/$time.gardens.json.$ext") return backupPath } From b47367701f643e45f8f5f159c35ae22ff93a8b48 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:15:50 +0100 Subject: [PATCH 15/27] Replaces checkbox with switch pref and adds support for restoring from encrypted files --- .../anon/grow/fragment/SettingsFragment.java | 66 ++++++++++++++----- app/src/main/res/xml/preferences.xml | 12 ++-- 2 files changed, 55 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java index 4f0b606f..90aa5861 100644 --- a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java @@ -15,11 +15,12 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; -import android.preference.CheckBoxPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; import android.preference.PreferenceManager; +import android.preference.SwitchPreference; +import android.support.design.widget.Snackbar; import android.support.v4.content.FileProvider; import android.text.Html; import android.text.TextUtils; @@ -29,6 +30,8 @@ import com.nostra13.universalimageloader.core.ImageLoader; +import org.jetbrains.annotations.NotNull; + import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; @@ -45,6 +48,7 @@ import me.anon.grow.MainApplication; import me.anon.grow.R; import me.anon.lib.SnackBar; +import me.anon.lib.SnackBarListener; import me.anon.lib.TempUnit; import me.anon.lib.Unit; import me.anon.lib.helper.AddonHelper; @@ -106,14 +110,14 @@ public class SettingsFragment extends PreferenceFragment implements Preference.O findPreference("backup_now").setOnPreferenceClickListener(this); findPreference("restore").setOnPreferenceClickListener(this); - findPreference("failsafe").setEnabled(((CheckBoxPreference)findPreference("encrypt")).isChecked()); + findPreference("failsafe").setEnabled(((SwitchPreference)findPreference("encrypt")).isChecked()); if (MainApplication.isFailsafe()) { - findPreference("failsafe").setTitle(""); - findPreference("failsafe").setSummary(""); - findPreference("encrypt").setTitle(""); - findPreference("encrypt").setSummary(""); + findPreference("failsafe").setTitle("Redacted"); + findPreference("failsafe").setSummary("Redacted"); + findPreference("encrypt").setTitle("Redacted"); + findPreference("encrypt").setSummary("Redacted"); } else { @@ -286,7 +290,7 @@ private void populateAddons() } else { - ((CheckBoxPreference)preference).setChecked(false); + ((SwitchPreference)preference).setChecked(false); Toast.makeText(getActivity(), "Error - passphrases did not match up", Toast.LENGTH_SHORT).show(); } } @@ -299,14 +303,14 @@ private void populateAddons() { @Override public void onClick(DialogInterface dialog, int which) { - ((CheckBoxPreference)preference).setChecked(false); + ((SwitchPreference)preference).setChecked(false); } }) - .setOnDismissListener(new DialogInterface.OnDismissListener() + .setOnCancelListener(new DialogInterface.OnCancelListener() { - @Override public void onDismiss(DialogInterface dialog) + @Override public void onCancel(DialogInterface dialog) { - ((CheckBoxPreference)preference).setChecked(false); + ((SwitchPreference)preference).setChecked(false); } }) .show(); @@ -343,7 +347,7 @@ private void populateAddons() } else { - ((CheckBoxPreference)preference).setChecked(true); + ((SwitchPreference)preference).setChecked(true); Toast.makeText(getActivity(), "Error - incorrect passphrase", Toast.LENGTH_SHORT).show(); } } @@ -392,7 +396,7 @@ else if ("failsafe".equals(preference.getKey())) } else { - ((CheckBoxPreference)preference).setChecked(false); + ((SwitchPreference)preference).setChecked(false); Toast.makeText(getActivity(), "Error - passphrases did not match up", Toast.LENGTH_SHORT).show(); } } @@ -405,7 +409,7 @@ else if ("failsafe".equals(preference.getKey())) { @Override public void onClick(DialogInterface dialog, int which) { - ((CheckBoxPreference)preference).setChecked(false); + ((SwitchPreference)preference).setChecked(false); } }) .show(); @@ -613,7 +617,8 @@ class BackupData { DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(getActivity()); DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(getActivity()); - return dateFormat.format(date) + " " + timeFormat.format(date) + " (" + lengthToString(size, false) + ")"; + boolean encrypted = plantsPath != null && plantsPath.endsWith("dat"); + return dateFormat.format(date) + " " + timeFormat.format(date) + " (" + (encrypted ? "encrypted " : "") + lengthToString(size, false) + ")"; } } @@ -727,28 +732,55 @@ class BackupData MainApplication.setFailsafe(false); } + if (selectedBackup.plantsPath.endsWith("dat") && !MainApplication.isEncrypted()) + { + SnackBar.show(getActivity(), "You must have encrpted mode enabled with the same password to restore this backup", "Enable", new SnackBarListener() + { + @Override public void onSnackBarStarted(@NotNull Object o){} + @Override public void onSnackBarFinished(@NotNull Object o){} + + @Override public void onSnackBarAction(@NotNull Object o) + { + ((SwitchPreference)findPreference("encrypt")).setChecked(true); + onPreferenceChange(findPreference("encrypt"), true); + } + }); + return; + } + + FileManager.getInstance().copyFile(PlantManager.FILES_DIR + "/plants.json", PlantManager.FILES_DIR + "/plants.temp"); FileManager.getInstance().copyFile(selectedBackup.plantsPath, PlantManager.FILES_DIR + "/plants.json"); - boolean loaded = PlantManager.getInstance().load(); + boolean loaded = PlantManager.getInstance().load(true); if (selectedBackup.gardenPath != null) { + FileManager.getInstance().copyFile(GardenManager.FILES_DIR + "/gardens.json", GardenManager.FILES_DIR + "/gardens.temp"); FileManager.getInstance().copyFile(selectedBackup.gardenPath, GardenManager.FILES_DIR + "/gardens.json"); GardenManager.getInstance().load(); } if (selectedBackup.schedulePath != null) { + FileManager.getInstance().copyFile(ScheduleManager.FILES_DIR + "/schedules.json", ScheduleManager.FILES_DIR + "/schedules.temp"); FileManager.getInstance().copyFile(selectedBackup.schedulePath, ScheduleManager.FILES_DIR + "/schedules.json"); ScheduleManager.instance.load(); } if (!loaded) { - SnackBar.show(getActivity(), "Could not restore from backup " + selectedBackup, null); + String errorEnd = MainApplication.isEncrypted() ? "unencrypted" : "encryped"; + SnackBar.show(getActivity(), "Could not restore from backup " + selectedBackup + ". File may be " + errorEnd, Snackbar.LENGTH_INDEFINITE, null); + FileManager.getInstance().copyFile(PlantManager.FILES_DIR + "/plants.temp", PlantManager.FILES_DIR + "/plants.json"); + FileManager.getInstance().copyFile(GardenManager.FILES_DIR + "/gardens.temp", GardenManager.FILES_DIR + "/gardens.json"); + FileManager.getInstance().copyFile(ScheduleManager.FILES_DIR + "/schedules.temp", ScheduleManager.FILES_DIR + "/schedules.json"); + PlantManager.getInstance().load(); + GardenManager.getInstance().load(); + ScheduleManager.instance.load(); } else { Toast.makeText(getActivity(), "Restore to " + selectedBackup + " completed", Toast.LENGTH_LONG).show(); + getActivity().recreate(); } } }) diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index bd20d0f1..64185ce5 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -12,7 +12,7 @@ android:summary="Default garden to show on open, currently All" /> - - - - - From 32464cedabb036ccd876f96d73cd634527550964 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:21:31 +0100 Subject: [PATCH 16/27] Adds null catch for decrypt stream --- .../java/me/anon/lib/stream/DecryptInputStream.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/me/anon/lib/stream/DecryptInputStream.java b/app/src/main/java/me/anon/lib/stream/DecryptInputStream.java index 8373e892..f6a261dc 100644 --- a/app/src/main/java/me/anon/lib/stream/DecryptInputStream.java +++ b/app/src/main/java/me/anon/lib/stream/DecryptInputStream.java @@ -84,7 +84,14 @@ public DecryptInputStream(String key, File file) throws FileNotFoundException @Override public void close() throws IOException { - cis.close(); - fis.close(); + try + { + cis.close(); + fis.close(); + } + catch (NullPointerException e) + { + e.printStackTrace(); + } } } From 46cdfb5392f7aa83d8b9d2ae85c94a3b6ef934d5 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 18 May 2019 15:55:19 +0100 Subject: [PATCH 17/27] Adds backup size limit setting --- .../java/me/anon/grow/MainApplication.java | 8 ++++++ .../anon/grow/fragment/SettingsFragment.java | 17 ++++++++++- .../java/me/anon/lib/helper/BackupHelper.kt | 28 +++++++++++++++++++ app/src/main/res/xml/preferences.xml | 8 ++++++ 4 files changed, 60 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/me/anon/grow/MainApplication.java b/app/src/main/java/me/anon/grow/MainApplication.java index 08cc957a..72f1dd11 100644 --- a/app/src/main/java/me/anon/grow/MainApplication.java +++ b/app/src/main/java/me/anon/grow/MainApplication.java @@ -7,6 +7,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; +import android.preference.PreferenceCategory; import android.preference.PreferenceManager; import com.nostra13.universalimageloader.core.DisplayImageOptions; @@ -90,10 +91,17 @@ public static boolean isTablet() return isTablet; } + private static Context context; + public static SharedPreferences getDefaultPreferences() + { + return PreferenceManager.getDefaultSharedPreferences(context); + } + @Override public void onCreate() { super.onCreate(); + context = this; ExceptionHandler.getInstance().register(this); encrypted = PreferenceManager.getDefaultSharedPreferences(this).getBoolean("encrypt", false); diff --git a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java index 90aa5861..ffd339ec 100644 --- a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java @@ -15,6 +15,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.Environment; +import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceCategory; import android.preference.PreferenceFragment; @@ -101,6 +102,10 @@ public class SettingsFragment extends PreferenceFragment implements Preference.O findPreference("encrypt").setOnPreferenceChangeListener(this); findPreference("failsafe").setOnPreferenceChangeListener(this); findPreference("auto_backup").setOnPreferenceChangeListener(this); + findPreference("backup_size").setOnPreferenceChangeListener(this); + String currentBackup = findPreference("backup_size").getSharedPreferences().getString("backup_size", "20"); + findPreference("backup_size").setSummary("Currently " + currentBackup + "mb / Using " + lengthToString(BackupHelper.backupSize(), false)); + findPreference("readme").setOnPreferenceClickListener(this); findPreference("export").setOnPreferenceClickListener(this); findPreference("default_garden").setOnPreferenceClickListener(this); @@ -233,7 +238,17 @@ private void populateAddons() @Override public boolean onPreferenceChange(final Preference preference, Object newValue) { - if ("encrypt".equals(preference.getKey())) + if ("backup_size".equals(preference.getKey())) + { + String currentBackup = (String)newValue; + PreferenceManager.getDefaultSharedPreferences(getContext()).edit() + .putString("backup_size", currentBackup) + .apply(); + ((EditTextPreference)preference).setText(currentBackup); + BackupHelper.limitBackups(currentBackup); + findPreference("backup_size").setSummary("Currently " + currentBackup + "mb / Using " + lengthToString(BackupHelper.backupSize(), false)); + } + else if ("encrypt".equals(preference.getKey())) { if ((Boolean)newValue == true) { diff --git a/app/src/main/java/me/anon/lib/helper/BackupHelper.kt b/app/src/main/java/me/anon/lib/helper/BackupHelper.kt index b664d6b8..28babaff 100644 --- a/app/src/main/java/me/anon/lib/helper/BackupHelper.kt +++ b/app/src/main/java/me/anon/lib/helper/BackupHelper.kt @@ -1,5 +1,6 @@ package me.anon.lib.helper +import android.content.Context import android.os.Environment import me.anon.grow.MainApplication import me.anon.lib.ext.T @@ -20,6 +21,7 @@ object BackupHelper { if (MainApplication.isFailsafe()) return null + limitBackups() val isEncrypted = MainApplication.isEncrypted() val time = System.currentTimeMillis() val backupPath = File(FILES_PATH) @@ -31,4 +33,30 @@ object BackupHelper return backupPath } + + @JvmStatic + public fun backupSize(): Long + { + val path = File(BackupHelper.FILES_PATH) + return path.listFiles().fold(0L, { acc, file -> acc + file.length() }) + } + + @JvmStatic + public fun limitBackups(size: String = MainApplication.getDefaultPreferences().getString("backup_size", "20")!!) + { + val files = File(BackupHelper.FILES_PATH).listFiles() + val sorted = ArrayList(files.sortedBy { it.lastModified() }) + val limit = size.toInt() * 1_048_576 + + var currentSize = backupSize() + while (currentSize > limit) + { + val remove = sorted.removeAt(0) + val len = remove.length() + if (remove.delete()) + { + currentSize -= len + } + } + } } diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 64185ce5..cf3a6cb4 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -65,6 +65,14 @@ android:title="Backup now" /> + + Date: Sat, 18 May 2019 16:06:08 +0100 Subject: [PATCH 18/27] Links planted action with plant date --- .../java/me/anon/controller/adapter/ActionAdapter.java | 7 +++++++ .../java/me/anon/grow/fragment/EventListFragment.java | 6 ++++++ .../java/me/anon/grow/fragment/PlantDetailsFragment.java | 9 +++++++++ 3 files changed, 22 insertions(+) diff --git a/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java b/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java index f6e96659..427face8 100644 --- a/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java +++ b/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java @@ -37,6 +37,7 @@ import me.anon.model.EmptyAction; import me.anon.model.NoteAction; import me.anon.model.Plant; +import me.anon.model.PlantStage; import me.anon.model.StageChange; import me.anon.model.Water; import me.anon.view.ActionHolder; @@ -152,6 +153,12 @@ public void setActions(@Nullable Plant plant, ArrayList actions, ArrayLi Collections.reverse(actions); for (Action item : actions) { + // force planted stage to use plant date + if (item instanceof StageChange && ((StageChange)item).getNewStage() == PlantStage.PLANTED) + { + item.setDate(plant.getPlantDate()); + } + if (plant != null) { ArrayList groupedImages = new ArrayList<>(); diff --git a/app/src/main/java/me/anon/grow/fragment/EventListFragment.java b/app/src/main/java/me/anon/grow/fragment/EventListFragment.java index 2648179a..2aff63cf 100644 --- a/app/src/main/java/me/anon/grow/fragment/EventListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/EventListFragment.java @@ -40,6 +40,7 @@ import me.anon.model.EmptyAction; import me.anon.model.NoteAction; import me.anon.model.Plant; +import me.anon.model.PlantStage; import me.anon.model.StageChange; import me.anon.model.Water; import me.anon.view.ActionHolder; @@ -436,6 +437,11 @@ else if (action instanceof StageChange) { @Override public void onStageUpdated(final StageChange action) { + if (action.getNewStage() == PlantStage.PLANTED) + { + plant.setPlantDate(action.getDate()); + } + plant.getActions().set(originalIndex, action); PlantManager.getInstance().upsert(plantIndex, plant); setActions(); diff --git a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java index ecb78e0d..2fba63ab 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -81,6 +81,7 @@ import me.anon.lib.manager.PlantManager; import me.anon.lib.task.AsyncCallback; import me.anon.lib.task.EncryptTask; +import me.anon.model.Action; import me.anon.model.EmptyAction; import me.anon.model.NoteAction; import me.anon.model.Plant; @@ -277,6 +278,14 @@ private void setUi() { @Override public void onDateSelected(Calendar newDate) { + for (Action action : plant.getActions()) + { + if (action instanceof StageChange && ((StageChange)action).getNewStage() == PlantStage.PLANTED) + { + action.setDate(newDate.getTimeInMillis()); + } + } + plant.setPlantDate(newDate.getTimeInMillis()); String dateStr = dateFormat.format(new Date(plant.getPlantDate())) + " " + timeFormat.format(new Date(plant.getPlantDate())); date.setText(dateStr); From 1b552134dee27a7ad4adfd834cea138d128a03a3 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sun, 19 May 2019 15:39:22 +0100 Subject: [PATCH 19/27] Fixes issue with action text getting cut off --- .../grow/fragment/ActionSelectDialogFragment.java | 7 +++++-- app/src/main/res/layout/action_item.xml | 15 ++++++++++----- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java b/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java index a18fa29c..e2cd2679 100644 --- a/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java @@ -58,11 +58,14 @@ public ActionSelectDialogFragment(ArrayList actions) { super.onBindViewHolder(vh, index); int padding = (int)getResources().getDimension(R.dimen.padding_8dp); - vh.itemView.setPadding(padding, padding, padding, padding); + vh.itemView.setPadding(0, 0, 0, 0); + vh.itemView.findViewById(R.id.date_container).setVisibility(View.GONE); + ((View)vh.itemView.findViewById(R.id.content_container).getParent()).setPadding(0, 0, 0, 0); if (vh instanceof ActionHolder) { - ((ActionHolder)vh).getCard().setCardBackgroundColor(0xffffff); + ((ActionHolder)vh).getCard().setBackgroundResource(0); + ((ActionHolder)vh).getCard().setContentPadding(padding, padding, padding * 2, (int)(padding * 2.5)); } } }; diff --git a/app/src/main/res/layout/action_item.xml b/app/src/main/res/layout/action_item.xml index 2f866e7f..1f509aa8 100644 --- a/app/src/main/res/layout/action_item.xml +++ b/app/src/main/res/layout/action_item.xml @@ -12,6 +12,7 @@ @@ -84,6 +87,8 @@ android:id="@+id/name" android:paddingEnd="8dp" android:paddingStart="8dp" + android:minHeight="42dp" + android:gravity="center_vertical" app:layout_constraintTop_toTopOf="@id/date" app:layout_constraintEnd_toStartOf="@id/date" app:layout_constraintStart_toStartOf="parent" From 7e596c5fa02411ef86a326da87199aa89430b0e1 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Tue, 28 May 2019 19:58:49 +0100 Subject: [PATCH 20/27] Adds stage time to photo lightbox --- .../anon/controller/adapter/ImageAdapter.java | 4 ++ .../grow/fragment/ImageLightboxDialog.java | 49 +++++++++++++++++-- .../grow/fragment/ViewPhotosFragment.java | 1 + .../java/me/anon/view/ImageActionHolder.java | 2 + 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/me/anon/controller/adapter/ImageAdapter.java b/app/src/main/java/me/anon/controller/adapter/ImageAdapter.java index be990c7b..fe98086e 100644 --- a/app/src/main/java/me/anon/controller/adapter/ImageAdapter.java +++ b/app/src/main/java/me/anon/controller/adapter/ImageAdapter.java @@ -17,6 +17,8 @@ import me.anon.grow.MainApplication; import me.anon.grow.R; import me.anon.grow.fragment.ImageLightboxDialog; +import me.anon.lib.manager.PlantManager; +import me.anon.model.Plant; import me.anon.view.ImageHolder; /** @@ -28,6 +30,7 @@ */ public class ImageAdapter extends RecyclerView.Adapter { + public Plant plant = null; private List images = new ArrayList<>(); private List selected = new ArrayList<>(); private View.OnLongClickListener onLongClickListener; @@ -75,6 +78,7 @@ public void setImages(List images) if (!inActionMode) { Intent details = new Intent(v.getContext(), ImageLightboxDialog.class); + details.putExtra("plant_index", PlantManager.getInstance().getPlants().indexOf(plant)); details.putExtra("images", (String[])images.toArray(new String[getItemCount()])); details.putExtra("image_position", position); v.getContext().startActivity(details); diff --git a/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java b/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java index 2c4dbb70..9c655536 100644 --- a/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java +++ b/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java @@ -35,11 +35,17 @@ import me.anon.grow.R; import me.anon.lib.DateRenderer; import me.anon.lib.Views; +import me.anon.lib.helper.TimeHelper; +import me.anon.lib.manager.PlantManager; +import me.anon.model.Action; +import me.anon.model.Plant; +import me.anon.model.StageChange; @Views.Injectable public class ImageLightboxDialog extends Activity { private String[] imageUrls = {}; + private Plant plant; @Views.InjectView(R.id.pager) public ViewPager pager; private int pagerPosition = 0; @@ -61,6 +67,7 @@ public class ImageLightboxDialog extends Activity imageUrls = getIntent().getExtras().getStringArray("images"); } + plant = PlantManager.getInstance().getPlants().get(getIntent().getIntExtra("plant_index", -1)); pagerPosition = getIntent().getExtras().getInt("image_position", 0); } else @@ -71,6 +78,7 @@ public class ImageLightboxDialog extends Activity if (savedInstanceState != null) { + plant = PlantManager.getInstance().getPlants().get(getIntent().getIntExtra("plant_index", -1)); pagerPosition = savedInstanceState.getInt("image_position"); } @@ -82,6 +90,7 @@ public class ImageLightboxDialog extends Activity @Override protected void onSaveInstanceState(Bundle outState) { + outState.putInt("plant_index", PlantManager.getInstance().getPlants().indexOf(plant)); outState.putInt("image_position", pager.getCurrentItem()); super.onSaveInstanceState(outState); } @@ -169,10 +178,44 @@ private void forceHideKeyboard(ViewGroup view) DateFormat dateFormat = android.text.format.DateFormat.getDateFormat(view.getContext()); DateFormat timeFormat = android.text.format.DateFormat.getTimeFormat(view.getContext()); String[] parts = images[position].split("/"); - String date = parts[parts.length - 1].split("\\.")[0]; + String fileDate = parts[parts.length - 1].split("\\.")[0]; + Date date = new Date(Long.parseLong(fileDate)); - String dateStr = dateFormat.format(new Date(Long.parseLong(date))) + " " + timeFormat.format(new Date(Long.parseLong(date))); - ((TextView)imageLayout.findViewById(R.id.taken)).setText(Html.fromHtml("Image taken: " + dateStr + " (" + new DateRenderer().timeAgo(Long.parseLong(date)).formattedDate + " ago)")); + StageChange lastChange = null; + StageChange currentChange = null; + + for (int index = plant.getActions().size() - 1; index >= 0; index--) + { + Action action = plant.getActions().get(index); + if (action instanceof StageChange) + { + if (action.getDate() > date.getTime()) + { + currentChange = (StageChange)action; + } + else if (action.getDate() < date.getTime() && lastChange == null) + { + lastChange = (StageChange)action; + } + } + } + + String stageDayStr = ""; + if (lastChange != null) + { + if (currentChange == null) + { + // fake action to get time diff from + currentChange = new StageChange(lastChange.getNewStage()); + currentChange.setDate(System.currentTimeMillis()); + } + + int currentDays = (int)TimeHelper.toDays(Math.abs(currentChange.getDate() - lastChange.getDate())); + stageDayStr += "/" + currentDays + lastChange.getNewStage().getPrintString().substring(0, 1).toLowerCase(); + } + + String dateStr = dateFormat.format(date) + " " + timeFormat.format(fileDate); + ((TextView)imageLayout.findViewById(R.id.taken)).setText(Html.fromHtml("Image taken: " + dateStr + stageDayStr +" (" + new DateRenderer().timeAgo(date.getTime()).formattedDate + " ago)")); try { diff --git a/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java b/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java index 05a5f65c..94ac5717 100644 --- a/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java @@ -119,6 +119,7 @@ public static ViewPhotosFragment newInstance(int plantIndex) } adapter = new ImageAdapter(); + adapter.plant = plant; adapter.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) diff --git a/app/src/main/java/me/anon/view/ImageActionHolder.java b/app/src/main/java/me/anon/view/ImageActionHolder.java index dbceafd2..9267da1b 100644 --- a/app/src/main/java/me/anon/view/ImageActionHolder.java +++ b/app/src/main/java/me/anon/view/ImageActionHolder.java @@ -21,6 +21,7 @@ import me.anon.grow.MainApplication; import me.anon.grow.R; import me.anon.grow.fragment.ImageLightboxDialog; +import me.anon.lib.manager.PlantManager; /** * // TODO: Add class description @@ -89,6 +90,7 @@ public void bind(final ArrayList imageUrls) Collections.reverse(images); Intent details = new Intent(v.getContext(), ImageLightboxDialog.class); + details.putExtra("plant_index", PlantManager.getInstance().getPlants().indexOf(adapter.getPlant())); details.putExtra("images", (String[])images.toArray(new String[images.size()])); details.putExtra("image_position", images.indexOf(imageUrl)); v.getContext().startActivity(details); From 2926fadcd2f0ac36e94e21098f831e1ee363d3d5 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Tue, 28 May 2019 21:51:31 +0100 Subject: [PATCH 21/27] Fixes incorrect calculations for stage dates --- .../controller/adapter/ActionAdapter.java | 44 ++++++------------- .../grow/fragment/ImageLightboxDialog.java | 24 ++++------ .../grow/fragment/ViewPhotosFragment.java | 31 ++++++++++++- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java b/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java index 427face8..39cddbaf 100644 --- a/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java +++ b/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java @@ -458,47 +458,31 @@ else if (item.getItemId() == R.id.delete) dateDay.setText(Html.fromHtml(dateDayStr)); String stageDayStr = ""; - StageChange current = null; - StageChange previous = null; + + StageChange lastChange = null; + StageChange currentChange = new StageChange(); + currentChange.setDate(action.getDate()); for (int actionIndex = index; actionIndex < actions.size(); actionIndex++) { if (actions.get(actionIndex) instanceof StageChange) { - if (current == null) - { - current = (StageChange)actions.get(actionIndex); - } - else if (previous == null) + if (lastChange == null) { - previous = (StageChange)actions.get(actionIndex); + lastChange = (StageChange)actions.get(actionIndex); + break; } } } - if (plant != null) - { - int totalDays = (int)TimeHelper.toDays(Math.abs(action.getDate() - plant.getPlantDate())); - stageDayStr += totalDays; - - if (previous == null) - { - previous = current; - } + int totalDays = (int)TimeHelper.toDays(Math.abs(action.getDate() - plant.getPlantDate())); + stageDayStr += (totalDays == 0 ? 1 : totalDays); - if (current != null) - { - if (action == current) - { - int currentDays = (int)TimeHelper.toDays(Math.abs(current.getDate() - previous.getDate())); - stageDayStr += "/" + currentDays + previous.getNewStage().getPrintString().substring(0, 1).toLowerCase(); - } - else - { - int currentDays = (int)TimeHelper.toDays(Math.abs(action.getDate() - current.getDate())); - stageDayStr += "/" + currentDays + current.getNewStage().getPrintString().substring(0, 1).toLowerCase(); - } - } + if (lastChange != null) + { + int currentDays = (int)TimeHelper.toDays(Math.abs(currentChange.getDate() - lastChange.getDate())); + currentDays = (currentDays == 0 ? 1 : currentDays); + stageDayStr += "/" + currentDays + lastChange.getNewStage().getPrintString().substring(0, 1).toLowerCase(); } stageDay.setText(stageDayStr); diff --git a/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java b/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java index 9c655536..6bddd836 100644 --- a/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java +++ b/app/src/main/java/me/anon/grow/fragment/ImageLightboxDialog.java @@ -182,20 +182,18 @@ private void forceHideKeyboard(ViewGroup view) Date date = new Date(Long.parseLong(fileDate)); StageChange lastChange = null; - StageChange currentChange = null; + StageChange currentChange = new StageChange(); + currentChange.setDate(date.getTime()); for (int index = plant.getActions().size() - 1; index >= 0; index--) { Action action = plant.getActions().get(index); if (action instanceof StageChange) { - if (action.getDate() > date.getTime()) - { - currentChange = (StageChange)action; - } - else if (action.getDate() < date.getTime() && lastChange == null) + if (action.getDate() < date.getTime() && lastChange == null) { lastChange = (StageChange)action; + break; } } } @@ -203,19 +201,13 @@ else if (action.getDate() < date.getTime() && lastChange == null) String stageDayStr = ""; if (lastChange != null) { - if (currentChange == null) - { - // fake action to get time diff from - currentChange = new StageChange(lastChange.getNewStage()); - currentChange.setDate(System.currentTimeMillis()); - } - int currentDays = (int)TimeHelper.toDays(Math.abs(currentChange.getDate() - lastChange.getDate())); - stageDayStr += "/" + currentDays + lastChange.getNewStage().getPrintString().substring(0, 1).toLowerCase(); + currentDays = (currentDays == 0 ? 1 : currentDays); + stageDayStr += " [" + currentDays + lastChange.getNewStage().getPrintString().substring(0, 1).toLowerCase() + "]"; } - String dateStr = dateFormat.format(date) + " " + timeFormat.format(fileDate); - ((TextView)imageLayout.findViewById(R.id.taken)).setText(Html.fromHtml("Image taken: " + dateStr + stageDayStr +" (" + new DateRenderer().timeAgo(date.getTime()).formattedDate + " ago)")); + String dateStr = dateFormat.format(date) + " " + timeFormat.format(date); + ((TextView)imageLayout.findViewById(R.id.taken)).setText(Html.fromHtml("Image taken: " + dateStr + stageDayStr + " (" + new DateRenderer().timeAgo(date.getTime()).formattedDate + " ago)")); try { diff --git a/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java b/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java index 94ac5717..37300afa 100644 --- a/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java @@ -51,9 +51,12 @@ import me.anon.lib.helper.ExportHelper; import me.anon.lib.helper.FabAnimator; import me.anon.lib.helper.PermissionHelper; +import me.anon.lib.helper.TimeHelper; import me.anon.lib.manager.PlantManager; import me.anon.lib.task.EncryptTask; +import me.anon.model.Action; import me.anon.model.Plant; +import me.anon.model.StageChange; /** * // TODO: Add class description @@ -242,7 +245,33 @@ private void setAdapter() if (!lastFileDate.equalsIgnoreCase(printedFileDate)) { lastFileDate = printedFileDate; - sections.add(new SectionedGridRecyclerViewAdapter.Section(index, printedFileDate)); + + StageChange lastChange = null; + StageChange currentChange = new StageChange(); + currentChange.setDate(fileDate); + + for (int actionIndex = plant.getActions().size() - 1; actionIndex >= 0; actionIndex--) + { + Action action = plant.getActions().get(actionIndex); + if (action instanceof StageChange) + { + if (action.getDate() < fileDate && lastChange == null) + { + lastChange = (StageChange)action; + break; + } + } + } + + String stageDayStr = ""; + if (lastChange != null) + { + int currentDays = (int)TimeHelper.toDays(Math.abs(currentChange.getDate() - lastChange.getDate())); + currentDays = (currentDays == 0 ? 1 : currentDays); + stageDayStr += " ~" + currentDays + lastChange.getNewStage().getPrintString().substring(0, 1).toLowerCase(); + } + + sections.add(new SectionedGridRecyclerViewAdapter.Section(index, printedFileDate + stageDayStr)); } } From d27e9e868631056f36c2257b99d269d30f776ad6 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 15 Jun 2019 10:58:52 +0100 Subject: [PATCH 22/27] Adds seedling stage --- .../main/java/me/anon/grow/fragment/PlantListFragment.java | 2 +- app/src/main/java/me/anon/model/PlantStage.java | 1 + app/src/main/res/menu/plant_list_menu.xml | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java index 522e6b1b..214247c3 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java @@ -457,7 +457,7 @@ else if (item.getItemId() == R.id.delete_garden) saveCurrentState(); } - int[] ids = {R.id.filter_planted, R.id.filter_germination, R.id.filter_cutting, R.id.filter_vegetation, R.id.filter_flowering, R.id.filter_drying, R.id.filter_curing, R.id.filter_harvested}; + int[] ids = {R.id.filter_planted, R.id.filter_germination, R.id.filter_seedling, R.id.filter_cutting, R.id.filter_vegetation, R.id.filter_flowering, R.id.filter_drying, R.id.filter_curing, R.id.filter_harvested}; PlantStage[] stages = PlantStage.values(); for (int index = 0; index < ids.length; index++) diff --git a/app/src/main/java/me/anon/model/PlantStage.java b/app/src/main/java/me/anon/model/PlantStage.java index 721a17f6..84dc6f9d 100644 --- a/app/src/main/java/me/anon/model/PlantStage.java +++ b/app/src/main/java/me/anon/model/PlantStage.java @@ -13,6 +13,7 @@ public enum PlantStage { PLANTED("Planted"), GERMINATION("Germination"), + SEEDLING("Seedling"), CUTTING("Cutting"), VEGETATION("Vegetation"), FLOWER("Flower"), diff --git a/app/src/main/res/menu/plant_list_menu.xml b/app/src/main/res/menu/plant_list_menu.xml index 4ac16497..edc41f17 100644 --- a/app/src/main/res/menu/plant_list_menu.xml +++ b/app/src/main/res/menu/plant_list_menu.xml @@ -24,6 +24,12 @@ android:checked="true" /> + + Date: Sat, 15 Jun 2019 11:26:56 +0100 Subject: [PATCH 23/27] Fixes issue with landscape stats view and adds seedling time --- .../grow/fragment/StatisticsFragment.java | 8 +++ .../main/res/layout-land/statistics_view.xml | 60 +++++++++++++++++++ app/src/main/res/layout/statistics_view.xml | 41 ++++++++++++- 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/StatisticsFragment.java b/app/src/main/java/me/anon/grow/fragment/StatisticsFragment.java index 30030a1b..e221b83e 100644 --- a/app/src/main/java/me/anon/grow/fragment/StatisticsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/StatisticsFragment.java @@ -72,6 +72,8 @@ public static StatisticsFragment newInstance(int plantIndex) @Views.InjectView(R.id.germ_time_container) private View germTimeContainer; @Views.InjectView(R.id.veg_time) private TextView vegTime; @Views.InjectView(R.id.veg_time_container) private View vegTimeContainer; + @Views.InjectView(R.id.seedling_time) private TextView seedlingTime; + @Views.InjectView(R.id.seedling_time_container) private View seedlingTimeContainer; @Views.InjectView(R.id.cutting_time) private TextView cuttingTime; @Views.InjectView(R.id.cutting_time_container) private View cuttingTimeContainer; @Views.InjectView(R.id.flower_time) private TextView flowerTime; @@ -299,6 +301,12 @@ private void setStatistics() vegTimeContainer.setVisibility(View.VISIBLE); } + if (stages.containsKey(PlantStage.SEEDLING)) + { + seedlingTime.setText((int)TimeHelper.toDays(stages.get(PlantStage.SEEDLING)) + " days"); + seedlingTimeContainer.setVisibility(View.VISIBLE); + } + if (stages.containsKey(PlantStage.CUTTING)) { cuttingTime.setText((int)TimeHelper.toDays(stages.get(PlantStage.CUTTING)) + " days"); diff --git a/app/src/main/res/layout-land/statistics_view.xml b/app/src/main/res/layout-land/statistics_view.xml index d9a0dbd8..5f94a862 100644 --- a/app/src/main/res/layout-land/statistics_view.xml +++ b/app/src/main/res/layout-land/statistics_view.xml @@ -65,6 +65,66 @@ /> + + + + + + + + + + + + @@ -65,6 +67,36 @@ /> + + + + + + @@ -102,17 +136,20 @@ android:visibility="gone" > Date: Sat, 22 Jun 2019 13:02:05 +0100 Subject: [PATCH 24/27] Adds reverse plant list order setting --- .../java/me/anon/grow/fragment/PlantListFragment.java | 5 ++++- app/src/main/res/xml/preferences.xml | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java index 214247c3..399567b8 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantListFragment.java @@ -134,7 +134,10 @@ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, Recycle } else { - recycler.setLayoutManager(new LinearLayoutManager(getActivity())); + boolean reverse = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("reverse_order", false); + LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, reverse); + layoutManager.setStackFromEnd(reverse); + recycler.setLayoutManager(layoutManager); } recycler.setAdapter(adapter); diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index cf3a6cb4..b61a3f01 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -12,13 +12,19 @@ android:summary="Default garden to show on open, currently All" /> + + - Date: Sat, 22 Jun 2019 13:23:37 +0100 Subject: [PATCH 25/27] Updates readme and moves screenshots --- README.md | 44 +++++++++--------- app/src/main/assets/readme.html | 2 +- .../images/phoneScreenshots}/1.png | Bin .../images/phoneScreenshots}/10.png | Bin .../images/phoneScreenshots}/11.png | Bin .../images/phoneScreenshots}/12.png | Bin .../images/phoneScreenshots}/13.png | Bin .../images/phoneScreenshots}/14.png | Bin .../images/phoneScreenshots}/15.png | Bin .../images/phoneScreenshots}/1b.png | Bin .../images/phoneScreenshots}/1c.png | Bin .../images/phoneScreenshots}/1d.png | Bin .../images/phoneScreenshots}/1e.png | Bin .../images/phoneScreenshots}/2.png | Bin .../images/phoneScreenshots}/3.png | Bin .../images/phoneScreenshots}/4.png | Bin .../images/phoneScreenshots}/4b.png | Bin .../images/phoneScreenshots}/5.png | Bin .../images/phoneScreenshots}/6.png | Bin .../images/phoneScreenshots}/7.png | Bin .../images/phoneScreenshots}/8.png | Bin .../images/phoneScreenshots}/9.png | Bin .../images/phoneScreenshots}/install.png | Bin .../phoneScreenshotsThumbs}/1-thumb.png | Bin .../phoneScreenshotsThumbs}/10-thumb.png | Bin .../phoneScreenshotsThumbs}/11-thumb.png | Bin .../phoneScreenshotsThumbs}/12-thumb.png | Bin .../phoneScreenshotsThumbs}/13-thumb.png | Bin .../phoneScreenshotsThumbs}/14-thumb.png | Bin .../phoneScreenshotsThumbs}/15-thumb.png | Bin .../phoneScreenshotsThumbs}/1b-thumb.png | Bin .../phoneScreenshotsThumbs}/1c-thumb.png | Bin .../phoneScreenshotsThumbs}/1d-thumb.png | Bin .../phoneScreenshotsThumbs}/1e-thumb.png | Bin .../phoneScreenshotsThumbs}/2-thumb.png | Bin .../phoneScreenshotsThumbs}/3-thumb.png | Bin .../phoneScreenshotsThumbs}/4-thumb.png | Bin .../phoneScreenshotsThumbs}/4b-thumb.png | Bin .../phoneScreenshotsThumbs}/5-thumb.png | Bin .../phoneScreenshotsThumbs}/6-thumb.png | Bin .../phoneScreenshotsThumbs}/7-thumb.png | Bin .../phoneScreenshotsThumbs}/8-thumb.png | Bin .../phoneScreenshotsThumbs}/9-thumb.png | Bin .../phoneScreenshotsThumbs}/install-thumb.png | Bin 44 files changed, 23 insertions(+), 23 deletions(-) rename {screenshots => metadata/images/phoneScreenshots}/1.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/10.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/11.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/12.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/13.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/14.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/15.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/1b.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/1c.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/1d.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/1e.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/2.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/3.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/4.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/4b.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/5.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/6.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/7.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/8.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/9.png (100%) rename {screenshots => metadata/images/phoneScreenshots}/install.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/1-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/10-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/11-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/12-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/13-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/14-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/15-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/1b-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/1c-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/1d-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/1e-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/2-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/3-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/4-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/4b-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/5-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/6-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/7-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/8-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/9-thumb.png (100%) rename {screenshots => metadata/images/phoneScreenshotsThumbs}/install-thumb.png (100%) diff --git a/README.md b/README.md index 075dfdbd..d31ad734 100644 --- a/README.md +++ b/README.md @@ -32,27 +32,27 @@ You can either elect to update manually, or get notified on releases by installi # Screenshots -[![install](screenshots/install-thumb.png)](screenshots/install.png) -[![plant list](screenshots/1-thumb.png)](screenshots/1.png) -[![discrete plant list](screenshots/1b-thumb.png)](screenshots/1b.png) -[![discrete plant list](screenshots/1c-thumb.png)](screenshots/1c.png) -[![discrete plant list](screenshots/1d-thumb.png)](screenshots/1d.png) -[![discrete plant list](screenshots/1e-thumb.png)](screenshots/1e.png) -[![plant details](screenshots/2-thumb.png)](screenshots/2.png) -[![feeding](screenshots/3-thumb.png)](screenshots/3.png) -[![nutrients](screenshots/4-thumb.png)](screenshots/4.png) -[![nutrients](screenshots/4b-thumb.png)](screenshots/4b.png) -[![actions](screenshots/5-thumb.png)](screenshots/5.png) -[![pictures](screenshots/6-thumb.png)](screenshots/6.png) -[![statistics](screenshots/7-thumb.png)](screenshots/7.png) -[![past actions](screenshots/8-thumb.png)](screenshots/8.png) -[![action filters](screenshots/9-thumb.png)](screenshots/9.png) -[![action options](screenshots/10-thumb.png)](screenshots/10.png) -[![settings](screenshots/11-thumb.png)](screenshots/11.png) -[![measurements](screenshots/12-thumb.png)](screenshots/12.png) -[![schedules](screenshots/13-thumb.png)](screenshots/13.png) -[![schedule details](screenshots/14-thumb.png)](screenshots/14.png) -[![schedule date](screenshots/15-thumb.png)](screenshots/15.png) +[![install](metadata/images/phoneScreenshotsThumbs/install-thumb.png)](metadata/images/phoneScreenshots/install.png) +[![plant list](metadata/images/phoneScreenshotsThumbs/1-thumb.png)](metadata/images/phoneScreenshots/1.png) +[![discrete plant list](metadata/images/phoneScreenshotsThumbs/1b-thumb.png)](metadata/images/phoneScreenshots/1b.png) +[![discrete plant list](metadata/images/phoneScreenshotsThumbs/1c-thumb.png)](metadata/images/phoneScreenshots/1c.png) +[![discrete plant list](metadata/images/phoneScreenshotsThumbs/1d-thumb.png)](metadata/images/phoneScreenshots/1d.png) +[![discrete plant list](metadata/images/phoneScreenshotsThumbs/1e-thumb.png)](metadata/images/phoneScreenshots/1e.png) +[![plant details](metadata/images/phoneScreenshotsThumbs/2-thumb.png)](metadata/images/phoneScreenshots/2.png) +[![feeding](metadata/images/phoneScreenshotsThumbs/3-thumb.png)](metadata/images/phoneScreenshots/3.png) +[![nutrients](metadata/images/phoneScreenshotsThumbs/4-thumb.png)](metadata/images/phoneScreenshots/4.png) +[![nutrients](metadata/images/phoneScreenshotsThumbs/4b-thumb.png)](metadata/images/phoneScreenshots/4b.png) +[![actions](metadata/images/phoneScreenshotsThumbs/5-thumb.png)](metadata/images/phoneScreenshots/5.png) +[![pictures](metadata/images/phoneScreenshotsThumbs/6-thumb.png)](metadata/images/phoneScreenshots/6.png) +[![statistics](metadata/images/phoneScreenshotsThumbs/7-thumb.png)](metadata/images/phoneScreenshots/7.png) +[![past actions](metadata/images/phoneScreenshotsThumbs/8-thumb.png)](metadata/images/phoneScreenshots/8.png) +[![action filters](metadata/images/phoneScreenshotsThumbs/9-thumb.png)](metadata/images/phoneScreenshots/9.png) +[![action options](metadata/images/phoneScreenshotsThumbs/10-thumb.png)](metadata/images/phoneScreenshots/10.png) +[![settings](metadata/images/phoneScreenshotsThumbs/11-thumb.png)](metadata/images/phoneScreenshots/11.png) +[![measurements](metadata/images/phoneScreenshotsThumbs/12-thumb.png)](metadata/images/phoneScreenshots/12.png) +[![schedules](metadata/images/phoneScreenshotsThumbs/13-thumb.png)](metadata/images/phoneScreenshots/13.png) +[![schedule details](metadata/images/phoneScreenshotsThumbs/14-thumb.png)](metadata/images/phoneScreenshots/14.png) +[![schedule date](metadata/images/phoneScreenshotsThumbs/15-thumb.png)](metadata/images/phoneScreenshots/15.png) # About the app @@ -88,7 +88,7 @@ One of, One of, -`PLANTED`, `GERMINATION`, `CUTTING`, `VEGETATION`, `FLOWER`, `DRYING`, `CURING`, `HARVESTED` +`PLANTED`, `GERMINATION`, `SEEDLING`, `CUTTING`, `VEGETATION`, `FLOWER`, `DRYING`, `CURING`, `HARVESTED` ### Action object (feeding) diff --git a/app/src/main/assets/readme.html b/app/src/main/assets/readme.html index 71279a50..2ba3a884 100644 --- a/app/src/main/assets/readme.html +++ b/app/src/main/assets/readme.html @@ -33,7 +33,7 @@

Medium (ENUM)

One of,

SOIL, HYDRO

Plant Stage (ENUM)

-

One of,

PLANTED, GERMINATION, CUTTING, VEGETATION, FLOWER, CURING, HARVESTED

+

One of,

PLANTED, GERMINATION, SEEDLING, CUTTING, VEGETATION, FLOWER, CURING, HARVESTED

Action object (feeding)

Temperature measured in ºC

diff --git a/screenshots/1.png b/metadata/images/phoneScreenshots/1.png similarity index 100% rename from screenshots/1.png rename to metadata/images/phoneScreenshots/1.png diff --git a/screenshots/10.png b/metadata/images/phoneScreenshots/10.png similarity index 100% rename from screenshots/10.png rename to metadata/images/phoneScreenshots/10.png diff --git a/screenshots/11.png b/metadata/images/phoneScreenshots/11.png similarity index 100% rename from screenshots/11.png rename to metadata/images/phoneScreenshots/11.png diff --git a/screenshots/12.png b/metadata/images/phoneScreenshots/12.png similarity index 100% rename from screenshots/12.png rename to metadata/images/phoneScreenshots/12.png diff --git a/screenshots/13.png b/metadata/images/phoneScreenshots/13.png similarity index 100% rename from screenshots/13.png rename to metadata/images/phoneScreenshots/13.png diff --git a/screenshots/14.png b/metadata/images/phoneScreenshots/14.png similarity index 100% rename from screenshots/14.png rename to metadata/images/phoneScreenshots/14.png diff --git a/screenshots/15.png b/metadata/images/phoneScreenshots/15.png similarity index 100% rename from screenshots/15.png rename to metadata/images/phoneScreenshots/15.png diff --git a/screenshots/1b.png b/metadata/images/phoneScreenshots/1b.png similarity index 100% rename from screenshots/1b.png rename to metadata/images/phoneScreenshots/1b.png diff --git a/screenshots/1c.png b/metadata/images/phoneScreenshots/1c.png similarity index 100% rename from screenshots/1c.png rename to metadata/images/phoneScreenshots/1c.png diff --git a/screenshots/1d.png b/metadata/images/phoneScreenshots/1d.png similarity index 100% rename from screenshots/1d.png rename to metadata/images/phoneScreenshots/1d.png diff --git a/screenshots/1e.png b/metadata/images/phoneScreenshots/1e.png similarity index 100% rename from screenshots/1e.png rename to metadata/images/phoneScreenshots/1e.png diff --git a/screenshots/2.png b/metadata/images/phoneScreenshots/2.png similarity index 100% rename from screenshots/2.png rename to metadata/images/phoneScreenshots/2.png diff --git a/screenshots/3.png b/metadata/images/phoneScreenshots/3.png similarity index 100% rename from screenshots/3.png rename to metadata/images/phoneScreenshots/3.png diff --git a/screenshots/4.png b/metadata/images/phoneScreenshots/4.png similarity index 100% rename from screenshots/4.png rename to metadata/images/phoneScreenshots/4.png diff --git a/screenshots/4b.png b/metadata/images/phoneScreenshots/4b.png similarity index 100% rename from screenshots/4b.png rename to metadata/images/phoneScreenshots/4b.png diff --git a/screenshots/5.png b/metadata/images/phoneScreenshots/5.png similarity index 100% rename from screenshots/5.png rename to metadata/images/phoneScreenshots/5.png diff --git a/screenshots/6.png b/metadata/images/phoneScreenshots/6.png similarity index 100% rename from screenshots/6.png rename to metadata/images/phoneScreenshots/6.png diff --git a/screenshots/7.png b/metadata/images/phoneScreenshots/7.png similarity index 100% rename from screenshots/7.png rename to metadata/images/phoneScreenshots/7.png diff --git a/screenshots/8.png b/metadata/images/phoneScreenshots/8.png similarity index 100% rename from screenshots/8.png rename to metadata/images/phoneScreenshots/8.png diff --git a/screenshots/9.png b/metadata/images/phoneScreenshots/9.png similarity index 100% rename from screenshots/9.png rename to metadata/images/phoneScreenshots/9.png diff --git a/screenshots/install.png b/metadata/images/phoneScreenshots/install.png similarity index 100% rename from screenshots/install.png rename to metadata/images/phoneScreenshots/install.png diff --git a/screenshots/1-thumb.png b/metadata/images/phoneScreenshotsThumbs/1-thumb.png similarity index 100% rename from screenshots/1-thumb.png rename to metadata/images/phoneScreenshotsThumbs/1-thumb.png diff --git a/screenshots/10-thumb.png b/metadata/images/phoneScreenshotsThumbs/10-thumb.png similarity index 100% rename from screenshots/10-thumb.png rename to metadata/images/phoneScreenshotsThumbs/10-thumb.png diff --git a/screenshots/11-thumb.png b/metadata/images/phoneScreenshotsThumbs/11-thumb.png similarity index 100% rename from screenshots/11-thumb.png rename to metadata/images/phoneScreenshotsThumbs/11-thumb.png diff --git a/screenshots/12-thumb.png b/metadata/images/phoneScreenshotsThumbs/12-thumb.png similarity index 100% rename from screenshots/12-thumb.png rename to metadata/images/phoneScreenshotsThumbs/12-thumb.png diff --git a/screenshots/13-thumb.png b/metadata/images/phoneScreenshotsThumbs/13-thumb.png similarity index 100% rename from screenshots/13-thumb.png rename to metadata/images/phoneScreenshotsThumbs/13-thumb.png diff --git a/screenshots/14-thumb.png b/metadata/images/phoneScreenshotsThumbs/14-thumb.png similarity index 100% rename from screenshots/14-thumb.png rename to metadata/images/phoneScreenshotsThumbs/14-thumb.png diff --git a/screenshots/15-thumb.png b/metadata/images/phoneScreenshotsThumbs/15-thumb.png similarity index 100% rename from screenshots/15-thumb.png rename to metadata/images/phoneScreenshotsThumbs/15-thumb.png diff --git a/screenshots/1b-thumb.png b/metadata/images/phoneScreenshotsThumbs/1b-thumb.png similarity index 100% rename from screenshots/1b-thumb.png rename to metadata/images/phoneScreenshotsThumbs/1b-thumb.png diff --git a/screenshots/1c-thumb.png b/metadata/images/phoneScreenshotsThumbs/1c-thumb.png similarity index 100% rename from screenshots/1c-thumb.png rename to metadata/images/phoneScreenshotsThumbs/1c-thumb.png diff --git a/screenshots/1d-thumb.png b/metadata/images/phoneScreenshotsThumbs/1d-thumb.png similarity index 100% rename from screenshots/1d-thumb.png rename to metadata/images/phoneScreenshotsThumbs/1d-thumb.png diff --git a/screenshots/1e-thumb.png b/metadata/images/phoneScreenshotsThumbs/1e-thumb.png similarity index 100% rename from screenshots/1e-thumb.png rename to metadata/images/phoneScreenshotsThumbs/1e-thumb.png diff --git a/screenshots/2-thumb.png b/metadata/images/phoneScreenshotsThumbs/2-thumb.png similarity index 100% rename from screenshots/2-thumb.png rename to metadata/images/phoneScreenshotsThumbs/2-thumb.png diff --git a/screenshots/3-thumb.png b/metadata/images/phoneScreenshotsThumbs/3-thumb.png similarity index 100% rename from screenshots/3-thumb.png rename to metadata/images/phoneScreenshotsThumbs/3-thumb.png diff --git a/screenshots/4-thumb.png b/metadata/images/phoneScreenshotsThumbs/4-thumb.png similarity index 100% rename from screenshots/4-thumb.png rename to metadata/images/phoneScreenshotsThumbs/4-thumb.png diff --git a/screenshots/4b-thumb.png b/metadata/images/phoneScreenshotsThumbs/4b-thumb.png similarity index 100% rename from screenshots/4b-thumb.png rename to metadata/images/phoneScreenshotsThumbs/4b-thumb.png diff --git a/screenshots/5-thumb.png b/metadata/images/phoneScreenshotsThumbs/5-thumb.png similarity index 100% rename from screenshots/5-thumb.png rename to metadata/images/phoneScreenshotsThumbs/5-thumb.png diff --git a/screenshots/6-thumb.png b/metadata/images/phoneScreenshotsThumbs/6-thumb.png similarity index 100% rename from screenshots/6-thumb.png rename to metadata/images/phoneScreenshotsThumbs/6-thumb.png diff --git a/screenshots/7-thumb.png b/metadata/images/phoneScreenshotsThumbs/7-thumb.png similarity index 100% rename from screenshots/7-thumb.png rename to metadata/images/phoneScreenshotsThumbs/7-thumb.png diff --git a/screenshots/8-thumb.png b/metadata/images/phoneScreenshotsThumbs/8-thumb.png similarity index 100% rename from screenshots/8-thumb.png rename to metadata/images/phoneScreenshotsThumbs/8-thumb.png diff --git a/screenshots/9-thumb.png b/metadata/images/phoneScreenshotsThumbs/9-thumb.png similarity index 100% rename from screenshots/9-thumb.png rename to metadata/images/phoneScreenshotsThumbs/9-thumb.png diff --git a/screenshots/install-thumb.png b/metadata/images/phoneScreenshotsThumbs/install-thumb.png similarity index 100% rename from screenshots/install-thumb.png rename to metadata/images/phoneScreenshotsThumbs/install-thumb.png From 0925001d4fda2697d3bd469d2ecd5a245c190d00 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 22 Jun 2019 13:24:00 +0100 Subject: [PATCH 26/27] Bumps versioncode --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index e54e8101..063d010a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { applicationId "me.anon.grow" minSdkVersion 17 targetSdkVersion 28 - versionCode 20 - versionName "2.4.1" + versionCode 21 + versionName "2.5" javaCompileOptions { annotationProcessorOptions { From eaae13ded2709920be6955dd2ec1cdb349e4c366 Mon Sep 17 00:00:00 2001 From: 7LPdWcaW <7LPdWcaW@gmail.com> Date: Sat, 22 Jun 2019 13:37:28 +0100 Subject: [PATCH 27/27] Updates readme for new binaries --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d31ad734..6d08fc50 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ Welcome to grow tracker. This app was created to help record data about growing plants in order to monitor the growing conditions to help make the plants grow better, and identify potential issues during the grow process. -[Latest APK: (MD5) 0afd3816fd71d77282f2a48efa265b71 v2.4.1](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.4.1/v2.4.1-production.apk) +[Latest APK: (MD5) aa3496894c364a766145a576122e3ee6 v2.5](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.5/v2.5-production.apk) -[Latest APK (Discrete): (MD5) 075a079e1e7697db54cf86180fc56e58 v2.4.1](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.4.1/v2.4.1-discrete.apk) +[Latest APK (Discrete): (MD5) 0f68525415eb2fc3c77184cdb3c51443 v2.5](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.5/v2.5-discrete.apk) [Get it on F-Droid with automatic updates](https://f-droid.org/packages/me.anon.grow/)