diff --git a/README.md b/README.md
index e3efad62..65a5e402 100644
--- a/README.md
+++ b/README.md
@@ -6,11 +6,11 @@ Welcome to grow tracker. This is a utility app designed for gardening and tracki
[Latest Nightly Build (Experimental!)](https://github.com/7LPdWcaW/GrowTracker-Android/releases/tag/alpha)
-[Latest APK: (SHA256) 4009957e99cc7e0d8a4d1fd9ef8f724cbd65566eb776c94350b6ed1e8a405275 v2.6](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.6/v2.6-production.apk)
+[Latest APK: (SHA256) 501786b7350eceb7b894a5745c06c378f1d2f2e6f4bf659ee2576b3dfaca5732 v2.6.1](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.6.1/v2.6.1-production.apk)
-[Latest APK (English only): (SHA256) 0f9cd87e57bcb9e402c9aeafba719a9f19be5c26fae54f756924da286403dcaa v2.6](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.6/v2.6-en.apk)
+[Latest APK (English only): (SHA256) e366c67c54548da4c46206c953e8847ba6e4c933449ca8d33525601ee2d87bb8 v2.6.1](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.6.1/v2.6.1-en.apk)
-[Latest APK (Discrete): (SHA256) 3feb38df6d8044d71fa3616092afe1242f5a9d2dd69d39e0660d125d57590f59 v2.6](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.6/v2.6-discrete.apk)
+[Latest APK (Discrete): (SHA256) 3b5edaceb462c6fcd51d11652943357976f75b53dacdfe650f422933357688d9 v2.6.1](https://github.com/7LPdWcaW/GrowTracker-Android/releases/download/v2.6.1/v2.6.1-discrete.apk)
[Get it on F-Droid with automatic updates](https://f-droid.org/packages/me.anon.grow/)
@@ -279,6 +279,7 @@ Translations provided by;
- Alex (Noxmiles) - de 
- Basti B (Weltenesche) - de 
+- Heimen Stoffels (Vistaus) - nl 
- EmmanuelMess - es 
- Maxtille - fr 
- Patrick B (EukalyptusX) - de 
diff --git a/app/build.gradle b/app/build.gradle
index e3c6bf08..326212d3 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -29,8 +29,8 @@ android {
applicationId "me.anon.grow"
minSdkVersion 17
targetSdkVersion 29
- versionCode getCommitCountTotal()
- versionName "2.6"
+ versionCode 1370//getCommitCountTotal()
+ versionName "2.6.1"
versionNameSuffix (travis ? "-alpha" : "")
compileOptions {
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b75e7d11..5193da2d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,6 +4,7 @@
>
+
" + new DateRenderer(getActivity()).timeAgo(lastWater.getDate()).formattedDate + " ago"));
+ lastFeedingDate.setText(Html.fromHtml(getString(R.string.ago, "" + new DateRenderer(getActivity()).timeAgo(lastWater.getDate()).formattedDate + "")));
final Water finalLastWater = lastWater;
duplicateFeeding.setOnClickListener(new View.OnClickListener()
@@ -431,6 +431,12 @@ private void setUi()
@Views.OnClick public void onPhotoClick()
{
+ if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.CAMERA))
+ {
+ PermissionHelper.doPermissionCheck(this, Manifest.permission.CAMERA, 1, getString(R.string.camera_permission_summary));
+ return;
+ }
+
if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))
{
PermissionHelper.doPermissionCheck(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, 1, getString(R.string.permission_summary));
@@ -517,8 +523,31 @@ private void setUi()
{
if (resultCode == Activity.RESULT_CANCELED)
{
- new File(plant.getImages().get(plant.getImages().size() - 1)).delete();
- plant.getImages().remove(plant.getImages().size() - 1);
+ File imageFile = new File(plant.getImages().get(plant.getImages().size() - 1));
+
+ if (imageFile.delete())
+ {
+ plant.getImages().remove(plant.getImages().size() - 1);
+ }
+
+ File folderFile = imageFile.getParentFile();
+ String[] list = folderFile.list();
+ if (list != null)
+ {
+ if (list.length == 1 && ".nomedia".equals(list[0]))
+ {
+ new File(folderFile, ".nomedia").delete();
+ }
+
+ if (folderFile.list() == null || folderFile.list().length == 0)
+ {
+ folderFile.delete();
+ }
+ }
+ else
+ {
+ folderFile.delete();
+ }
}
else
{
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 f98922e4..7726cf3e 100644
--- a/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java
+++ b/app/src/main/java/me/anon/grow/fragment/SettingsFragment.java
@@ -1,6 +1,5 @@
package me.anon.grow.fragment;
-import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.ComponentName;
@@ -15,22 +14,21 @@
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.provider.DocumentsContract;
import android.text.Html;
-import android.text.SpannableString;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
-import android.text.util.Linkify;
import android.util.Base64;
import android.view.View;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.nostra13.universalimageloader.core.ImageLoader;
+import com.squareup.moshi.Types;
import java.io.BufferedInputStream;
import java.io.File;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
@@ -65,6 +63,8 @@
import me.anon.lib.helper.AddonHelper;
import me.anon.lib.helper.BackupHelper;
import me.anon.lib.helper.EncryptionHelper;
+import me.anon.lib.helper.MigrationHelper;
+import me.anon.lib.helper.MoshiHelper;
import me.anon.lib.helper.NotificationHelper;
import me.anon.lib.helper.PathHelper;
import me.anon.lib.manager.FileManager;
@@ -76,10 +76,14 @@
import me.anon.model.Garden;
import me.anon.model.Plant;
+import static android.app.Activity.RESULT_OK;
+
public class SettingsFragment extends PreferenceFragmentCompat implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener
{
private static final int REQUEST_UNINSTALL = 0x01;
private static final int REQUEST_PICK_DOCUMENT = 0x02;
+ private static final int REQUEST_PICK_BACKUP_DOCUMENT = 0x03;
+ private static final int REQUEST_PICK_IMPORT_DOCUMENT = 0x04;
@Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey)
{
@@ -108,6 +112,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
findPreference("tds_unit").setSummary(Html.fromHtml(getString(R.string.settings_tds_summary, getString(TdsUnit.getSelectedTdsUnit(getActivity()).getStrRes()))));
findPreference("backup_now").setSummary(Html.fromHtml(getString(R.string.settings_lastbackup_summary, BackupHelper.getLastBackup())));
findPreference("image_location").setSummary(Html.fromHtml(getString(R.string.settings_image_location_summary, FileManager.IMAGE_PATH)));
+ findPreference("backup_location").setSummary(Html.fromHtml(getString(R.string.settings_backup_location_summary, BackupHelper.FILES_PATH)));
try
{
@@ -134,6 +139,8 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
}
else
{
+ findPreference("image_location").setEnabled(false);
+ findPreference("backup_location").setEnabled(false);
findPreference("backup_size").setEnabled(false);
}
@@ -148,7 +155,10 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
findPreference("backup_now").setOnPreferenceClickListener(this);
findPreference("restore").setOnPreferenceClickListener(this);
findPreference("image_location").setOnPreferenceClickListener(this);
+ findPreference("backup_location").setOnPreferenceClickListener(this);
+ findPreference("import").setOnPreferenceClickListener(this);
+ findPreference("import").setEnabled(!MainApplication.isEncrypted());
findPreference("failsafe").setEnabled(((SwitchPreferenceCompat)findPreference("encrypt")).isChecked());
if (MainApplication.isFailsafe())
@@ -176,7 +186,7 @@ public class SettingsFragment extends PreferenceFragmentCompat implements Prefer
{
Intent refresh = new Intent();
refresh.putExtra("refresh", true);
- getActivity().setResult(Activity.RESULT_OK, refresh);
+ getActivity().setResult(RESULT_OK, refresh);
}
}
@@ -291,7 +301,7 @@ private void populateAddons()
{
Intent refresh = new Intent();
refresh.putExtra("refresh", true);
- getActivity().setResult(Activity.RESULT_OK, refresh);
+ getActivity().setResult(RESULT_OK, refresh);
if ("force_dark".equals(preference.getKey()))
{
@@ -561,7 +571,7 @@ else if ("auto_backup".equalsIgnoreCase(preference.getKey()))
{
PreferenceManager.getDefaultSharedPreferences(getActivity()).edit().putBoolean("auto_backup", true).apply();
((MainApplication)getActivity().getApplication()).registerBackupService();
- SnackBar.show(getActivity(), getString(R.string.backup_enable_toast), Snackbar.LENGTH_LONG, null);
+ SnackBar.show(getActivity(), getString(R.string.backup_enable_toast, BackupHelper.FILES_PATH), Snackbar.LENGTH_LONG, null);
}
return true;
@@ -574,7 +584,7 @@ else if ("auto_backup".equalsIgnoreCase(preference.getKey()))
{
Intent refresh = new Intent();
refresh.putExtra("refresh", true);
- getActivity().setResult(Activity.RESULT_OK, refresh);
+ getActivity().setResult(RESULT_OK, refresh);
if ("delivery_unit".equals(preference.getKey()))
{
@@ -775,7 +785,7 @@ else if ("backup_now".equals(preference.getKey()))
else if ("restore".equals(preference.getKey()))
{
// get list of backups
- File backupPath = new File(Environment.getExternalStorageDirectory(), "/backups/GrowTracker/");
+ File backupPath = new File(BackupHelper.FILES_PATH);
String[] backupFiles = backupPath.list();
if (backupFiles == null || backupFiles.length == 0)
@@ -836,20 +846,23 @@ else if ("restore".equals(preference.getKey()))
}
File file = new File(backupPath.getPath() + "/" + backup);
- if (backup.contains("plants") && backup.endsWith(".bak"))
+ if (backup.contains("plants"))
{
+ current.requireMigration = !backup.endsWith(".bak");
current.plantsPath = backupPath.getPath() + "/" + backup;
current.size += file.length();
}
- if (backup.contains("gardens") && backup.endsWith(".bak"))
+ if (backup.contains("gardens"))
{
+ current.requireMigration = !backup.endsWith(".bak");
current.gardenPath = backupPath.getPath() + "/" + backup;
current.size += file.length();
}
- if (backup.contains("schedules") && backup.endsWith(".bak"))
+ if (backup.contains("schedules"))
{
+ current.requireMigration = !backup.endsWith(".bak");
current.schedulePath = backupPath.getPath() + "/" + backup;
current.size += file.length();
}
@@ -1013,12 +1026,30 @@ else if ("image_location".equals(preference.getKey()))
startActivityForResult(intent, REQUEST_PICK_DOCUMENT);
}
}
+ else if ("backup_location".equals(preference.getKey()))
+ {
+ if (Build.VERSION.SDK_INT >= 21)
+ {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
+ startActivityForResult(intent, REQUEST_PICK_BACKUP_DOCUMENT);
+ }
+ }
else if ("clear_image_cache".equals(preference.getKey()))
{
ImageLoader.getInstance().clearDiskCache();
ImageLoader.getInstance().clearMemoryCache();
SnackBar.show(getActivity(), getString(R.string.cache_cleared), Snackbar.LENGTH_SHORT, null);
}
+ else if ("import".equals(preference.getKey()))
+ {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
+
+ startActivityForResult(intent, REQUEST_PICK_IMPORT_DOCUMENT);
+ }
return false;
}
@@ -1057,12 +1088,16 @@ private void completeRestore(BackupData selectedBackup)
else
{
SnackBar.show(getActivity(), getString(R.string.restore_complete, selectedBackup.toString()), Snackbar.LENGTH_LONG, null);
- getActivity().recreate();
}
FileManager.getInstance().removeFile(PlantManager.FILES_DIR + "/plants.temp");
FileManager.getInstance().removeFile(GardenManager.FILES_DIR + "/gardens.temp");
FileManager.getInstance().removeFile(ScheduleManager.FILES_DIR + "/schedules.temp");
+
+ if (selectedBackup.requireMigration)
+ {
+ MigrationHelper.migratePpm(getActivity());
+ }
}
@Override public void onActivityResult(int requestCode, int resultCode, Intent data)
@@ -1077,7 +1112,7 @@ private void completeRestore(BackupData selectedBackup)
}
else if (requestCode == REQUEST_PICK_DOCUMENT && Build.VERSION.SDK_INT >= 19)
{
- if (resultCode == Activity.RESULT_OK)
+ if (resultCode == RESULT_OK)
{
Uri treeUri = data.getData();
DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
@@ -1094,10 +1129,58 @@ else if (requestCode == REQUEST_PICK_DOCUMENT && Build.VERSION.SDK_INT >= 19)
if (!TextUtils.isEmpty(filePath) && new File(filePath).exists())
{
if (!filePath.endsWith("/")) filePath = filePath + "/";
- findPreference("image_location").getSharedPreferences().edit().putString("image_location", filePath).apply();
- findPreference("image_location").setSummary(Html.fromHtml(getString(R.string.settings_image_location_summary, filePath)));
- return;
+ if (new File(filePath).canWrite())
+ {
+ findPreference("image_location").getSharedPreferences().edit().putString("image_location", filePath).apply();
+ findPreference("image_location").setSummary(Html.fromHtml(getString(R.string.settings_image_location_summary, filePath)));
+
+ return;
+ }
+ }
+ }
+ catch (URISyntaxException e)
+ {
+ }
+ }
+ }
+
+ SnackBar.show(getActivity(), getString(R.string.settings_image_location_error), Snackbar.LENGTH_LONG, null);
+ }
+ else if (requestCode == REQUEST_PICK_BACKUP_DOCUMENT && Build.VERSION.SDK_INT >= 19)
+ {
+ if (resultCode == RESULT_OK)
+ {
+ Uri treeUri = data.getData();
+ DocumentFile pickedDir = DocumentFile.fromTreeUri(getActivity(), treeUri);
+
+ Uri docUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, DocumentsContract.getTreeDocumentId(treeUri));
+
+ if (pickedDir != null)
+ {
+ String filePath = null;
+ try
+ {
+ filePath = PathHelper.getPath(getActivity(), docUri);
+
+ if (!TextUtils.isEmpty(filePath) && new File(filePath).exists())
+ {
+ if (!filePath.endsWith("/")) filePath = filePath + "/";
+
+ if (new File(filePath).canWrite())
+ {
+ BackupHelper.FILES_PATH = filePath;
+ findPreference("backup_location").getSharedPreferences().edit().putString("backup_location", filePath).apply();
+ findPreference("backup_location").setSummary(Html.fromHtml(getString(R.string.settings_backup_location_summary, filePath)));
+
+ // refresh backup size
+ String currentBackup = findPreference("backup_size").getSharedPreferences().getString("backup_size", "20");
+ findPreference("backup_size").setSummary(Html.fromHtml(getString(R.string.settings_backup_size, currentBackup, lengthToString(BackupHelper.backupSize()))));
+
+ // refresh last backup
+ findPreference("backup_now").setSummary(Html.fromHtml(getString(R.string.settings_lastbackup_summary, BackupHelper.getLastBackup())));
+ return;
+ }
}
}
catch (URISyntaxException e)
@@ -1106,10 +1189,40 @@ else if (requestCode == REQUEST_PICK_DOCUMENT && Build.VERSION.SDK_INT >= 19)
}
}
- if (resultCode != Activity.RESULT_CANCELED)
+ SnackBar.show(getActivity(), getString(R.string.settings_backup_location_error), Snackbar.LENGTH_LONG, null);
+ }
+ else if (requestCode == REQUEST_PICK_IMPORT_DOCUMENT)
+ {
+ if (resultCode == RESULT_OK && data.getData() != null)
{
- SnackBar.show(getActivity(), getString(R.string.settings_image_location_error), Snackbar.LENGTH_LONG, null);
+ try
+ {
+ File temp = new File(getActivity().getCacheDir(), "importtemp.json");
+ InputStream inputStream = getActivity().getContentResolver().openInputStream(data.getData());
+ FileManager.getInstance().copyFile(inputStream, new FileOutputStream(temp));
+
+ if (temp.exists())
+ {
+ // Try reading as plants
+ ArrayList plants = MoshiHelper.parse(temp, Types.newParameterizedType(ArrayList.class, Plant.class));
+ if (plants != null)
+ {
+ // backup
+ FileManager.getInstance().copyFile(PlantManager.FILES_DIR + "/plants." + PlantManager.getInstance().getFileExt(), PlantManager.FILES_DIR + "/plants." + PlantManager.getInstance().getFileExt() + ".bak");
+ FileManager.getInstance().copyFile(temp.getPath(), PlantManager.FILES_DIR + "/plants." + PlantManager.getInstance().getFileExt());
+ PlantManager.getInstance().load();
+ SnackBar.show(getActivity(), getString(R.string.settings_import_success), Snackbar.LENGTH_LONG, null);
+ return;
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
}
+
+ SnackBar.show(getActivity(), getString(R.string.settings_import_error), Snackbar.LENGTH_LONG, null);
}
}
@@ -1133,6 +1246,7 @@ public class BackupData
String gardenPath;
String schedulePath;
long size = 0;
+ boolean requireMigration = false;
@Override public String toString()
{
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 7fafe48a..beb0e637 100644
--- a/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java
+++ b/app/src/main/java/me/anon/grow/fragment/ViewPhotosFragment.java
@@ -24,7 +24,9 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -181,11 +183,38 @@ else if (item.getItemId() == R.id.delete)
{
@Override public void onClick(DialogInterface dialog, int which)
{
+ Set folders = new HashSet<>();
for (String integer : adapter.getSelected())
{
String image = adapter.getImages().get(Integer.parseInt(integer));
- new File(image).delete();
- plant.getImages().remove(image);
+ File imageFile = new File(image);
+
+ folders.add(imageFile.getParentFile().getPath());
+ if (imageFile.delete())
+ {
+ plant.getImages().remove(image);
+ }
+ }
+
+ for (String folder : folders)
+ {
+ File folderFile = new File(folder);
+ if (folderFile.isDirectory())
+ {
+ String[] list = folderFile.list();
+ if (list != null)
+ {
+ if (list.length == 1 && ".nomedia".equals(list[0]))
+ {
+ new File(folderFile, ".nomedia").delete();
+ }
+
+ if (folderFile.list() == null || folderFile.list().length == 0)
+ {
+ folderFile.delete();
+ }
+ }
+ }
}
PlantManager.getInstance().upsert(plant);
@@ -346,6 +375,12 @@ private void setEmpty()
@Views.OnClick public void onFabPhotoClick(final View view)
{
+ if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.CAMERA))
+ {
+ PermissionHelper.doPermissionCheck(this, Manifest.permission.CAMERA, 1, getString(R.string.camera_permission_summary));
+ return;
+ }
+
if (!PermissionHelper.hasPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE))
{
PermissionHelper.doPermissionCheck(this, Manifest.permission.WRITE_EXTERNAL_STORAGE, 1, getString(R.string.permission_summary));
@@ -418,8 +453,31 @@ private void setEmpty()
{
if (resultCode == Activity.RESULT_CANCELED)
{
- new File(plant.getImages().get(plant.getImages().size() - 1)).delete();
- plant.getImages().remove(plant.getImages().size() - 1);
+ File imageFile = new File(plant.getImages().get(plant.getImages().size() - 1));
+
+ if (imageFile.delete())
+ {
+ plant.getImages().remove(plant.getImages().size() - 1);
+ }
+
+ File folderFile = imageFile.getParentFile();
+ String[] list = folderFile.list();
+ if (list != null)
+ {
+ if (list.length == 1 && ".nomedia".equals(list[0]))
+ {
+ new File(folderFile, ".nomedia").delete();
+ }
+
+ if (folderFile.list() == null || folderFile.list().length == 0)
+ {
+ folderFile.delete();
+ }
+ }
+ else
+ {
+ folderFile.delete();
+ }
}
else
{
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 fd5e7cf4..79d8f4e6 100644
--- a/app/src/main/java/me/anon/grow/fragment/WateringFragment.java
+++ b/app/src/main/java/me/anon/grow/fragment/WateringFragment.java
@@ -41,6 +41,7 @@
import me.anon.lib.TempUnit;
import me.anon.lib.Unit;
import me.anon.lib.Views;
+import me.anon.lib.ext.NumberUtilsKt;
import me.anon.lib.manager.PlantManager;
import me.anon.lib.manager.ScheduleManager;
import me.anon.model.Action;
@@ -395,14 +396,14 @@ private void setHints()
if (!averageAmount.isNaN())
{
- amount.setHint(String.valueOf(ML.to(selectedDeliveryUnit, averageAmount)) + selectedDeliveryUnit.getLabel());
+ amount.setHint(NumberUtilsKt.formatWhole(ML.to(selectedDeliveryUnit, averageAmount)) + selectedDeliveryUnit.getLabel());
}
tempContainer.setVisibility(View.VISIBLE);
if (!averageTemp.isNaN())
{
- temp.setHint(String.valueOf(CELCIUS.to(selectedTemperatureUnit, averageTemp)) + selectedTemperatureUnit.getLabel());
+ temp.setHint(NumberUtilsKt.formatWhole(CELCIUS.to(selectedTemperatureUnit, averageTemp)) + selectedTemperatureUnit.getLabel());
}
notes.setHint(hintFeed.get(0).getNotes());
@@ -466,12 +467,7 @@ private void setUi()
if (water.getTds() != null)
{
- String ppm = String.valueOf(water.getTds().getAmount().intValue());
- if (selectedTdsUnit.getDecimalPlaces() == 2)
- {
- ppm = "" + Unit.toTwoDecimalPlaces(water.getTds().getAmount());
- }
-
+ String ppm = NumberUtilsKt.formatWhole(water.getTds().getAmount());
waterPpm.setText(ppm);
}
@@ -482,14 +478,14 @@ private void setUi()
if (water.getAmount() != null)
{
- amount.setText(String.valueOf(ML.to(selectedDeliveryUnit, water.getAmount())));
+ amount.setText(NumberUtilsKt.formatWhole(ML.to(selectedDeliveryUnit, water.getAmount())));
}
tempContainer.setVisibility(View.VISIBLE);
if (water.getTemp() != null)
{
- temp.setHint(String.valueOf(CELCIUS.to(selectedTemperatureUnit, water.getTemp())) + selectedTemperatureUnit.getLabel());
+ temp.setHint(NumberUtilsKt.formatWhole(CELCIUS.to(selectedTemperatureUnit, water.getTemp())) + selectedTemperatureUnit.getLabel());
}
populateAdditives();
diff --git a/app/src/main/java/me/anon/lib/export/MarkdownProcessor.kt b/app/src/main/java/me/anon/lib/export/MarkdownProcessor.kt
index c7d325dd..d9818299 100644
--- a/app/src/main/java/me/anon/lib/export/MarkdownProcessor.kt
+++ b/app/src/main/java/me/anon/lib/export/MarkdownProcessor.kt
@@ -77,7 +77,7 @@ class MarkdownProcessor : ExportProcessor()
}
val planted = DateTimeUtils.toLocalDateTime(Timestamp(plant.plantDate))
- documentBuilder.append("**Planted:** ").append(planted.format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm")))
+ documentBuilder.append("**Planted:** ").append(planted.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
documentBuilder.append(NEW_LINE + NEW_LINE)
documentBuilder.append("**From clone?:** ").append(plant.clone)
@@ -106,7 +106,7 @@ class MarkdownProcessor : ExportProcessor()
{
val harvested = DateTimeUtils.toLocalDateTime(Timestamp(stageDate))
- documentBuilder.append("**Harvested:** ").append(harvested.format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm")))
+ documentBuilder.append("**Harvested:** ").append(harvested.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
documentBuilder.append(NEW_LINE + NEW_LINE)
}
}
@@ -123,7 +123,7 @@ class MarkdownProcessor : ExportProcessor()
val stageName = key.enString
documentBuilder.append(" - ").append(stageName).append(NEW_LINE + NEW_LINE)
- documentBuilder.append("\t - ").append("**Set on:** ").append(stageDateTime.format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm"))).append(NEW_LINE)
+ documentBuilder.append("\t - ").append("**Set on:** ").append(stageDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"))).append(NEW_LINE)
if (key != PlantStage.HARVESTED)
{
@@ -265,7 +265,7 @@ class MarkdownProcessor : ExportProcessor()
val actionDate = DateTimeUtils.toLocalDateTime(Timestamp(action.date))
documentBuilder.append("| ")
- documentBuilder.append(actionDate.format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm")))
+ documentBuilder.append(actionDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
documentBuilder.append(" | ")
// stage date
@@ -381,7 +381,7 @@ class MarkdownProcessor : ExportProcessor()
val actionDate = DateTimeUtils.toLocalDateTime(Timestamp(action.date))
documentBuilder.append("| ")
- documentBuilder.append(actionDate.format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm")))
+ documentBuilder.append(actionDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")))
documentBuilder.append(" | ")
documentBuilder.append(action.getTypeStr())
documentBuilder.append(" | ")
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 918b4e2a..993a9c7a 100644
--- a/app/src/main/java/me/anon/lib/helper/BackupHelper.kt
+++ b/app/src/main/java/me/anon/lib/helper/BackupHelper.kt
@@ -21,35 +21,37 @@ import kotlin.collections.ArrayList
object BackupHelper
{
@JvmField
- public var FILES_PATH = Environment.getExternalStorageDirectory().absolutePath + "/backups/GrowTracker"
+ public var FILES_PATH = Environment.getExternalStorageDirectory().absolutePath + "/backups/GrowTracker/"
@JvmStatic
public fun getLastBackup(): String
{
File(FILES_PATH).listFiles()?.let {
val sorted = ArrayList(it.sortedBy { it.lastModified() })
- val parts = sorted.last().name.split(".")
- var date = Date()
- try
- {
- date = Date(java.lang.Long.parseLong(parts[0]))
- }
- catch (e: NumberFormatException)
- {
+ sorted.lastOrNull()?.let { str ->
+ val parts = str.name.split(".")
+ var date = Date()
try
{
- date = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").parse(parts[0])
+ date = Date(java.lang.Long.parseLong(parts[0]))
}
- catch (e2: Exception)
+ catch (e: NumberFormatException)
{
- date = Date(sorted.last().lastModified())
+ try
+ {
+ date = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").parse(parts[0])
+ }
+ catch (e2: Exception)
+ {
+ date = Date(sorted.last().lastModified())
+ }
}
- }
- return DateTimeUtils.toLocalDateTime(Timestamp(date.time)).format(DateTimeFormatter.ofPattern("yyyy-mm-dd HH:mm:ss"))
+ return DateTimeUtils.toLocalDateTime(Timestamp(date.time)).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))
+ }
}
- return ""
+ return "-"
}
@JvmStatic
diff --git a/app/src/main/java/me/anon/lib/helper/MigrationHelper.java b/app/src/main/java/me/anon/lib/helper/MigrationHelper.java
index 62ed3e82..ed2b5416 100644
--- a/app/src/main/java/me/anon/lib/helper/MigrationHelper.java
+++ b/app/src/main/java/me/anon/lib/helper/MigrationHelper.java
@@ -40,31 +40,7 @@ public static boolean performMigration(Context context, final AsyncCallback call
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (!preferences.getBoolean("migration_tds", false))
{
- // migrate ppm to tds
- boolean usingEc = preferences.getBoolean("tds_ec", false);
- for (Plant plant : PlantManager.getInstance().getPlants())
- {
- for (Action action : plant.getActions())
- {
- if (action instanceof Water && ((Water)action).getPpm() != null)
- {
- Tds replacement = new Tds();
- if (usingEc && ((Water)action).getPpm() < 25)
- {
- replacement.setAmount(Unit.toTwoDecimalPlaces((((Water)action).getPpm() * 2d) / 1000d));
- replacement.setType(TdsUnit.EC);
- }
- else
- {
- replacement.setAmount(((Water)action).getPpm());
- replacement.setType(TdsUnit.PPM500);
- }
-
- ((Water)action).setTds(replacement);
- ((Water)action).setPpm(null);
- }
- }
- }
+ migratePpm(context);
preferences.edit().putBoolean("migration_tds", true).apply();
PlantManager.getInstance().save(callback, true);
@@ -77,4 +53,34 @@ public static boolean performMigration(Context context, final AsyncCallback call
}
return false;
}
+
+ public static void migratePpm(Context context)
+ {
+ // migrate ppm to tds
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
+ boolean usingEc = preferences.getBoolean("tds_ec", false);
+ for (Plant plant : PlantManager.getInstance().getPlants())
+ {
+ for (Action action : plant.getActions())
+ {
+ if (action instanceof Water && ((Water)action).getPpm() != null)
+ {
+ Tds replacement = new Tds();
+ if (usingEc && ((Water)action).getPpm() < 25)
+ {
+ replacement.setAmount(Unit.toTwoDecimalPlaces((((Water)action).getPpm() * 2d) / 1000d));
+ replacement.setType(TdsUnit.EC);
+ }
+ else
+ {
+ replacement.setAmount(((Water)action).getPpm());
+ replacement.setType(TdsUnit.PPM500);
+ }
+
+ ((Water)action).setTds(replacement);
+ ((Water)action).setPpm(null);
+ }
+ }
+ }
+ }
}
diff --git a/app/src/main/java/me/anon/lib/helper/PathHelper.java b/app/src/main/java/me/anon/lib/helper/PathHelper.java
index f08efdac..5f561d85 100644
--- a/app/src/main/java/me/anon/lib/helper/PathHelper.java
+++ b/app/src/main/java/me/anon/lib/helper/PathHelper.java
@@ -9,6 +9,7 @@
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
+import android.text.TextUtils;
import java.net.URISyntaxException;
@@ -45,12 +46,27 @@ public static String getPath(Context context, Uri uri) throws URISyntaxException
}
}
- return "/storage/" + split[0] + "/" + split[1];
+ return "/storage/" + TextUtils.join("/", split);
}
else if (isDownloadsDocument(uri))
{
final String id = DocumentsContract.getDocumentId(uri);
- uri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+ if (!TextUtils.isEmpty(id))
+ {
+ if (id.startsWith("raw:"))
+ {
+ return id.replaceFirst("raw:", "");
+ }
+ try
+ {
+ final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
+ return getDataColumn(context, contentUri, null, null);
+ }
+ catch (NumberFormatException e)
+ {
+ return null;
+ }
+ }
}
else if (isMediaDocument(uri))
{
@@ -129,4 +145,28 @@ public static boolean isMediaDocument(Uri uri)
{
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
+
+ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs)
+ {
+ Cursor cursor = null;
+ final String column = "_data";
+ final String[] projection = {column};
+ try
+ {
+ cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
+ if (cursor != null && cursor.moveToFirst())
+ {
+ final int index = cursor.getColumnIndexOrThrow(column);
+ return cursor.getString(index);
+ }
+ }
+ finally
+ {
+ if (cursor != null)
+ {
+ cursor.close();
+ }
+ }
+ return null;
+ }
}
diff --git a/app/src/main/java/me/anon/lib/task/DecryptTask.java b/app/src/main/java/me/anon/lib/task/DecryptTask.java
index c9ac4713..fa8a8057 100644
--- a/app/src/main/java/me/anon/lib/task/DecryptTask.java
+++ b/app/src/main/java/me/anon/lib/task/DecryptTask.java
@@ -80,7 +80,7 @@ public DecryptTask(Context appContext)
dis = new DecryptInputStream(cipher, temp);
fos = new FileOutputStream(file);
- byte[] buffer = new byte[524288];
+ byte[] buffer = new byte[8192];
int len;
while ((len = dis.read(buffer)) != -1)
diff --git a/app/src/main/java/me/anon/lib/task/EncryptTask.java b/app/src/main/java/me/anon/lib/task/EncryptTask.java
index 92db51de..d7b198d1 100644
--- a/app/src/main/java/me/anon/lib/task/EncryptTask.java
+++ b/app/src/main/java/me/anon/lib/task/EncryptTask.java
@@ -80,7 +80,7 @@ public EncryptTask(Context appContext)
fis = new FileInputStream(temp);
eos = new EncryptOutputStream(cipher, file);
- byte[] buffer = new byte[524288];
+ byte[] buffer = new byte[8192];
int len = 0;
while ((len = fis.read(buffer)) != -1)
diff --git a/app/src/main/java/me/anon/lib/task/ImportTask.java b/app/src/main/java/me/anon/lib/task/ImportTask.java
index 946170b3..593bf66e 100644
--- a/app/src/main/java/me/anon/lib/task/ImportTask.java
+++ b/app/src/main/java/me/anon/lib/task/ImportTask.java
@@ -91,7 +91,12 @@ public ImportTask(Context appContext, AsyncCallback callback)
{
File toPath = new File(to, System.currentTimeMillis() + ".jpg");
copyImage(appContext, filePath, toPath);
- imagesToAdd.add(toPath.getPath());
+
+ if (toPath.exists())
+ {
+ imagesToAdd.add(toPath.getPath());
+ }
+
publishProgress(++count, total);
}
@@ -139,19 +144,19 @@ public void copyImage(Context context, Uri imageUri, File newLocation)
ParcelFileDescriptor parcelFileDescriptor = context.getContentResolver().openFileDescriptor(imageUri, "r");
FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
- InputStream streamIn = new BufferedInputStream(new FileInputStream(fileDescriptor), 524288);
+ InputStream streamIn = new BufferedInputStream(new FileInputStream(fileDescriptor), 8192);
- OutputStream streamOut = new BufferedOutputStream(eos, 524288);
+ OutputStream streamOut = new BufferedOutputStream(eos, 8192);
int len;
- byte[] buffer = new byte[524288];
+ byte[] buffer = new byte[8192];
while ((len = streamIn.read(buffer)) != -1)
{
streamOut.write(buffer, 0, len);
}
- streamIn.close();
streamOut.flush();
+ streamIn.close();
streamOut.close();
}
else if (imageUri.getScheme().startsWith("file"))
@@ -163,23 +168,24 @@ else if (imageUri.getScheme().startsWith("file"))
String image = imageUri.getPath();
- InputStream streamIn = new BufferedInputStream(new FileInputStream(new File(image)), 524288);
- OutputStream streamOut = new BufferedOutputStream(eos, 524288);
+ InputStream streamIn = new BufferedInputStream(new FileInputStream(new File(image)), 8192);
+ OutputStream streamOut = new BufferedOutputStream(eos, 8192);
int len;
- byte[] buffer = new byte[524288];
+ byte[] buffer = new byte[8192];
while ((len = streamIn.read(buffer)) != -1)
{
streamOut.write(buffer, 0, len);
}
- streamIn.close();
streamOut.flush();
+ streamIn.close();
streamOut.close();
}
}
catch (Exception e)
{
+ newLocation.delete();
e.printStackTrace();
}
}
diff --git a/app/src/main/res/layout-land/actions_list_view.xml b/app/src/main/res/layout-land/actions_list_view.xml
index 8ad23d70..7e523d01 100644
--- a/app/src/main/res/layout-land/actions_list_view.xml
+++ b/app/src/main/res/layout-land/actions_list_view.xml
@@ -6,40 +6,74 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
>
-
-
+
+
+
+
-
-
-
+
Se falló en establecer la ubicación para imágenes
Ubicación de las imágenes guardadas
Limpiar el cache de imágenes
- Disco de imágenes y caché de memoria vaciados
+ Imágenes y caché en memoria y disco vaciados
Importando imágenes, esto puede tomar un ratito…
Tarea de información
Tarea completada
@@ -406,4 +406,6 @@
Exportado por completo
¿Incluir imágenes?
No hay ninguna información disponible
+
+ Para tomar fotos se necesita el permiso para la cámara
diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml
index 155e9b70..b8306f06 100644
--- a/app/src/main/res/values-fr/strings.xml
+++ b/app/src/main/res/values-fr/strings.xml
@@ -83,8 +83,6 @@
Date
Aujourd\'hui
Curé depuis %s jours
- %s jours
- Jours %s
Arrosages
Changements d\'état
@@ -248,7 +246,7 @@
Fournir ce password pendant la phase de décryptage pour éviter de charger les données
Attention
Il s\'agit d\'une forme basique d\'encryptage AES sur base d\'une phrase de passe fournie par l\'utilisateur. Ne pas considérer comme une forme de protection absolue contre le gouvernement ou toute agence de surveillance.
- Backup activé, les backups seront enregistrés dans /sdcard/backups/GrowTracker/
+ Backup activé, sauvegarde dans %s
Restoration à %s complete.
Impossible de restaurer depuis le backup %s. Le fichier est peut être %s.
encrypté
@@ -407,4 +405,14 @@
%s exporté à %s
Export complet
Inclure les images ?
+ Aucune donnée accessible
+
+ Permission d\'utiliser l\'appareil photo requise pour prendre des photos
+ Gestions des backups
+ Emplacement de stockage des backups
+ L\'emplacement de stockage des backups est actuellement <b>%s</b>
+ Impossible de stocker à cet emplacement
+ Impossible d\'importer les plantes depuis le fichier
+ Importer des plantes depuis un fichier
+ Plantes bien importées depuis le fichier
diff --git a/app/src/main/res/values-land/dimens.xml b/app/src/main/res/values-land/dimens.xml
new file mode 100644
index 00000000..5cdfbeee
--- /dev/null
+++ b/app/src/main/res/values-land/dimens.xml
@@ -0,0 +1,5 @@
+
+
+
+ 128dp
+
diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml
new file mode 100644
index 00000000..d8f79ab7
--- /dev/null
+++ b/app/src/main/res/values-nl-rNL/strings.xml
@@ -0,0 +1,418 @@
+
+
+
+ Versie %s
+ Instellingen
+
+
+ %s planten
+
+ Alle planten
+
+ Alle
+ Acties toegevoegd
+ Aantekeningen toegevoegd
+ Water geven toegevoegd
+ Tuin verwijderd
+
+ Weet je zeker dat je de tuin <b>\'%s\'</b> wilt verwijderen? De planten worden niet verwijderd.
+ Water geven
+ Actie
+
+ De widget is niet beschikbaar zolang versleuteling is ingeschakeld
+ Plant toevoegen
+ Plantinformatie
+ Plantstatistieken
+
+ Foto maken
+
+ Kiezen uit fotogalerij
+ Kies een optie
+ Afbeelding kiezen
+ Toepassen op andere plant
+ Plant kiezen
+ Afbeelding toegevoegd
+ Nóg één maken
+
+ Schema\'s
+ Schema-informatie
+ Schemadatum
+ Naam
+ Licht voeden
+ Omschrijving
+
+ Elke 2 weken licht voeden
+
+ + Voeding toevoegen
+ + Schema toevoegen
+ + Toevoeging toevoegen
+ Toevoegingen
+ Toevoeging
+ Vanaf datum
+ Vanaf fase
+ Tot datum
+ Tot fase
+ Samenstellen uit schema
+ Samenstellen uit vorige
+ Weet je het zeker?
+ Wil je het geselecteerde schema kopiëren?
+ Wil je het geselecteerde schema verwijderen?
+ Schema gekopieerd
+ Schema verwijderd
+ Ongedaan maken
+ Voederschema\'s
+ Voederschema
+ App is bijgewerkt
+ De app is geüpdatet naar versie %s
+ Wijzigingslog bekijken
+ Verwerpen
+
+
+ %s water geven
+ Meerdere planten water geven
+
+ Aantekeningen
+ Waterinformatie
+ pH-waarde
+ PPM
+ Afvoer
+ Hoeveelheid (%s)
+ Temperatuur (º%s)
+ Datum en tijd
+ Nu
+ Datum
+ Vandaag
+ %s dagen genezen
+
+ Water geven
+ Fase-aanpassingen
+
+ Klonen
+ Kopiëren naar
+ Actie bewerken
+ Actie verwijderen
+
+ Planten selecteren
+ Tuinen
+ Alle planten
+ + Tuin toevoegen
+ Aanvullend
+ Instellingen
+ Verwijderen
+ Delen
+ Foto
+ Water gegeven op
+ Opnieuw water geven
+
+ Plantnaam
+ Plant toevoegen
+ Plantensoort
+ Citroengras
+ Groei-informatie
+ Geplant op
+ Gemiddeld
+
+ Gemiddelde informatie
+
+ Aarde en perliet (half-om-half)
+ Uit kloon?
+ Fase-informatie
+ Plantfase
+ Foto\'s bekijken
+ Geschiedenis tonen
+ Statistieken tonen
+ Foto\'s
+ Geschiedenis
+ Statistieken
+ Filter
+ Geplant
+ Ontkiemd
+ Gewied
+ Groeiende
+ Bloeiende
+ Drogende
+ Genezende
+ Geplukt
+
+ %s geleden geplukt
+ Tuin exporteren
+ Tuin aanpassen
+ Tuin verwijderen
+ Exporteren
+
+ Totaaltijd:
+ Totaal water gegeven:
+ Totaal bijgesteld:
+ Gem. tijd tussen water geven:
+ Toevoegingen filteren
+ pH-waarde
+
+ Min.:
+ Max.:
+ Gem.:
+ Temperatuur
+ Algemeen
+ Kan plant niet laden
+ Oeps, vergeten! (OV)
+ Bijstellen
+ Bladvoeding
+ Lagestresstraining
+ Lolly
+ Bestrijdingsmiddel
+ Afgedekt
+ Verpot
+ Bijsnijden
+ ScrOG Tuck
+
+ Water gegeven
+ Aantekening
+ Item verwijderen?
+ Weet je zeker dat je het volgende wilt verwijderen:
+ Weet je zeker dat je <b>%s</b> foto\'s wilt verwijderen? Je kunt dit niet ongedaan maken.
+ Bewerken
+ fase
+ Instellen
+ Aanpassen
+ Toevoegen
+ Annuleren
+ actie
+ Oké
+ Veld is vereist
+ Fout: onjuiste toegangscode
+ Water geven aanpassen
+ toegevoegd
+ Actie gekloond
+ meerdere planten
+ Actie toegevoegd aan
+ Aantekening bijgewerkt
+ bijgewerkt
+ Fase bijgewerkt
+ Gebeurtenis verwijderd
+ Klaar
+ Tuin
+ Alles selecteren
+ Niets selecteren
+ Toestaan
+ Inschakelen
+ Afwijzen
+ Kiemplant:
+ Kiemplant
+
+ Oeps
+ Het lijkt erop dat de app vorige keer gecrasht is. Wil je deze anonieme logbestanden versturen? Ze worden verstuurd naar <a href=\"https://github.com/7LPdWcaW/GrowTracker-Android/issues\">github.com/7LPdWcaW/GrowTracker-Android/issues</a> - er worden geen persoonlijke gegevens meegestuurd. Je kunt de bestanden ook handmatig plaatsen op <a href=\"https://reddit.com/r/growutils\">reddit.com/r/growutils</a>. De logbestanden worden opgeslagen in <i>%s<i>
+ Ja
+ Nee
+
+ Voer je toegangscode in
+ Voer een toegangscode in
+ Voer nogmaals je toegangscode in
+ Fout: de toegangscodes komen niet overeen
+
+ Er is een fatale fout opgetreden tijdens het opslaan van de tuingegevens. Maak hier een back-up van.
+ In gebruik
+ %s geleden
+ Water gegeven: <b>%s</b> geleden
+ Water gegeven: <b>%s</b> geleden
+ Geplant: <b>%s</b> geleden
+
+ GrowTracker heeft toegang nodig tot je externe opslag om foto\'s op te slaan. Er worden geen andere gegevens uitgelezen.
+
+ Water geven toegevoegd
+ Weet je zeker dat je <b>%s</b> en alle bijbehorende foto\'s wilt verwijderen? Je kunt dit niet ongedaan maken.
+ Bezig met verwijderen van plant...
+
+ Plant gekloond
+ Openen
+
+ Bezig met exporteren van groeilog...
+ Bezig met exporteren van groeilog...
+ %s toegevoegd
+ Voer een naam in
+ Bezig met exporteren van %s...
+
+ Aarde
+ Hydrocultuur
+ Kokosvezel
+ Luchtcultuur
+ Afbeeldingen verbergen
+ Afbeeldingen tonen
+
+ De te tonen tuin na het openen van de app - momenteel: <b>%s</b>
+ Kaartstijl kiezen - momenteel: %s
+ De standaard maateenheid - momenteel: <b>%s</b>
+ De standaard maateenheid bij toevoegingen - momenteel: <b>%s</b>
+ De standaard temperatuureenheid - momenteel: <b>%s</b>
+ Momenteel: <b>%s MiB</b> / Verbruik: <b>%s</b>
+ Voer dit wachtvoord in tijdens het ontgrendelen om te voorkomen dat er informatie wordt geladen
+ Waarschuwing
+ Dit is een basale vorm van AES-versleuteling gebaseerd op de opgegeven toegangscode. Dit is geen gegarandeerde vorm van bescherming tegen handhaving.
+ Back-ups ingeschakeld. Back-ups worden opgeslagen in %s
+ Herstellen van %s voltooid
+ De back-up, %s, kan niet worden hersteld. Het bestand is mogelijk %s.
+ versleuteld
+ onversleuteld
+
+ Maat kiezen
+ Temperatuur kiezen
+ Tuin kiezen
+ Back-up kiezen
+ Je kunt deze back-up alleen herstellen als je versleuteling hebt ingeschakeld en hetzelfde wachtwoord opgeeft
+
+ Gebackupt naar
+ Er zijn geen te herstellen back-ups
+ Alles/Niets
+
+ Toevoer-pH-waarde
+ Afvoer-pH-waarde
+ Gemiddelde pH-waarde
+
+ Plantenfoto\'s
+
+ Toev.-pH:
+ Afv.-pH:
+ Hoeveelheid:
+ Temp.:
+ Toevoegingen:
+ EC:
+ PPM:
+
+ Schema kiezen
+ Acties
+ Donker thema gebruiken
+ Stelt het donkere thema in in de app, ongeacht de systeeminstelling voor dag- en nachtthema\'s
+ Geplukte items verbergen
+ Verberg alle planten die reeds geplukt zijn
+ Plantvolgorde omdraaien
+ Toont de planten in omgekeerde volgorde. (herstart vereist)
+ Standaardtuin
+
+ Eenheden
+ Toevoereenheid
+ Maateenheid
+ Temperatuureenheid
+ TDS-eenheid
+ De standaard tds-eenheid - momenteel: <b>%s</b>
+ Gegevensbeheer
+ Automatisch back-uppen
+ Plantinformatie elke 24 uur automatisch back-uppen
+ Nu back-uppen
+ Back-upgrootte
+ Grootte (in MiB)
+ Back-uplimiet (MiB)
+ Herstellen uit back-up
+ Gegevens versleutelen
+ Voegt een pincode toe aan de app en versleutelt alle gegevens/afbeeldingen
+ Noodwachtwoord
+ Stel een noodwachtwoord in waarmee je informatietoegang voorkomt
+ Uitbreidingen
+ Over
+ Leesmij
+ Informatie exporteren
+ Kaartstijl
+ Je hebt geen planten
+ Je hebt geen schema\'s
+ Je hebt geen foto\'s
+ Eerdere acties
+ Kalender tonen/verbergen
+
+ %d geselecteerd
+ Met dank aan
+ Vertaald door
+ Helpen met vertalen
+ Bezig met versleutelen; even geduld...
+ Bezig met ontsleutelen; even geduld...
+
+
+ 1s
+ s
+ m
+ u
+ d
+ w
+ ma
+ j
+
+ Informatie
+
+
+ - seconde
+ - seconden
+
+
+
+ - minuut
+ - minuten
+
+
+
+ - uur
+ - uur
+
+
+
+ - dag
+ - dagen
+
+
+
+ - week
+ - weken
+
+
+
+ - maand
+ - maanden
+
+
+
+ - jaar
+ - jaar
+
+
+
+ - Origineel
+ - Compact
+ - Extreem
+
+
+ Alle niet-opgeslagen aanpassingen gaan verloren
+ Afsluiten
+ Planten
+ Luchtvochtigheid
+ Huidige temperatuur:
+ Huidige luchtvochtigheid:
+ Lichtschema
+ Item verwijderen?
+ Weet je zeker dat je <b>\'%s\'</b> wilt verwijderen?
+ Tonen
+ Verbergen
+ Lichten aan
+ Lichten uit
+
+ Gebackupt: <b>%s</b>
+ Opslaglocatie van afbeeldingen - momenteel: <b>%s</b>
+ Kan afbeeldingslocatie niet instellen
+ Opslaglocatie voor afbeeldingen
+ Afbeeldingscache wissen
+ Afbeeldings- en geheugencache gewist
+ Bezig met importeren; even geduld...
+ Gegevenstaak
+ Taak volbracht
+ Er zijn geen acties
+
+ Bezig met exporteren van groeilog van %s...
+ %s is geëxporteerd
+ %s is geëxporteerd naar %s
+ Exporteren voltooid
+ Afbeeldingen bijvoegen?
+ Er in geen informatie beschikbaar
+
+ Cameramachtiging vereist om foto\'s te maken
+ Back-upbeheer
+ Opslaglocatie
+ Opslaglocatie van back-ups - momenteel: <b>%s</b>
+ Kan back-uplocatie niet instellen
+ Kan planten niet importeren uit bestand
+ Planten importeren uit bestand
+ De planten zijn geïmporteerd
+
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 0935d3b4..0be65b56 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -246,7 +246,7 @@
Используйте этот пароль на стадии расшифровки чтобы не допустить загрузки данных
Внимание
Используется базовая форма AES-шифрования на основе введённого пароля. Данный метод не гарантирует защиту от доступа правоохранительными органами.
- Включено резервное копирование. Резервные копии будут храниться в /sdcard/backups/GrowTracker/
+ Резервное копирование включено, копии будут храниться в %s
Восстановление в %s завершено
Не удалось восстановить резервную копию %s. Возможно файл %s
зашифрован
@@ -410,7 +410,7 @@
Очистить кеш изображений
Кеш изображений очищен
Импорт изображений, может занять некоторое время...
- Задание данных
+ Обработка данных
Задание выполнено
Нет действий
@@ -420,4 +420,13 @@
Экспорт завершён
Включить изображения?
Данные отсутствуют
+
+ Камере необходимо разрешение чтобы делать фото
+ Управление резервными копиями
+ Расположение резервных копий
+ Расположение резервных копий, сейчас <b>%s</b>
+ Не удалось изменить расположение резервных копий
+ Не удалось импортировать растения
+ Импортировать растения из файла
+ Растение успешно импортировано из файла
diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml
index 9ca6236a..b6e3bfc0 100644
--- a/app/src/main/res/values-uk/strings.xml
+++ b/app/src/main/res/values-uk/strings.xml
@@ -149,7 +149,7 @@
Мін:
Макс:
- Середній:
+ Серед:
Температура
Загальні
Не вдалося завантажити рослину
@@ -174,7 +174,7 @@
Встановити
Змінити
Додати
- Відмінити
+ Скасувати
дію
Ок
Обов\'язкове поле
@@ -246,7 +246,7 @@
Введіть цей пароль під час розшифрування щоб упередити завантаження даних
Увага
Використовується базова форма AES-шифрування на основі введеного пароля. Даний спосіб не гарантує захисту від правоохоронних органів.
- Резервне копіювання ввімкнено. Резервні копії будуть зберігатися в /sdcard/backups/GrowTracker/
+ Резервне копіювання ввімкнено, копії зберігатимуться до %s
Відновлення до %s завершено
Не вдалося відновити з копії %s. Можливо файл %s
зашифрований
@@ -410,7 +410,7 @@
Очистити кеш зображень
Кеш зображень очищено
Імпортую зображення, це може тривати довго...
- Завдання даних
+ Обробка даних
Завдання виконано
Немає дій
@@ -420,4 +420,13 @@
Експорт завершено
Включити зображення?
Дані відсутні
+
+ Камері потрібен дозвіл щоб робити знімки
+ Керування резервними копіями
+ Розташування резервних копій
+ Наразі <b>%s</b>
+ Не вдалося встановити розташування резервних копій
+ Не вдалося імпортувати
+ Імпортувати рослини з файлу
+ Імпорт завершено
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index ab4ba952..73bfbd84 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -4,4 +4,5 @@
8dp
4dp
190dp
+ 256dp
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cc1232ec..765f15b8 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -246,7 +246,7 @@
Provide this password during unencryption phase to prevent data from being loaded
Warning
This is basic form of AES encryption based on a provided passphrase. This is not a guaranteed form of protection from law enforcement agencies.
- Backup enabled, backups will be stored in /sdcard/backups/GrowTracker/
+ Backup enabled, backups will be stored in %s
Restore to %s completed
Could not restore from backup %s. File may be %s
encrypted
@@ -336,6 +336,7 @@
- Alex (Noxmiles) - de
- Basti B (Weltenesche) - de
+ - Heimen Stoffels (Vistaus) - nl/NL
- EmmanuelMess - es
- Maxtille - fr
- Patrick B (EukalyptusX) - de
@@ -423,4 +424,13 @@
Export complete
Include images?
There is no data available
+
+ Camera permission is required to take photos
+ Backup management
+ Backup storage location
+ Backup storage location, currently <b>%s</b>
+ Failed to set backup location
+ Failed to import plants from file
+ Import plants from file
+ Plants successfully imported from file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index ca50f123..76e5f70a 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -71,13 +71,6 @@
app:title="@string/settings_data_management"
android:key="settings_data"
>
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
diff --git a/fastlane/metadata/android/en-GB/changelogs/1370.txt b/fastlane/metadata/android/en-GB/changelogs/1370.txt
new file mode 100644
index 00000000..87fa3d16
--- /dev/null
+++ b/fastlane/metadata/android/en-GB/changelogs/1370.txt
@@ -0,0 +1,12 @@
+- Adds Dutch (Netherlands) localisations
+- Adds custom backup path setting
+- Adds import plants from file functionality
+
+- Allows old backups to be visible
+
+- Fixes issue with adding plant via garden not working
+- Fixes grow log date formatting
+- Fixes issue with folders not being deleted if there are no images
+- Fixes issue with camera intent not taking photos correctly due to lack of permission
+- Fixes issue with calendar obstructing actions view in landscape
+- Fixes issue with importing large images causing corrupt images