diff --git a/README.md b/README.md index 83b0681c..075dfdbd 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,12 @@ -# Read me +# GrowTracker 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) 45ed03e8b120c6be233a30e743f61e92 v2.4](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.4/v2.4-production.apk) +[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 (Discrete): (MD5) e154652199c8598c2457f5b606237e1b v2.4](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.4/v2.4-discrete.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) + +[Get it on F-Droid with automatic updates](https://f-droid.org/packages/me.anon.grow/) You can follow development, post questions, or grow logs in the [Subreddit](https://reddit.com/r/growutils) diff --git a/app/build.gradle b/app/build.gradle index 0608b618..e54e8101 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { applicationId "me.anon.grow" minSdkVersion 17 targetSdkVersion 28 - versionCode 19 - versionName "2.4" + versionCode 20 + versionName "2.4.1" javaCompileOptions { annotationProcessorOptions { 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 651bce10..f6e96659 100644 --- a/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java +++ b/app/src/main/java/me/anon/controller/adapter/ActionAdapter.java @@ -3,6 +3,7 @@ import android.app.AlertDialog; import android.content.DialogInterface; import android.preference.PreferenceManager; +import android.support.annotation.Nullable; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; import android.text.Html; @@ -58,12 +59,20 @@ public interface OnActionSelectListener public void onActionDuplicate(Action action); } + public interface OnItemSelectCallback + { + public void onItemSelected(Action action); + } + + private OnItemSelectCallback onItemSelectCallback; private OnActionSelectListener onActionSelectListener; - private Plant plant; + @Nullable private Plant plant; private List actions = new ArrayList<>(); private Unit measureUnit, deliveryUnit; private TempUnit tempUnit; private boolean usingEc = false; + private boolean showDate = true; + private boolean showActions = true; /** * Dummy image action placeholder class @@ -83,6 +92,21 @@ public void setOnActionSelectListener(OnActionSelectListener onActionSelectListe this.onActionSelectListener = onActionSelectListener; } + public void setOnItemSelectCallback(OnItemSelectCallback onItemSelectCallback) + { + this.onItemSelectCallback = onItemSelectCallback; + } + + public void setShowDate(boolean showDate) + { + this.showDate = showDate; + } + + public void setShowActions(boolean showActions) + { + this.showActions = showActions; + } + public Plant getPlant() { return plant; @@ -114,7 +138,12 @@ public TempUnit getTempUnit() return tempUnit; } - public void setActions(Plant plant, List actions) + public void setActions(@Nullable Plant plant, ArrayList actions) + { + setActions(plant, actions, new ArrayList()); + } + + public void setActions(@Nullable Plant plant, ArrayList actions, ArrayList exclude) { this.plant = plant; this.actions = new ArrayList<>(); @@ -123,41 +152,44 @@ public void setActions(Plant plant, List actions) Collections.reverse(actions); for (Action item : actions) { - ArrayList groupedImages = new ArrayList<>(); - for (String image : plant.getImages()) + if (plant != null) { - long imageDate = getImageDate(image); - - if (imageDate <= item.getDate() && !addedImages.contains(image)) + ArrayList groupedImages = new ArrayList<>(); + for (String image : plant.getImages()) { - groupedImages.add(image); - addedImages.add(image); + long imageDate = getImageDate(image); + + if (imageDate <= item.getDate() && !addedImages.contains(image)) + { + groupedImages.add(image); + addedImages.add(image); + } } - } - if (!groupedImages.isEmpty()) - { - Collections.sort(groupedImages, new Comparator() + if (!groupedImages.isEmpty()) { - @Override public int compare(String o1, String o2) + Collections.sort(groupedImages, new Comparator() { - long o1Date = getImageDate(o1); - long o2Date = getImageDate(o2); + @Override public int compare(String o1, String o2) + { + long o1Date = getImageDate(o1); + long o2Date = getImageDate(o2); - if (o2Date < o1Date) return -1; - if (o2Date > o1Date) return 1; - return 0; - } - }); - ImageAction imageAction = new ImageAction(); - imageAction.images = groupedImages; - this.actions.add(imageAction); + if (o2Date < o1Date) return -1; + if (o2Date > o1Date) return 1; + return 0; + } + }); + ImageAction imageAction = new ImageAction(); + imageAction.images = groupedImages; + this.actions.add(imageAction); + } } this.actions.add(item); } - if (addedImages.size() != plant.getImages().size()) + if (plant != null && addedImages.size() != plant.getImages().size()) { ArrayList remainingImages = new ArrayList<>(plant.getImages()); remainingImages.removeAll(addedImages); @@ -316,147 +348,181 @@ else if (action instanceof StageChange) viewHolder.getSummary().setVisibility(View.VISIBLE); } - viewHolder.getOverflow().setOnClickListener(new View.OnClickListener() + if (showActions) { - @Override public void onClick(final View v) + viewHolder.getOverflow().setVisibility(View.VISIBLE); + viewHolder.getOverflow().setOnClickListener(new View.OnClickListener() { - PopupMenu menu = new PopupMenu(v.getContext(), v, Gravity.BOTTOM); - menu.inflate(R.menu.event_overflow); - menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() + @Override public void onClick(final View v) { - @Override public boolean onMenuItemClick(MenuItem item) + PopupMenu menu = new PopupMenu(v.getContext(), v, Gravity.BOTTOM); + menu.inflate(R.menu.event_overflow); + menu.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() { - if (item.getItemId() == R.id.duplicate) + @Override public boolean onMenuItemClick(MenuItem item) { - if (onActionSelectListener != null) + if (item.getItemId() == R.id.duplicate) { - Kryo kryo = new Kryo(); - onActionSelectListener.onActionDuplicate(kryo.copy(action)); - } + if (onActionSelectListener != null) + { + Kryo kryo = new Kryo(); + onActionSelectListener.onActionDuplicate(kryo.copy(action)); + } - return true; - } - else if (item.getItemId() == R.id.copy) - { - if (onActionSelectListener != null) - { - Kryo kryo = new Kryo(); - onActionSelectListener.onActionCopy(kryo.copy(action)); + return true; } - - return true; - } - else if (item.getItemId() == R.id.edit) - { - if (onActionSelectListener != null) + else if (item.getItemId() == R.id.copy) { - onActionSelectListener.onActionEdit(action); - } + if (onActionSelectListener != null) + { + Kryo kryo = new Kryo(); + onActionSelectListener.onActionCopy(kryo.copy(action)); + } - return true; - } - else if (item.getItemId() == R.id.delete) - { - new AlertDialog.Builder(v.getContext()) - .setTitle("Delete this event?") - .setMessage("Are you sure you want to delete " + viewHolder.getName().getText()) - .setPositiveButton("Yes", new DialogInterface.OnClickListener() + return true; + } + else if (item.getItemId() == R.id.edit) + { + if (onActionSelectListener != null) { - @Override public void onClick(DialogInterface dialog, int which) + onActionSelectListener.onActionEdit(action); + } + + return true; + } + else if (item.getItemId() == R.id.delete) + { + new AlertDialog.Builder(v.getContext()) + .setTitle("Delete this event?") + .setMessage("Are you sure you want to delete " + viewHolder.getName().getText()) + .setPositiveButton("Yes", new DialogInterface.OnClickListener() { - if (onActionSelectListener != null) + @Override public void onClick(DialogInterface dialog, int which) { - onActionSelectListener.onActionDeleted(action); + if (onActionSelectListener != null) + { + onActionSelectListener.onActionDeleted(action); + } } - } - }) - .setNegativeButton("No", null) - .show(); + }) + .setNegativeButton("No", null) + .show(); - return true; - } + return true; + } - return false; - } - }); + return false; + } + }); - menu.show(); - } - }); + menu.show(); + } + }); + } + else + { + viewHolder.getOverflow().setVisibility(View.GONE); + } } - // plant date & stage - Date actionDate = new Date(action.getDate()); - Calendar actionCalendar = GregorianCalendar.getInstance(); - actionCalendar.setTime(actionDate); - - String dateDayStr = actionCalendar.get(Calendar.DAY_OF_MONTH) + " " + actionCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()); - - if (dateDay != null) + if (showDate) { - String lastDateStr = ""; + // plant date & stage + Date actionDate = new Date(action.getDate()); + Calendar actionCalendar = GregorianCalendar.getInstance(); + actionCalendar.setTime(actionDate); - if (index - 1 >= 0) - { - Date lastActionDate = new Date(actions.get(index - 1).getDate()); - Calendar lastActionCalendar = GregorianCalendar.getInstance(); - lastActionCalendar.setTime(lastActionDate); - lastDateStr = lastActionCalendar.get(Calendar.DAY_OF_MONTH) + " " + lastActionCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()); - } + String dateDayStr = actionCalendar.get(Calendar.DAY_OF_MONTH) + " " + actionCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()); - if (!lastDateStr.equalsIgnoreCase(dateDayStr)) + if (dateDay != null) { - dateDay.setText(Html.fromHtml(dateDayStr)); + String lastDateStr = ""; - String stageDayStr = ""; - StageChange current = null; - StageChange previous = null; + if (index - 1 >= 0) + { + Date lastActionDate = new Date(actions.get(index - 1).getDate()); + Calendar lastActionCalendar = GregorianCalendar.getInstance(); + lastActionCalendar.setTime(lastActionDate); + lastDateStr = lastActionCalendar.get(Calendar.DAY_OF_MONTH) + " " + lastActionCalendar.getDisplayName(Calendar.MONTH, Calendar.SHORT, Locale.getDefault()); + } - for (int actionIndex = index; actionIndex < actions.size(); actionIndex++) + if (!lastDateStr.equalsIgnoreCase(dateDayStr)) { - if (actions.get(actionIndex) instanceof StageChange) + dateDay.setText(Html.fromHtml(dateDayStr)); + + String stageDayStr = ""; + StageChange current = null; + StageChange previous = null; + + for (int actionIndex = index; actionIndex < actions.size(); actionIndex++) { - if (current == null) + if (actions.get(actionIndex) instanceof StageChange) { - current = (StageChange)actions.get(actionIndex); + if (current == null) + { + current = (StageChange)actions.get(actionIndex); + } + else if (previous == null) + { + previous = (StageChange)actions.get(actionIndex); + } } - else if (previous == null) + } + + if (plant != null) + { + int totalDays = (int)TimeHelper.toDays(Math.abs(action.getDate() - plant.getPlantDate())); + stageDayStr += totalDays; + + if (previous == null) { - previous = (StageChange)actions.get(actionIndex); + previous = current; } - } - } - int totalDays = (int)TimeHelper.toDays(Math.abs(action.getDate() - plant.getPlantDate())); - stageDayStr += 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 (previous == null) - { - previous = current; + stageDay.setText(stageDayStr); } - - if (current != null) + else { - 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(); - } + dateDay.setText(""); + stageDay.setText(""); } - stageDay.setText(stageDayStr); + ((View)dateDay.getParent()).setVisibility(View.VISIBLE); } - else + } + else + { + if (dateDay != null && stageDay != null) { - dateDay.setText(""); - stageDay.setText(""); + ((View)dateDay.getParent()).setVisibility(View.GONE); } } + + if (onItemSelectCallback != null) + { + vh.itemView.setOnClickListener(new View.OnClickListener() + { + @Override public void onClick(View v) + { + onItemSelectCallback.onItemSelected(action); + } + }); + } } @Override public int getItemCount() diff --git a/app/src/main/java/me/anon/grow/BootActivity.java b/app/src/main/java/me/anon/grow/BootActivity.java index 3ba6d858..5378f10f 100644 --- a/app/src/main/java/me/anon/grow/BootActivity.java +++ b/app/src/main/java/me/anon/grow/BootActivity.java @@ -5,8 +5,10 @@ import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; +import android.support.v4.content.FileProvider; import android.text.Html; import android.util.Base64; import android.widget.Toast; @@ -49,10 +51,19 @@ public class BootActivity extends Activity for (String file : exceptions) { File fileIn = new File(ExceptionHandler.getInstance().getFilesPath() + "/" + file); - Uri u = Uri.fromFile(fileIn); + Uri u = FileProvider.getUriForFile(BootActivity.this, getPackageName() + ".provider", fileIn); uris.add(u); } + String deviceInfo = ""; + deviceInfo += Build.BRAND + " "; + deviceInfo += Build.MODEL + " "; + deviceInfo += Build.DEVICE + ", "; + deviceInfo += Build.MANUFACTURER + ", "; + deviceInfo += Build.VERSION.SDK_INT + ", "; + deviceInfo += BuildConfig.VERSION_NAME; + + share.putExtra(Intent.EXTRA_TEXT, deviceInfo); share.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris); startActivity(Intent.createChooser(share, "Send mail...")); sentIntent = true; diff --git a/app/src/main/java/me/anon/grow/MainActivity.java b/app/src/main/java/me/anon/grow/MainActivity.java index 95fc33d2..64597802 100644 --- a/app/src/main/java/me/anon/grow/MainActivity.java +++ b/app/src/main/java/me/anon/grow/MainActivity.java @@ -2,7 +2,10 @@ import android.Manifest; import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; import android.content.Intent; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; @@ -67,6 +70,7 @@ public NavigationView getNavigation() setSupportActionBar(toolbar); setNavigationView(); showDrawerToggle(); + showUpdateDialog(); if (savedInstanceState == null) { @@ -109,6 +113,31 @@ public NavigationView getNavigation() BusHelper.getInstance().register(this); } + public void showUpdateDialog() + { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); + int lastVersion = prefs.getInt("last_version", -1); + if (lastVersion != BuildConfig.VERSION_CODE && lastVersion != -1) + { + new AlertDialog.Builder(this) + .setTitle(R.string.update_dialog_title) + .setMessage(getString(R.string.update_dialog_message, BuildConfig.VERSION_NAME)) + .setPositiveButton(R.string.update_dialog_view_changes_button, new DialogInterface.OnClickListener() + { + @Override public void onClick(DialogInterface dialog, int which) + { + Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setData(Uri.parse("https://github.com/7LPdWcaW/GrowTracker-Android/releases/tag/v" + BuildConfig.VERSION_NAME)); + startActivity(intent); + } + }) + .setNegativeButton(R.string.update_dialog_dismiss_button, null) + .show(); + } + + prefs.edit().putInt("last_version", BuildConfig.VERSION_CODE).apply(); + } + public void setNavigationView() { navigation.getMenu().clear(); diff --git a/app/src/main/java/me/anon/grow/ScheduleDateDetailsActivity.kt b/app/src/main/java/me/anon/grow/ScheduleDateDetailsActivity.kt index eb88ea6f..e6306da1 100644 --- a/app/src/main/java/me/anon/grow/ScheduleDateDetailsActivity.kt +++ b/app/src/main/java/me/anon/grow/ScheduleDateDetailsActivity.kt @@ -21,6 +21,7 @@ class ScheduleDateDetailsActivity : AppCompatActivity() setContentView(R.layout.fragment_holder) setSupportActionBar(toolbar) + setTitle(R.string.schedule_date_title) if (fragmentManager.findFragmentByTag(TAG_FRAGMENT) == null) { diff --git a/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java b/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java new file mode 100644 index 00000000..ac18d56b --- /dev/null +++ b/app/src/main/java/me/anon/grow/fragment/ActionSelectDialogFragment.java @@ -0,0 +1,107 @@ +package me.anon.grow.fragment; + +import android.annotation.SuppressLint; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.os.Bundle; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.widget.LinearLayout; + +import java.util.ArrayList; + +import me.anon.controller.adapter.ActionAdapter; +import me.anon.grow.R; +import me.anon.lib.Views; +import me.anon.model.Action; +import me.anon.view.ImageActionHolder; + +/** + * // TODO: Add class description + * + * @author 7LPdWcaW + * @documentation // TODO Reference flow doc + * @project GrowTracker + */ +@Views.Injectable +public class ActionSelectDialogFragment extends DialogFragment +{ + public interface OnActionSelectedListener + { + public void onActionSelected(Action action); + } + + @Views.InjectView(R.id.recycler_view) private RecyclerView recyclerView; + private ActionAdapter adapter; + private OnActionSelectedListener onActionSelected; + + public void setOnActionSelectedListener(OnActionSelectedListener onActionSelected) + { + this.onActionSelected = onActionSelected; + } + + @SuppressLint("ValidFragment") + public ActionSelectDialogFragment(ArrayList actions) + { + ArrayList exclude = new ArrayList<>(); + exclude.add(ImageActionHolder.class); + + adapter = new ActionAdapter() + { + @Override public void onBindViewHolder(RecyclerView.ViewHolder vh, int index) + { + super.onBindViewHolder(vh, index); + int padding = (int)getResources().getDimension(R.dimen.padding_8dp); + vh.itemView.setPadding(padding, padding, padding, padding); + } + }; + + adapter.setShowDate(false); + adapter.setShowActions(false); + adapter.setActions(null, actions, exclude); + } + + + @SuppressLint("ValidFragment") + public ActionSelectDialogFragment() + { + } + + @Override public Dialog onCreateDialog(Bundle savedInstanceState) + { + View view = getActivity().getLayoutInflater().inflate(R.layout.action_list_dialog_view, null, false); + Views.inject(this, view); + + recyclerView.setAdapter(adapter); + recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); + recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), LinearLayout.VERTICAL)); + + final AlertDialog dialog = new AlertDialog.Builder(getActivity()) + .setTitle("Actions") + .setView(view) + .create(); + + adapter.setOnItemSelectCallback(new ActionAdapter.OnItemSelectCallback() + { + @Override public void onItemSelected(Action action) + { + if (onActionSelected != null) + { + onActionSelected.onActionSelected(action); + } + + dialog.dismiss(); + } + }); + + dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); + + return dialog; + } +} diff --git a/app/src/main/java/me/anon/grow/fragment/FeedingSelectDialogFragment.java b/app/src/main/java/me/anon/grow/fragment/FeedingScheduleSelectDialogFragment.java similarity index 93% rename from app/src/main/java/me/anon/grow/fragment/FeedingSelectDialogFragment.java rename to app/src/main/java/me/anon/grow/fragment/FeedingScheduleSelectDialogFragment.java index 43ff2578..fa0b7b9a 100644 --- a/app/src/main/java/me/anon/grow/fragment/FeedingSelectDialogFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/FeedingScheduleSelectDialogFragment.java @@ -33,7 +33,7 @@ * @project GrowTracker */ @Views.Injectable -public class FeedingSelectDialogFragment extends DialogFragment +public class FeedingScheduleSelectDialogFragment extends DialogFragment { public interface OnFeedingSelectedListener { @@ -52,7 +52,7 @@ public void setOnFeedingSelectedListener(OnFeedingSelectedListener onFeedingSele } @SuppressLint("ValidFragment") - public FeedingSelectDialogFragment(FeedingSchedule schedule, Plant plant) + public FeedingScheduleSelectDialogFragment(FeedingSchedule schedule, Plant plant) { this.plant = plant; this.schedule = schedule; @@ -60,7 +60,7 @@ public FeedingSelectDialogFragment(FeedingSchedule schedule, Plant plant) @SuppressLint("ValidFragment") - public FeedingSelectDialogFragment() + public FeedingScheduleSelectDialogFragment() { } @@ -77,7 +77,7 @@ public FeedingSelectDialogFragment() recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), LinearLayout.VERTICAL)); final AlertDialog dialog = new AlertDialog.Builder(getActivity()) - .setTitle("Feedings") + .setTitle("Feeding schedule") .setView(view) .create(); 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 17ff07e5..6edd47b3 100644 --- a/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/PlantDetailsFragment.java @@ -441,6 +441,7 @@ else if (requestCode == 2) { if (resultCode != Activity.RESULT_CANCELED) { + setLastFeeding(); SnackBar.show(getActivity(), "Watering added", "Apply to another plant", new SnackBarListener() { @Override public void onSnackBarStarted(Object o) 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 ee3dec64..30030a1b 100644 --- a/app/src/main/java/me/anon/grow/fragment/StatisticsFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/StatisticsFragment.java @@ -2,6 +2,7 @@ import android.app.Fragment; import android.os.Bundle; +import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.MenuItem; @@ -86,6 +87,7 @@ public static StatisticsFragment newInstance(int plantIndex) @Views.InjectView(R.id.max_input_ph) private TextView maxInputPh; @Views.InjectView(R.id.ave_input_ph) private TextView aveInputPh; + @Views.InjectView(R.id.ppm_title) private TextView ppmTitle; @Views.InjectView(R.id.min_ppm) private TextView minppm; @Views.InjectView(R.id.max_ppm) private TextView maxppm; @Views.InjectView(R.id.ave_ppm) private TextView aveppm; @@ -96,6 +98,7 @@ public static StatisticsFragment newInstance(int plantIndex) @Views.InjectView(R.id.ave_temp) private TextView avetemp; private Set checkedAdditives = null; + private boolean usingEc = false; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -114,6 +117,7 @@ public static StatisticsFragment newInstance(int plantIndex) checkedAdditives = new HashSet<>(savedInstanceState.getStringArrayList("checked_additives")); } + usingEc = PreferenceManager.getDefaultSharedPreferences(getActivity()).getBoolean("tds_ec", false); getActivity().setTitle("Plant statistics"); if (getArguments() != null) @@ -136,11 +140,16 @@ public static StatisticsFragment newInstance(int plantIndex) aveInputPh.setText(inputAdditional[2]); String[] ppmAdditional = new String[3]; - StatsHelper.setPpmData(plant, ppm, ppmAdditional); + StatsHelper.setPpmData(plant, ppm, ppmAdditional, usingEc); minppm.setText(ppmAdditional[0].equals(String.valueOf(Long.MAX_VALUE)) ? "0" : ppmAdditional[0]); maxppm.setText(ppmAdditional[1].equals(String.valueOf(Long.MIN_VALUE)) ? "0" : ppmAdditional[1]); aveppm.setText(ppmAdditional[2]); + if (usingEc) + { + ppmTitle.setText("EC"); + } + setAdditiveStats(); tempContainer.setVisibility(View.VISIBLE); 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 510054de..d6604aa2 100644 --- a/app/src/main/java/me/anon/grow/fragment/WateringFragment.java +++ b/app/src/main/java/me/anon/grow/fragment/WateringFragment.java @@ -203,14 +203,39 @@ public static WateringFragment newInstance(int[] plantIndex, int feedingIndex) }) .show(); } + else if (item.getItemId() == R.id.action_populate_previous) + { + ArrayList items = new ArrayList<>(); + for (Plant plant : plants) + { + for (Action action : plant.getActions()) + { + if (action.getClass() == Water.class) + { + items.add((Water)action); + } + } + } + + ActionSelectDialogFragment actionSelectDialogFragment = new ActionSelectDialogFragment(items); + actionSelectDialogFragment.setOnActionSelectedListener(new ActionSelectDialogFragment.OnActionSelectedListener() + { + @Override public void onActionSelected(Action action) + { + water = (Water)new Kryo().copy(action); + setUi(); + } + }); + actionSelectDialogFragment.show(getFragmentManager(), "actions"); + } return super.onOptionsItemSelected(item); } private void showScheduleDialog(FeedingSchedule schedule) { - FeedingSelectDialogFragment feedingSelectDialogFragment = new FeedingSelectDialogFragment(schedule, plants.get(0)); - feedingSelectDialogFragment.setOnFeedingSelectedListener(new FeedingSelectDialogFragment.OnFeedingSelectedListener() + FeedingScheduleSelectDialogFragment feedingScheduleSelectDialogFragment = new FeedingScheduleSelectDialogFragment(schedule, plants.get(0)); + feedingScheduleSelectDialogFragment.setOnFeedingSelectedListener(new FeedingScheduleSelectDialogFragment.OnFeedingSelectedListener() { @Override public void onFeedingSelected(FeedingScheduleDate date) { @@ -218,7 +243,7 @@ private void showScheduleDialog(FeedingSchedule schedule) populateAdditives(); } }); - feedingSelectDialogFragment.show(getFragmentManager(), "feeding"); + feedingScheduleSelectDialogFragment.show(getFragmentManager(), "feeding"); } private void setHints() @@ -228,8 +253,8 @@ private void setHints() if (usingEc) { - waterPpm.setHint("1.0 EC"); - ((TextView)((ViewGroup)waterPpm.getParent()).findViewById(R.id.ppm_label)).setText("EC"); + waterPpm.setHint("1.0 " + (usingEc ? "EC" : "PPM")); + ((TextView)((ViewGroup)waterPpm.getParent()).findViewById(R.id.ppm_label)).setText(usingEc ? "EC" : "PPM"); } if (water != null) @@ -294,11 +319,11 @@ private void setHints() } } - averagePh = averagePh / phCount; - averagePpm = averagePpm / ppmCount; - averageRunoff = averageRunoff / runoffCount; - averageAmount = averageAmount / amountCount; - averageTemp = averageTemp / tempCount; + averagePh = Unit.toTwoDecimalPlaces(averagePh / phCount); + averagePpm = Unit.toTwoDecimalPlaces(averagePpm / ppmCount); + averageRunoff = Unit.toTwoDecimalPlaces(averageRunoff / runoffCount); + averageAmount = Unit.toTwoDecimalPlaces(averageAmount / amountCount); + averageTemp = Unit.toTwoDecimalPlaces(averageTemp / tempCount); if (!averagePh.isNaN()) { @@ -307,7 +332,12 @@ private void setHints() if (!averagePpm.isNaN()) { - waterPpm.setHint(String.valueOf(averagePpm)); + if (usingEc) + { + averagePpm = Unit.toTwoDecimalPlaces((averagePpm * 2d) / 1000d); + } + + waterPpm.setHint(String.valueOf(averagePpm) + " " + (usingEc ? "EC" : "PPM")); } if (!averageRunoff.isNaN()) @@ -335,6 +365,14 @@ private void setHints() private void setUi() { + waterPh.setText(""); + waterPpm.setText(""); + runoffPh.setText(""); + amount.setText(""); + temp.setText(""); + date.setText(""); + notes.setText(""); + getActivity().setTitle("Feeding " + (plants.size() == 1 ? plants.get(0).getName() : "multiple plants")); Calendar date = Calendar.getInstance(); @@ -485,9 +523,9 @@ private void populateAdditives() } final View focus = getActivity().getCurrentFocus(); - final Object currentTag = view.getTag(); FragmentManager fm = getFragmentManager(); - AddAdditiveDialogFragment addAdditiveDialogFragment = new AddAdditiveDialogFragment(view.getTag() instanceof Additive ? (Additive)view.getTag() : null); + final Additive current = view.getTag() instanceof Additive ? (Additive)view.getTag() : null; + AddAdditiveDialogFragment addAdditiveDialogFragment = new AddAdditiveDialogFragment(current); addAdditiveDialogFragment.setOnAdditiveSelectedListener(new AddAdditiveDialogFragment.OnAdditiveSelectedListener() { @Override public void onAdditiveSelected(Additive additive) @@ -497,7 +535,7 @@ private void populateAdditives() return; } - if (!water.getAdditives().contains(additive)) + if (!water.getAdditives().contains(current)) { water.getAdditives().add(additive); } @@ -507,7 +545,7 @@ private void populateAdditives() { Object tag = additiveContainer.getChildAt(childIndex).getTag(); - if (tag == currentTag) + if (tag == current) { water.getAdditives().set(childIndex, additive); break; @@ -526,13 +564,13 @@ private void populateAdditives() @Override public void onAdditiveDeleteRequested(Additive additive) { - water.getAdditives().remove(additive); + water.getAdditives().remove(current); for (int childIndex = 0; childIndex < additiveContainer.getChildCount(); childIndex++) { Object tag = additiveContainer.getChildAt(childIndex).getTag(); - if (tag == additive) + if (tag == current) { additiveContainer.removeViewAt(childIndex); break; diff --git a/app/src/main/java/me/anon/lib/Unit.java b/app/src/main/java/me/anon/lib/Unit.java index cab6b05a..e794ee65 100644 --- a/app/src/main/java/me/anon/lib/Unit.java +++ b/app/src/main/java/me/anon/lib/Unit.java @@ -177,7 +177,7 @@ public String getLabel() public static Double toTwoDecimalPlaces(double input) { - return new BigDecimal(input).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); + return Double.isInfinite(input) || Double.isNaN(input) ? 0.0d : new BigDecimal(input).setScale(2, RoundingMode.HALF_EVEN).doubleValue(); } /** 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 60e2739d..732024e9 100644 --- a/app/src/main/java/me/anon/lib/helper/ExportHelper.java +++ b/app/src/main/java/me/anon/lib/helper/ExportHelper.java @@ -9,6 +9,7 @@ import android.os.AsyncTask; import android.os.Build; import android.os.Environment; +import android.preference.PreferenceManager; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; @@ -69,6 +70,7 @@ public class ExportHelper String folderPath = ""; Unit measureUnit = Unit.getSelectedMeasurementUnit(context); Unit deliveryUnit = Unit.getSelectedDeliveryUnit(context); + boolean usingEc = PreferenceManager.getDefaultSharedPreferences(context).getBoolean("tds_ec", false); if (Environment.getExternalStorageDirectory() != null) { @@ -179,12 +181,12 @@ public class ExportHelper plantDetails.append(NEW_LINE); String[] avePpm = new String[3]; - StatsHelper.setPpmData(plant, null, avePpm); - plantDetails.append(" - *Minimum input ppm*: ").append(avePpm[0]); + StatsHelper.setPpmData(plant, null, avePpm, usingEc); + plantDetails.append(" - *Minimum input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[0]); plantDetails.append(NEW_LINE); - plantDetails.append(" - *Maximum input ppm*: ").append(avePpm[1]); + plantDetails.append(" - *Maximum input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[1]); plantDetails.append(NEW_LINE); - plantDetails.append(" - *Average input ppm*: ").append(avePpm[2]); + plantDetails.append(" - *Average input " + (usingEc ? "EC" : "ppm") + "*: ").append(avePpm[2]); plantDetails.append(NEW_LINE); String[] aveTemp = new String[3]; @@ -404,12 +406,12 @@ else if (action instanceof StageChange) ppm.measure(widthMeasureSpec, heightMeasureSpec); ppm.requestLayout(); ppm.layout(0, 0, width, height); - StatsHelper.setPpmData(plant, ppm, null); + StatsHelper.setPpmData(plant, ppm, null, usingEc); ppm.getData().setDrawValues(true); try { - OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/ppm.png"); + OutputStream stream = new FileOutputStream(tempFolder.getAbsolutePath() + "/" + (usingEc ? "ec" : "ppm") + ".png"); ppm.getChartBitmap().compress(Bitmap.CompressFormat.PNG, 100, stream); stream.close(); @@ -458,6 +460,11 @@ else if (action instanceof StageChange) 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); 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 28b3e1e2..34a2bca9 100644 --- a/app/src/main/java/me/anon/lib/helper/StatsHelper.java +++ b/app/src/main/java/me/anon/lib/helper/StatsHelper.java @@ -317,8 +317,9 @@ public static void setInputData(Plant plant, LineChart chart, String[] additiona * @param plant The plant * @param chart The chart to set the data * @param additionalRef Pass-by-reference value for min/max/ave for the generated values. Must be length of 3 if not null + * @param usingEc If using EC measurements = (ppm * 1000d) / 2d */ - public static void setPpmData(Plant plant, LineChart chart, String[] additionalRef) + public static void setPpmData(Plant plant, LineChart chart, String[] additionalRef, boolean usingEc) { ArrayList vals = new ArrayList<>(); ArrayList xVals = new ArrayList<>(); @@ -334,7 +335,15 @@ public static void setPpmData(Plant plant, LineChart chart, String[] additionalR { if (action instanceof Water && ((Water)action).getPpm() != null) { - vals.add(new Entry(((Water)action).getPpm().floatValue(), index++)); + float value = ((Water)action).getPpm().floatValue(); + + if (usingEc) + { + // PPM -> EC + value = (value * 2.0f) / 1000.0f; + } + + vals.add(new Entry(value, index++)); PlantStage stage = null; long changeDate = 0; ListIterator iterator = new ArrayList(stageTimes.keySet()).listIterator(stageTimes.size()); @@ -359,15 +368,15 @@ public static void setPpmData(Plant plant, LineChart chart, String[] additionalR xVals.add(""); } - min = Math.min(min, ((Water)action).getPpm().longValue()); - max = Math.max(max, ((Water)action).getPpm().longValue()); - total += ((Water)action).getPpm(); + min = Math.min(min, (long)value); + max = Math.max(max, (long)value); + total += value; } } if (chart != null) { - LineDataSet dataSet = new LineDataSet(vals, "PPM"); + LineDataSet dataSet = new LineDataSet(vals, usingEc ? "EC" : "PPM"); styleDataset(dataSet, Color.parseColor(chart.getContext().getResources().getStringArray(R.array.stats_colours)[0])); styleGraph(chart); chart.setMarkerView(new MarkerView(chart.getContext(), R.layout.chart_marker) diff --git a/app/src/main/res/layout-land/statistics_view.xml b/app/src/main/res/layout-land/statistics_view.xml index 5f76f640..d9a0dbd8 100644 --- a/app/src/main/res/layout-land/statistics_view.xml +++ b/app/src/main/res/layout-land/statistics_view.xml @@ -474,6 +474,7 @@ - - - + > + + + + + + + + diff --git a/app/src/main/res/layout/statistics_view.xml b/app/src/main/res/layout/statistics_view.xml index fc66af54..5a2a9d16 100644 --- a/app/src/main/res/layout/statistics_view.xml +++ b/app/src/main/res/layout/statistics_view.xml @@ -509,6 +509,7 @@ + + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml new file mode 100644 index 00000000..abfaf7da --- /dev/null +++ b/app/src/main/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + + 8dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d76736b0..09a8d995 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,6 +1,7 @@ Schedules Schedule Details + Schedule Date Name Light feeding Description @@ -15,6 +16,7 @@ To date To stage Populate from schedule + Populate from previous Are you sure? yes no @@ -24,4 +26,8 @@ Schedule deleted Undo Feeding schedules + App updated + App has been updated to version %s + View changes + Dismiss