From 3a581fa44928dbe6e4b11053cdcc5445a2866c54 Mon Sep 17 00:00:00 2001 From: Steve <51911097+uDEV2019@users.noreply.github.com> Date: Sat, 2 Mar 2024 02:11:40 +0100 Subject: [PATCH 01/13] german short --- fastlane/metadata/android/de-DE/short_description.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/de-DE/short_description.txt diff --git a/fastlane/metadata/android/de-DE/short_description.txt b/fastlane/metadata/android/de-DE/short_description.txt new file mode 100644 index 00000000..c94d52db --- /dev/null +++ b/fastlane/metadata/android/de-DE/short_description.txt @@ -0,0 +1 @@ +Behalten Sie den Überblick über Ihre Medikamente From 28e075948b5bc97596fcb8f2e4bdfbd4ffd77656 Mon Sep 17 00:00:00 2001 From: Steve <51911097+uDEV2019@users.noreply.github.com> Date: Sat, 2 Mar 2024 02:14:49 +0100 Subject: [PATCH 02/13] german full --- fastlane/metadata/android/de-DE/full_description.txt | 1 + 1 file changed, 1 insertion(+) create mode 100644 fastlane/metadata/android/de-DE/full_description.txt diff --git a/fastlane/metadata/android/de-DE/full_description.txt b/fastlane/metadata/android/de-DE/full_description.txt new file mode 100644 index 00000000..d9c3262e --- /dev/null +++ b/fastlane/metadata/android/de-DE/full_description.txt @@ -0,0 +1 @@ +

MediTrak ist eine Android-App, die es Benutzern erleichtern soll, den Überblick über ihre Medikamente zu behalten und sich an deren Einnahme zu erinnern. Die App bietet die Möglichkeit, Medikamente für mehrere Patienten hinzuzufügen, Medikamentenerinnerungen in verschiedenen Intervallen festzulegen und Notizen zu einem Medikament zu machen, um eventuelle Nebenwirkungen aufzuzeichnen. Alle Benutzerdaten werden lokal gespeichert und weder an den Entwickler noch an andere Dritte weitergegeben.

From efd153368d426ce175056abdfe477eb3dd11242e Mon Sep 17 00:00:00 2001 From: Steve <51911097+uDEV2019@users.noreply.github.com> Date: Sat, 2 Mar 2024 03:05:01 +0100 Subject: [PATCH 03/13] german strings --- app/src/main/res/values-de/strings.xml | 235 +++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 app/src/main/res/values-de/strings.xml diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml new file mode 100644 index 00000000..d1b37645 --- /dev/null +++ b/app/src/main/res/values-de/strings.xml @@ -0,0 +1,235 @@ + + + Ja + Nein + Schließen + Abbrechen + MediTrak + Bearbeiten + Übernehmen + Änderungen speichern + Speichern + Ich + Dein + OK + Löschen + Ich stimme zu + + + Ungültiger Wert angegeben + Der Wert muss eine positive ganze Zahl sein + Der angegebene Wert ist zu groß + Der angegebene Wert darf 50 nicht überschreiten + Der angegebene Wert muss größer als 0 sein + Der angegebene Name ist ungültig + Bitte gib einen Namen an + Bitte gib die Dosierung ein + Bitte gib die Einheiten für dieses Medikament ein + Bitte wähle die Häufigkeit aus + Bitte wähle eine Uhrzeit aus + Bitte wähle ein Startdatum aus. + Bitte gib an, wie oft dieses Medikament täglich eingenommen wird + Bitte gibt an, wie oft das Medikament eingenommen wird. + Bitte wähle eine Zeiteinheit aus. + Bitte gib einen Namen für dieses Medikament ein + Fehlender Dateiname + Die Exportdatei muss die Erweiterung .json haben + + + Mmm dd, yyyy + hh:mm aa + + + Sonntag + Montag + Dienstag + Mittwoch + Donnerstag + Freitag + Samstag + Minuten + Stunden + Tage + Wochen + + + Keine Medikamente gefunden + Medikamentenplan + Keine Medikamente für %1$s + Medikamente können nicht mehr als %1$d Stunden im Voraus eingenommen werden + Nach Bedarf dosieren + Willkommen zu MediTrak! + + Diese Anwendung soll dir dabei helfen, den Überblick über deine Medikamente zu behalten. Es ist + nicht als medizinischer Rat gedacht und die in dieser Anwendung bereitgestellten Informationen + sollten nicht als solche missverstanden werden. Die Ersteller dieser Anwendung haften nicht für + etwaige versäumte Dosen Ihrer Medikamente, aus welchem Grund auch immer. Wenn Du medizinische Hilfe benötigst, + wende dich bitte an einen Arzt. + Durch die Nutzung dieser Anwendung stimmst Du den oben genannten Bedingungen sowie denen der GPL2-Lizenz zu, + unter der die App veröffentlicht wird. \n\n + + Diese Anwendung wird unter der GNU Public License Version 2 veröffentlicht. + Weitere Einzelheiten findest Du weiter unten: + + + + Medikamente hinzufügen + Medikamente bearbeiten + Medikamente löschen + am: %1$s um: %2$s + Bearbeitet: %1$s um: %2$s + Patient geändert von %1$s zu %2$s\n + Name geändert von %1$s zu %2$s\n + Alias %1$s entfernt\n + Alias %1$s hinzugefügt\n + Alias geändert von %1$s zu %2$s\n + Dosierung geändert von %1$s auf %2$s\n + Häufigkeit geändert von %1$s auf %2$s\n + Zeiten pro + Ich + Patient + Andere + Name + Medikamentennamee + Dosierung + Häufigkeit + Eingenommen jeden + Einheiten + Eingenommen + Häufigkeit + Mehrmals pro Tag + Wie benötigt + Täglich + Wöchentlich + Monatlich + + Wer nimmt dieses Medikament?? + Jemand anderes + + Alias für Benachrichtigungen + Wie oft wird das Medikament eingenommen? + Startdatum + Eingenommen um + Zeiteinheit + Benutzerdefinierte Häufigkeit + Hier tippen, um die Uhrzeit einzustellen + Zeiten pro Tag + Hier tippen, um die Uhrzeit einzustellen + Beginnend: + Alias für Medikamente + Alias für Medikamentenbenachrichtigung + Medikamentenalias wird in der Benachrichtigung angezeigt + PPlatzhalter, tippen Sie erneut und klicken Sie dann auf Abbrechen + Deine Medikamente: + Für dieses Medikament liegen keine Hinweise vor + z.B. mg, g, Tropfen + Patientenname: + Keine Aliase + Tippen, um das Datum einzustellen + Um: + Tippen, um die Zeit einzustellen + Änderungen rückwirkend anwenden + Warnung: Änderungen der Häufigkeit können dazu führen, dass zuvor eingenommene Dosen nicht mehr im Zeitplan sichtbar sind. + Änderungen der Dosierung und Häufigkeit können rückwirkend ab der vorherigen Änderung angewendet werden. Möchten Sie diese Änderungen rückwirkend anwenden? + Änderungen übernehmen ab: + Letzte Bearbeitung + Beginn der Medikamenteneinnahme + + + Meine Medikamente + Name des Medikaments: %1$s + Dosierung: %1$s %2$s + Täglich eingenommen um: + Eingenommen, alle: + Alias: %1$s + Eingenommen, seit: %1$s + Nach Bedarf eingenommen. + Alle vorherigen Änderungen wurden überschrieben. + + + Notizen + Notiz hinzufügen + Notiz bearbeiten + + + Einstellungen + Optionen + Lizenz + MediTrak ist eine kostenlose Open-Source-Anwendung, die dir dabei helfen soll, den Überblick über deine Medikamente zu behalten, damit Du die Frage \"Habe ich gestern meine Medikamente eingenommen?\" einfacher beantworten kannst + Copyright © 2022 Adam Guidarini + MediTrak ist freie Software und darf unter den Bedingungen der GNU General Public License, wie von der Free Software Foundation veröffentlicht, entweder Version 2 oder (nach deiner Wahl) später, weitergegeben und/oder geändert werden. + + Dieses Programm ist freie Software; Du kannst es weiterverbreiten und/oder + unter den Bedingungen der GNU General Public License, wie von der + Free Software Foundation veröffentlicht, ändern; entweder Version 2 + der Lizenz oder (nach deiner Wahl) einer späteren Version. + + Dieses Programm wird in der Hoffnung verbreitet, dass es nützlich sein wird, + aber OHNE JEGLICHE GARANTIE; ohne die stillschweigende Garantie von + + MARKTGÄNGIGKEIT oder EIGNUNG FÜR EINEN BESTIMMTEN ZWECK. Siehe die + weiteren Informationen zur GNU General Public License + + + Sie sollten eine Kopie der GNU General Public License erhalten haben + zusammen mit diesem Programm; Wenn nicht, schreiben Sie an die Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + Kann Medikamente nicht mehr einnehmen als + Stunden im Voraus + Einstellungen + Alles löschen + Medikation pausieren + Medikation fortsetzen + Bist Du sicher, dass Du %1$s pausieren möchtest? Es erscheint nicht mehr in deinem Zeitplan, kann aber jederzeit wieder aufgenommen werden. + Wenn Du %1$s fortsetzt, wird dein Zeitplan wiederhergestellt. Bist Du sicher, dass Du fortfahren möchtest? + Benachrichtigungen aktivieren + Zeitlimit im Voraus deaktivieren + Thema + Hell + Dunkel + System + Grenze, bis zu der Medikamente nicht eingenommen werden dürfen: + Exportieren + Importieren + Daten + Export-Verzeichnis + Export-Dateiname + Downloads + Dokumente + Daten exportieren + Daten erfolgreich nach %1$s exportiert + Daten erfolgreich importiert + Daten konnten nicht exportiert werden + Daten konnten nicht importiert werden + Die Importdatei konnte nicht abgerufen werden + Benachrichtigungen sind bereits aktiviert + Datumsformat + MM/TT/JJJJ + TT/MM/JJJJ + Zeitformat + 12 Stunden + 24 Stunden + + + Noch nicht eingenommen + Nicht eingenommen + Als eingenommen markiert + Dosisinformationen + Eingenommen um + Eingenommen am + Diese Dosis + + + Alle gespeicherten Daten löschen + Alle gespeicherten Daten löschen? Diese Aktion kann nicht rückgängig gemacht werden. + Alle Daten wurden gelöscht + + + Eingenommen + Schlummern + Es ist Zeit, dein %1$s zu nehmen + Es ist Zeit für die %2$s von %1$s + Dosis hinzufügen + + From edd7f7e4e86ca8150515a431999e480748be3c05 Mon Sep 17 00:00:00 2001 From: Adam Guidarini <45023561+AdamGuidarini@users.noreply.github.com> Date: Fri, 1 Mar 2024 22:20:47 -0500 Subject: [PATCH 04/13] Gradle update, enforce readonly for import file --- app/build.gradle | 11 +++++++++-- app/src/main/cpp/DbManager/DbManager.cpp | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 978abcf1..34810d64 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,13 +3,20 @@ plugins { } android { + dependenciesInfo { + // Disables dependency metadata when building APKs. + includeInApk = false + // Disables dependency metadata when building Android App Bundles. + includeInBundle = false + } + defaultConfig { applicationId "projects.medicationtracker" minSdkVersion 26 targetSdkVersion 33 compileSdk 33 - versionCode 15 - versionName "0.11.4" + versionCode 16 + versionName "0.11.5" ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' diff --git a/app/src/main/cpp/DbManager/DbManager.cpp b/app/src/main/cpp/DbManager/DbManager.cpp index 0247d7fc..df1d77b3 100644 --- a/app/src/main/cpp/DbManager/DbManager.cpp +++ b/app/src/main/cpp/DbManager/DbManager.cpp @@ -406,7 +406,7 @@ void DbManager::importData(const std::string &importFilePath, const vector Date: Fri, 1 Mar 2024 22:35:11 -0500 Subject: [PATCH 05/13] Update README.md Added languages section --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 86066b90..c5f0d96c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ MediTrak is an Android application designed to make it easier for users to keep + Android 8.0 Oreo or newer + 15 MB storage +## Languages + + - English + - German + ## Building the App It is strongly recommended to use [Android Studio](https://developer.android.com/studio) to build and test this application. From f369da5718cd7b76a922e15575962111a7a83dd8 Mon Sep 17 00:00:00 2001 From: Adam Guidarini <45023561+AdamGuidarini@users.noreply.github.com> Date: Sat, 2 Mar 2024 08:33:45 -0500 Subject: [PATCH 06/13] Can change languages --- app/build.gradle | 2 +- app/src/main/AndroidManifest.xml | 3 ++- app/src/main/res/xml/locales_config.xml | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 app/src/main/res/xml/locales_config.xml diff --git a/app/build.gradle b/app/build.gradle index 978abcf1..a44f1a1e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ android { compileSdk 33 versionCode 15 versionName "0.11.4" - + resConfigs('en', 'de') ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ca02332b..59774ca5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,7 +18,8 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" - android:theme="@style/Theme.MedicationTracker"> + android:theme="@style/Theme.MedicationTracker" + android:localeConfig="@xml/locales_config"> + + + + From fd50da2bde9e312c270c8e3b732e665fda5ef40f Mon Sep 17 00:00:00 2001 From: Adam Guidarini <45023561+AdamGuidarini@users.noreply.github.com> Date: Sat, 2 Mar 2024 14:42:29 -0500 Subject: [PATCH 07/13] Update README.md Gave credit for German translation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c5f0d96c..c26c2906 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ MediTrak is an Android application designed to make it easier for users to keep ## Languages - - English - - German + - English (default) + - German by [uDEV2019](https://github.com/uDEV2019) ## Building the App From c5dd6108a7ce6a16f8c5be13255477c52a5b4218 Mon Sep 17 00:00:00 2001 From: Adam Guidarini <45023561+AdamGuidarini@users.noreply.github.com> Date: Sat, 2 Mar 2024 18:36:23 -0500 Subject: [PATCH 08/13] German works in older API versions, can actually write to downloads --- app/build.gradle | 5 +- app/src/main/AndroidManifest.xml | 14 +++- .../DatabaseController/DatabaseController.h | 4 +- app/src/main/cpp/DbManager/DbManager.cpp | 35 +++++++-- app/src/main/cpp/DbManager/DbManager.h | 1 + app/src/main/cpp/medicationtracker.cpp | 2 + .../Dialogs/BackupDestinationPicker.java | 71 ++++--------------- .../projects/medicationtracker/Settings.java | 27 ++++--- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 82 insertions(+), 79 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ab7ccff7..557d8bda 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,10 +14,11 @@ android { applicationId "projects.medicationtracker" minSdkVersion 26 targetSdkVersion 33 - compileSdk 33 + compileSdk 34 versionCode 16 versionName "0.11.5" + resConfigs('en', 'de') ndk { @@ -54,7 +55,7 @@ android { dependencies { implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.9.0' + implementation 'com.google.android.material:material:1.11.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'androidx.preference:preference:1.2.1' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 59774ca5..cc5dacb1 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,10 +8,9 @@ + android:maxSdkVersion="28" /> + android:maxSdkVersion="28" /> + + + + \ No newline at end of file diff --git a/app/src/main/cpp/DatabaseController/DatabaseController.h b/app/src/main/cpp/DatabaseController/DatabaseController.h index fbc8350b..7ff9739f 100644 --- a/app/src/main/cpp/DatabaseController/DatabaseController.h +++ b/app/src/main/cpp/DatabaseController/DatabaseController.h @@ -14,12 +14,12 @@ using namespace std; namespace TimeFormats { const string _12_HOUR = "hh:mm a"; const string _24_HOUR = "HH:mm"; -}; +} namespace DateFormats { const string MM_DD_YYYY = "MM/dd/yyyy"; const string DD_MM_YYYY = "dd/MM/yyyy"; -}; +} class DatabaseController { private: diff --git a/app/src/main/cpp/DbManager/DbManager.cpp b/app/src/main/cpp/DbManager/DbManager.cpp index df1d77b3..6d3d2e03 100644 --- a/app/src/main/cpp/DbManager/DbManager.cpp +++ b/app/src/main/cpp/DbManager/DbManager.cpp @@ -345,16 +345,24 @@ void DbManager::exportData(const string& exportFilePath, const vector& i map>> data = getAllRowFromAllTables(ignoreTables); string outData; + outFile.exceptions(std::ifstream::failbit | std::ifstream::badbit); + try { outFile.open(exportFilePath, fstream::trunc); + } catch (system_error& error) { + const string errMessage = "File failed to open at '" + + exportFilePath + + "' with error '" + + error.code().message() + + "'"; + + cerr << errMessage << endl; - if (!outFile.is_open()) { - throw runtime_error("Could not open file: " + exportFilePath); + if (outFile.is_open()) { + outFile.close(); } - } catch (exception& e) { - cerr << e.what() << endl; - throw runtime_error("Could not open file: " + exportFilePath); + throw runtime_error(errMessage); } outFile << "{"; @@ -401,6 +409,9 @@ void DbManager::importData(const std::string &importFilePath, const vector{fin}, {}); fin.close(); + } catch (system_error& error) { + const string errMessage = "File failed to open at '" + importFilePath + + "' with error '" + error.code().message() + "'" + + " error number: " + to_string(errno); + + cerr << errMessage << endl; + + if (fin.is_open()) { + fin.close(); + } + + throw runtime_error(errMessage); } catch (runtime_error& error) { cerr << error.what() << ": " << importFilePath << endl; diff --git a/app/src/main/cpp/DbManager/DbManager.h b/app/src/main/cpp/DbManager/DbManager.h index 183726e8..82d57fdf 100644 --- a/app/src/main/cpp/DbManager/DbManager.h +++ b/app/src/main/cpp/DbManager/DbManager.h @@ -14,6 +14,7 @@ #include #include #include +#include using namespace std; diff --git a/app/src/main/cpp/medicationtracker.cpp b/app/src/main/cpp/medicationtracker.cpp index 8ad40616..64264385 100644 --- a/app/src/main/cpp/medicationtracker.cpp +++ b/app/src/main/cpp/medicationtracker.cpp @@ -75,6 +75,8 @@ Java_projects_medicationtracker_Helpers_NativeDbHelper_dbImporter( std::vector ignoredTbls; int len = env->GetArrayLength(ignored_tables); + + for (int i = 0; i < len; i++) { auto str = (jstring) (env->GetObjectArrayElement(ignored_tables, i)); string rawString = env->GetStringUTFChars(str, JNI_FALSE); diff --git a/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java b/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java index 0119e788..9b97e764 100644 --- a/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java +++ b/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java @@ -1,7 +1,10 @@ package projects.medicationtracker.Dialogs; +import android.app.Activity; import android.app.Dialog; import android.content.DialogInterface; +import android.content.Intent; +import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.text.Editable; @@ -12,6 +15,9 @@ import android.widget.ArrayAdapter; import android.widget.Toast; +import androidx.activity.result.ActivityResult; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; @@ -31,9 +37,7 @@ public class BackupDestinationPicker extends DialogFragment { private String exportDir; private String exportFile; - private MaterialAutoCompleteTextView dirSelector; private TextInputLayout fileNameInputLayout; - private TextInputEditText fileName; private NativeDbHelper nativeDb; @Override @@ -62,6 +66,10 @@ public Dialog onCreateDialog(Bundle savedInstances) { dialog = builder.create(); dialog.show(); + MaterialAutoCompleteTextView dirSelector = dialog.findViewById(R.id.export_dir); + fileNameInputLayout = dialog.findViewById(R.id.export_file_layout); + TextInputEditText fileName = dialog.findViewById(R.id.export_file); + dirs.add(getString(R.string.downloads)); dirs.add(getString(R.string.documents)); @@ -72,24 +80,10 @@ public Dialog onCreateDialog(Bundle savedInstances) { adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line, dirs); - dirSelector = dialog.findViewById(R.id.export_dir); - fileNameInputLayout = dialog.findViewById(R.id.export_file_layout); - fileName = dialog.findViewById(R.id.export_file); - dirSelector.setAdapter(adapter); - dirSelector.setText(adapter.getItem(0)); - - exportDir = directories[0]; + dirSelector.setText(dirSelector.getAdapter().getItem(0).toString(), false); - dirSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - exportDir = directories[position]; - } - - @Override - public void onNothingSelected(AdapterView parent) {} - }); + dirSelector.setOnItemClickListener((parent, view, position, id) -> exportDir = directories[position]); exportFile = "meditrak_" + now.getYear() + "_" @@ -99,11 +93,8 @@ public void onNothingSelected(AdapterView parent) {} fileName.setText(exportFile); fileName.addTextChangedListener(new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} - - @Override - public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} + @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @Override public void afterTextChanged(Editable editable) { @@ -134,40 +125,6 @@ public void afterTextChanged(Editable editable) { return dialog; } - @Override - public void onStart() { - final String[] directories; - ArrayAdapter adapter; - ArrayList dirs = new ArrayList<>(); - - super.onStart(); - - dirs.add(getString(R.string.downloads)); - dirs.add(getString(R.string.documents)); - - directories = new String[] { - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath(), - Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getPath() - }; - - adapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_dropdown_item_1line, dirs); - - dirSelector = getDialog().findViewById(R.id.export_dir); - fileName = getDialog().findViewById(R.id.export_file); - - dirSelector.setAdapter(adapter); - - dirSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - exportDir = directories[position]; - } - - @Override - public void onNothingSelected(AdapterView parent) {} - }); - } - private void onExportClick() { String resMessage; boolean res = nativeDb.dbExport( diff --git a/app/src/main/java/projects/medicationtracker/Settings.java b/app/src/main/java/projects/medicationtracker/Settings.java index e867b6ab..bd7043c0 100644 --- a/app/src/main/java/projects/medicationtracker/Settings.java +++ b/app/src/main/java/projects/medicationtracker/Settings.java @@ -13,6 +13,7 @@ import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Color; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; @@ -36,6 +37,9 @@ import com.google.android.material.textfield.MaterialAutoCompleteTextView; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileInputStream; import java.util.ArrayList; import java.util.Objects; @@ -46,7 +50,7 @@ public class Settings extends AppCompatActivity { private final DBHelper db = new DBHelper(this); - private ActivityResultLauncher chooseFileLauncher; + private ActivityResultLauncher chooseFileLauncher; private final ActivityResultLauncher permissionRequester = registerForActivityResult( new ActivityResultContracts.RequestPermission(), isGranted -> {} @@ -89,12 +93,14 @@ protected void onCreate(Bundle savedInstanceState) { setTimeFormatMenu(); chooseFileLauncher = registerForActivityResult( - new ActivityResultContracts.GetContent(), + new ActivityResultContracts.StartActivityForResult(), result -> { - if (result != null && result.getPath() != null) { + Uri data = result.getData().getData(); + + if (data != null && data.getPath() != null) { String absPath = ""; String name; - Cursor cursor = getContentResolver().query(result, null, null, null, null); + Cursor cursor = getContentResolver().query(data, null, null, null, null); if (cursor != null && cursor.getCount() > 0) { cursor.moveToFirst(); @@ -104,11 +110,11 @@ protected void onCreate(Bundle savedInstanceState) { return; } - switch (Objects.requireNonNull(result.getAuthority())) { + switch (Objects.requireNonNull(data.getAuthority())) { case "com.android.providers.downloads.documents": absPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/" + name; break; - case "com.android.providers.documents.documents": + case "com.android.externalstorage.documents": absPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getPath() + "/" + name; break; } @@ -144,7 +150,8 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { */ @Override public void onBackPressed() { - Intent intent = new Intent(this,MainActivity.class); + super.onBackPressed(); + Intent intent = new Intent(this, MainActivity.class); finish(); startActivity(intent); } @@ -403,12 +410,16 @@ public void onExportClick(View view) { public void onImportClick(View view) { String type= Build.VERSION.SDK_INT >= 30 ? "application/json" : "*/*"; + Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT); + + i.addCategory(Intent.CATEGORY_OPENABLE); + i.setType(type); if (Build.VERSION.SDK_INT <= 32 && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissionRequester.launch(Manifest.permission.READ_EXTERNAL_STORAGE); } - chooseFileLauncher.launch(type); + chooseFileLauncher.launch(i); } /** diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index d1b37645..adefbe32 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -210,6 +210,7 @@ Zeitformat 12 Stunden 24 Stunden + Sprache Noch nicht eingenommen diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 87018a19..7ed8ff07 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -208,6 +208,7 @@ Time Format 12 Hour 24 Hour + Language Not yet taken From bf61e2e25322ecf7b7d71bd63d25867fedc692c6 Mon Sep 17 00:00:00 2001 From: Adam Guidarini <45023561+AdamGuidarini@users.noreply.github.com> Date: Sat, 2 Mar 2024 23:48:00 -0500 Subject: [PATCH 09/13] Importing works consistently now --- app/src/main/AndroidManifest.xml | 12 +-- .../DatabaseController/DatabaseController.cpp | 11 +- .../DatabaseController/DatabaseController.h | 10 +- app/src/main/cpp/DbManager/DbManager.cpp | 26 ++--- app/src/main/cpp/DbManager/DbManager.h | 10 +- app/src/main/cpp/medicationtracker.cpp | 16 +-- .../Dialogs/BackupDestinationPicker.java | 2 + .../Helpers/NativeDbHelper.java | 8 +- .../projects/medicationtracker/Settings.java | 100 +++++++++++------- 9 files changed, 121 insertions(+), 74 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index cc5dacb1..30b93bbf 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,18 +7,16 @@ - - + + + android:localeConfig="@xml/locales_config" + android:requestLegacyExternalStorage="true" > - - \ No newline at end of file diff --git a/app/src/main/cpp/DatabaseController/DatabaseController.cpp b/app/src/main/cpp/DatabaseController/DatabaseController.cpp index 11136aca..26d0ce1f 100644 --- a/app/src/main/cpp/DatabaseController/DatabaseController.cpp +++ b/app/src/main/cpp/DatabaseController/DatabaseController.cpp @@ -166,9 +166,16 @@ void DatabaseController::exportJSON( manager.exportData(exportFilePath, ignoreTables); } -void DatabaseController::importJSON( +void DatabaseController::importJSONFile( const string &importFilePath, const vector &ignoreTables ) { - manager.importData(importFilePath, ignoreTables); + manager.importDataFromFile(importFilePath, ignoreTables); +} + +void DatabaseController::importJSONString( + string &data, + const vector &ignoreTables +) { + manager.importData(data, ignoreTables); } diff --git a/app/src/main/cpp/DatabaseController/DatabaseController.h b/app/src/main/cpp/DatabaseController/DatabaseController.h index 7ff9739f..a71680c0 100644 --- a/app/src/main/cpp/DatabaseController/DatabaseController.h +++ b/app/src/main/cpp/DatabaseController/DatabaseController.h @@ -148,7 +148,15 @@ class DatabaseController { * column that does not exist in the provided database/table. * @param importFilePath Path to JSON file storing data to import. */ - void importJSON(const string& importFilePath, const vector& ignoreTables = {}); + void importJSONFile(const string& importFilePath, const vector& ignoreTables = {}); + + /** + * Imports data from JSON file and writes it to database, + * throws an error if attempting to write to a table or + * column that does not exist in the provided database/table. + * @param importData JSON formatted string containing data to import + */ + void importJSONString(string& importData, const vector& ignoreTables = {}); }; diff --git a/app/src/main/cpp/DbManager/DbManager.cpp b/app/src/main/cpp/DbManager/DbManager.cpp index 6d3d2e03..49265fbd 100644 --- a/app/src/main/cpp/DbManager/DbManager.cpp +++ b/app/src/main/cpp/DbManager/DbManager.cpp @@ -350,11 +350,9 @@ void DbManager::exportData(const string& exportFilePath, const vector& i try { outFile.open(exportFilePath, fstream::trunc); } catch (system_error& error) { - const string errMessage = "File failed to open at '" - + exportFilePath + - "' with error '" - + error.code().message() - + "'"; + const string errMessage = "File failed to open at '" + exportFilePath + + "' with error '" + error.code().message() + "'" + + " error number: " + to_string(errno); cerr << errMessage << endl; @@ -401,17 +399,12 @@ void DbManager::exportData(const string& exportFilePath, const vector& i outFile.close(); } -void DbManager::importData(const std::string &importFilePath, const vector& ignoreTables) { - fstream fin; +void DbManager::importDataFromFile(const std::string &importFilePath, const vector& ignoreTables) { string inData; - map>> data; - vector tables = getTables(ignoreTables); - stringstream importQuery; - char* err; + ifstream fin; fin.exceptions(std::ifstream::failbit | std::ifstream::badbit); - try { if (importFilePath.substr(importFilePath.find_last_of('.') + 1) != "json") { throw runtime_error("Provided file is not a JSON file"); @@ -440,6 +433,15 @@ void DbManager::importData(const std::string &importFilePath, const vector& ignoreTables) { + map>> data; + vector tables = getTables(ignoreTables); + stringstream importQuery; + char* err; + // Remove unneeded chars inData.erase( remove_if(inData.begin(), inData.end(), [](unsigned char x) { return std::isspace(x); }), diff --git a/app/src/main/cpp/DbManager/DbManager.h b/app/src/main/cpp/DbManager/DbManager.h index 82d57fdf..c7dd46b3 100644 --- a/app/src/main/cpp/DbManager/DbManager.h +++ b/app/src/main/cpp/DbManager/DbManager.h @@ -14,7 +14,6 @@ #include #include #include -#include using namespace std; @@ -161,7 +160,14 @@ class DbManager { * column that does not exist in the provided database/table. * @param importFilePath Path to JSON file storing data to import. */ - void importData(const string& importFilePath, const vector& ignoreTables = {}); + void importDataFromFile(const string &importFilePath, const vector &ignoreTables); + + /** + * Imports data from a string + * @param inData Data to feed to database, must be in JSON format + * @param ignoreTables Tables not to ignore that may be referenced indata + */ + void importData(string& inData, const vector& ignoreTables = {}); }; diff --git a/app/src/main/cpp/medicationtracker.cpp b/app/src/main/cpp/medicationtracker.cpp index 64264385..c3458f4c 100644 --- a/app/src/main/cpp/medicationtracker.cpp +++ b/app/src/main/cpp/medicationtracker.cpp @@ -5,6 +5,7 @@ #include #include #include +#include std::map getValues(jobjectArray arr, JNIEnv *env) { const jclass pair = env->FindClass("android/util/Pair"); @@ -67,15 +68,14 @@ Java_projects_medicationtracker_Helpers_NativeDbHelper_dbImporter( JNIEnv *env, jobject thiz, jstring db_path, - jstring import_path, + jstring file_contents, jobjectArray ignored_tables ) { std::string db = env->GetStringUTFChars(db_path, new jboolean(true)); - std::string importPath = env->GetStringUTFChars(import_path, new jboolean(true)); + std::string fileContents = env->GetStringUTFChars(file_contents, new jboolean(true)); std::vector ignoredTbls; int len = env->GetArrayLength(ignored_tables); - - + bool success = true; for (int i = 0; i < len; i++) { auto str = (jstring) (env->GetObjectArrayElement(ignored_tables, i)); @@ -87,14 +87,16 @@ Java_projects_medicationtracker_Helpers_NativeDbHelper_dbImporter( DatabaseController controller(db); try { - controller.importJSON(importPath, ignoredTbls); + controller.importJSONString(fileContents, ignoredTbls); } catch (exception &e) { __android_log_write(ANDROID_LOG_ERROR, nullptr, e.what()); - return false; + success = false; } - return true; +// env->ReleaseStringUTFChars(file_contents, fileContents.c_str()); + + return success; } extern "C" diff --git a/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java b/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java index 9b97e764..aedbb5b8 100644 --- a/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java +++ b/app/src/main/java/projects/medicationtracker/Dialogs/BackupDestinationPicker.java @@ -83,6 +83,8 @@ public Dialog onCreateDialog(Bundle savedInstances) { dirSelector.setAdapter(adapter); dirSelector.setText(dirSelector.getAdapter().getItem(0).toString(), false); + exportDir = directories[0]; + dirSelector.setOnItemClickListener((parent, view, position, id) -> exportDir = directories[position]); exportFile = "meditrak_" diff --git a/app/src/main/java/projects/medicationtracker/Helpers/NativeDbHelper.java b/app/src/main/java/projects/medicationtracker/Helpers/NativeDbHelper.java index 044940d3..eed994b9 100644 --- a/app/src/main/java/projects/medicationtracker/Helpers/NativeDbHelper.java +++ b/app/src/main/java/projects/medicationtracker/Helpers/NativeDbHelper.java @@ -79,12 +79,12 @@ public boolean dbExport(String exportPath, String[] ignoredTables) { /** * Imports a database - * @param importPath Where to find import file + * @param fileContents Contents of import file * @param ignoredTables Tables to ignore while importing * @return true if import succeeded */ - public boolean dbImport(String importPath, String[] ignoredTables) { - return dbImporter(dbPath, importPath, ignoredTables); + public boolean dbImport(String fileContents, String[] ignoredTables) { + return dbImporter(dbPath, fileContents, ignoredTables); } private native void dbCreate(String dbPath); @@ -93,5 +93,5 @@ public boolean dbImport(String importPath, String[] ignoredTables) { private native boolean update(String dbPath, String table, Pair[] values, Pair[] where); private native long delete(String dbPath, String table, Pair[] values); private native boolean dbExporter(String databaseName, String exportDirectory, String[] ignoredTables); - private native boolean dbImporter(String dbPath, String importPath, String[] ignoredTables); + private native boolean dbImporter(String dbPath, String fileContents, String[] ignoredTables); } diff --git a/app/src/main/java/projects/medicationtracker/Settings.java b/app/src/main/java/projects/medicationtracker/Settings.java index bd7043c0..b9d45480 100644 --- a/app/src/main/java/projects/medicationtracker/Settings.java +++ b/app/src/main/java/projects/medicationtracker/Settings.java @@ -9,17 +9,16 @@ import static projects.medicationtracker.MainActivity.preferences; import android.Manifest; +import android.content.ContentResolver; import android.content.Intent; import android.content.pm.PackageManager; -import android.database.Cursor; import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.os.Environment; -import android.provider.OpenableColumns; import android.text.Editable; import android.text.TextWatcher; +import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.ArrayAdapter; @@ -37,9 +36,10 @@ import com.google.android.material.textfield.MaterialAutoCompleteTextView; -import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; +import java.io.DataInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Objects; @@ -95,39 +95,64 @@ protected void onCreate(Bundle savedInstanceState) { chooseFileLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { - Uri data = result.getData().getData(); - - if (data != null && data.getPath() != null) { - String absPath = ""; - String name; - Cursor cursor = getContentResolver().query(data, null, null, null, null); - - if (cursor != null && cursor.getCount() > 0) { - cursor.moveToFirst(); - - name = cursor.getString(cursor.getColumnIndexOrThrow(OpenableColumns.DISPLAY_NAME)); - } else { - return; - } - - switch (Objects.requireNonNull(data.getAuthority())) { - case "com.android.providers.downloads.documents": - absPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath() + "/" + name; - break; - case "com.android.externalstorage.documents": - absPath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS).getPath() + "/" + name; - break; - } - - if (nativeDb.dbImport(absPath, new String[]{DBHelper.ANDROID_METADATA, DBHelper.SETTINGS_TABLE})) { - Toast.makeText(this, getString(R.string.import_success), Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(this, getString(R.string.failed_import), Toast.LENGTH_SHORT).show(); + Uri uri = result.getData().getData(); + + if (uri != null && uri.getPath() != null) { + ContentResolver contentResolver = getContentResolver(); + InputStream inputStream; + int size; + String contents; + boolean success = false; + int length; + byte[] bytes; + + try { + try { + inputStream = contentResolver.openInputStream(uri); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + + try { + size = inputStream.available(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + DataInputStream dis = new DataInputStream(inputStream); + bytes = new byte[size]; + + try { + length = dis.read(bytes, 0, size); + } catch (IOException e) { + throw new RuntimeException(e); + } + + inputStream.markSupported(); + + contents = new String(bytes, 0, length); + + try { + inputStream.close(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + success = nativeDb.dbImport(contents, new String[]{DBHelper.ANDROID_METADATA, DBHelper.SETTINGS_TABLE}); + } catch (Exception e) { + Log.e("Import Error", "Error occurred when reading file"); + } finally { + if (success) { + Toast.makeText(this, getString(R.string.import_success), Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(this, getString(R.string.failed_import), Toast.LENGTH_SHORT).show(); + } } } else { Toast.makeText(this, getString(R.string.could_not_retrieve_file), Toast.LENGTH_SHORT).show(); } - }); + } + ); } /** @@ -400,7 +425,7 @@ private ArrayAdapter createTimeFormatMenuAdapter() { * Listener for export data button */ public void onExportClick(View view) { - if (Build.VERSION.SDK_INT <= 32 && checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissionRequester.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE); } @@ -411,11 +436,10 @@ public void onExportClick(View view) { public void onImportClick(View view) { String type= Build.VERSION.SDK_INT >= 30 ? "application/json" : "*/*"; Intent i = new Intent(Intent.ACTION_OPEN_DOCUMENT); - i.addCategory(Intent.CATEGORY_OPENABLE); i.setType(type); - if (Build.VERSION.SDK_INT <= 32 && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { permissionRequester.launch(Manifest.permission.READ_EXTERNAL_STORAGE); } From c4872741dfefa8ca8fd2419626164a6c30d03781 Mon Sep 17 00:00:00 2001 From: Adam Guidarini <45023561+AdamGuidarini@users.noreply.github.com> Date: Sun, 3 Mar 2024 02:19:00 -0500 Subject: [PATCH 10/13] Replaced spinners with auto complete text views --- .../medicationtracker/MainActivity.java | 39 +++++++++--------- .../medicationtracker/MyMedications.java | 41 ++++++++++++------- app/src/main/res/layout/activity_main.xml | 16 ++++++-- .../res/layout/activity_my_medications.xml | 17 ++++++-- 4 files changed, 71 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/projects/medicationtracker/MainActivity.java b/app/src/main/java/projects/medicationtracker/MainActivity.java index 31879686..207c5802 100644 --- a/app/src/main/java/projects/medicationtracker/MainActivity.java +++ b/app/src/main/java/projects/medicationtracker/MainActivity.java @@ -16,15 +16,15 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.util.Pair; import android.view.Menu; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.ScrollView; -import android.widget.Spinner; import android.widget.TextView; import androidx.activity.result.ActivityResultLauncher; @@ -33,6 +33,8 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.fragment.app.FragmentContainerView; +import com.google.android.material.textfield.MaterialAutoCompleteTextView; + import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -43,7 +45,6 @@ import projects.medicationtracker.Dialogs.WelcomeDialog; import projects.medicationtracker.Fragments.MedicationScheduleFragment; import projects.medicationtracker.Helpers.DBHelper; -import projects.medicationtracker.Helpers.NativeDbHelper; import projects.medicationtracker.Helpers.NotificationHelper; import projects.medicationtracker.Helpers.TimeFormatting; import projects.medicationtracker.SimpleClasses.Medication; @@ -53,7 +54,6 @@ public class MainActivity extends AppCompatActivity { public static String dbDir; public static Bundle preferences; private final DBHelper db = new DBHelper(this); - private NativeDbHelper nativeDb; private LinearLayout scheduleLayout; private LocalDate aDayThisWeek; private final ActivityResultLauncher notificationPermissionLauncher = registerForActivityResult( @@ -73,8 +73,6 @@ protected void onCreate(Bundle savedInstanceState) { dbDir = getDatabasePath(DBHelper.DATABASE_NAME).getAbsolutePath(); - nativeDb = new NativeDbHelper(dbDir); - preferences = db.getPreferences(); String theme = preferences.getString(THEME); @@ -166,7 +164,7 @@ public void onSettingsClick(MenuItem item) { public void createMainActivityViews() { TextView noMeds = findViewById(R.id.noMeds); ScrollView scheduleScrollView = findViewById(R.id.scheduleScrollView); - Spinner patientNames = findViewById(R.id.patientSpinner); + MaterialAutoCompleteTextView patientNames = findViewById(R.id.patientSpinner); final String you = getString(R.string.you); // Exit if there are no patients in DB @@ -199,31 +197,32 @@ public void createMainActivityViews() { ); patientNames.setAdapter(patientAdapter); - // Select "You" by default - if (names.contains(you)) { - for (int i = 0; i < names.size(); i++) { - if (names.get(i).equals(you)) - patientNames.setSelection(i); - } - } + patientNames.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} - patientNames.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + public void afterTextChanged(Editable s) { scheduleLayout.removeAllViews(); - String name = adapterView.getSelectedItem().toString(); + String name = s.toString(); if (name.equals(you)) name = "ME!"; createMedicationSchedule(medications, name); + patientNames.clearFocus(); } + }); - @Override - public void onNothingSelected(AdapterView adapterView) { + // Select "You" by default + if (names.contains(you)) { + for (int i = 0; i < names.size(); i++) { + if (names.get(i).equals(you)) { + patientNames.setText(names.get(i), false); + } } - }); + } } } diff --git a/app/src/main/java/projects/medicationtracker/MyMedications.java b/app/src/main/java/projects/medicationtracker/MyMedications.java index c9c4f86c..4d447257 100644 --- a/app/src/main/java/projects/medicationtracker/MyMedications.java +++ b/app/src/main/java/projects/medicationtracker/MyMedications.java @@ -2,19 +2,21 @@ import android.content.Intent; import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.LinearLayout; import android.widget.ScrollView; -import android.widget.Spinner; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentContainerView; +import com.google.android.material.textfield.MaterialAutoCompleteTextView; + import java.util.ArrayList; import java.util.Arrays; import java.util.Objects; @@ -53,7 +55,7 @@ protected void onCreate(Bundle savedInstanceState) { scrollMyMeds.setVisibility(View.VISIBLE); } - final Spinner nameSpinner = findViewById(R.id.nameSpinner); + final MaterialAutoCompleteTextView namesSelector = findViewById(R.id.nameSpinner); final LinearLayout myMedsLayout = findViewById(R.id.medLayout); ArrayList patientNames = db.getPatients(); @@ -78,7 +80,9 @@ protected void onCreate(Bundle savedInstanceState) { createMyMedCards(medication, myMedsLayout); } } else if (allMeds.size() > 1) { - String[] patients = allMeds.stream().map(Pair::getFirst).map(p -> Objects.equals(p, "ME!") ? getString(R.string.you) : p).toArray(String[]::new); + String[] patients = allMeds.stream().map(Pair::getFirst).map(p -> + Objects.equals(p, "ME!") ? getString(R.string.you) : p).toArray(String[]::new + ); if (allMeds.stream().allMatch(m -> m.getFirst().equals("ME!"))) { allMeds = allMeds.stream().map(m -> { @@ -91,20 +95,26 @@ protected void onCreate(Bundle savedInstanceState) { } ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, patients); - nameSpinner.setAdapter(adapter); + namesSelector.setAdapter(adapter); + - nameSpinner.setVisibility(View.VISIBLE); + namesSelector.setVisibility(View.VISIBLE); - if (Arrays.asList(patients).contains(you)) - nameSpinner.setSelection(adapter.getPosition(you)); + if (Arrays.asList(patients).contains(you)) { + namesSelector.setSelection(adapter.getPosition(you)); + } final ArrayList>> allMedsClone = (ArrayList>>) allMeds.clone(); - nameSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + namesSelector.addTextChangedListener(new TextWatcher() { + @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} + @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} + @Override - public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + public void afterTextChanged(Editable s) { myMedsLayout.removeAllViews(); - String selected = adapterView.getSelectedItem().toString(); + String selected = s.toString(); final String patient = selected.equals(you) ? "ME!" : selected; ArrayList patientMeds = allMedsClone.stream().filter( @@ -116,12 +126,12 @@ public void onItemSelected(AdapterView adapterView, View view, int i, long l) createMyMedCards(medication, myMedsLayout); } - } - @Override - public void onNothingSelected(AdapterView adapterView) { + namesSelector.clearFocus(); } }); + + namesSelector.setText(adapter.getItem(0).toString(), false); } } @@ -170,7 +180,8 @@ private void createMyMedCards(Medication medication, LinearLayout baseLayout) { bundle.putParcelable("Medication", medication); - getSupportFragmentManager().beginTransaction() + getSupportFragmentManager() + .beginTransaction() .setReorderingAllowed(true) .add((int) medication.getId(), MyMedicationsFragment.class, bundle) .commit(); diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5c8b8b80..4d44cb07 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -24,10 +24,20 @@ android:layout_height="match_parent" android:orientation="vertical"> - + android:layout_height="wrap_content" + android:layout_marginStart="10dp" + android:layout_marginEnd="10dp" + android:layout_marginBottom="15dp" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu"> + + + - + android:layout_height="wrap_content" + android:layout_marginStart="10dp" + android:layout_marginEnd="10dp" + android:layout_marginBottom="15dp" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense.ExposedDropdownMenu"> + + + Date: Sun, 3 Mar 2024 22:12:27 -0500 Subject: [PATCH 11/13] TextInputLayout fixes in MainActiity and MyMedications --- .../medicationtracker/MainActivity.java | 27 ++++++++++++------- .../medicationtracker/MyMedications.java | 15 ++++++----- app/src/main/res/layout/activity_main.xml | 1 + .../res/layout/activity_my_medications.xml | 2 ++ app/src/main/res/layout/activity_settings.xml | 16 +++++++++++ 5 files changed, 44 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/projects/medicationtracker/MainActivity.java b/app/src/main/java/projects/medicationtracker/MainActivity.java index 207c5802..b4976a4a 100644 --- a/app/src/main/java/projects/medicationtracker/MainActivity.java +++ b/app/src/main/java/projects/medicationtracker/MainActivity.java @@ -34,6 +34,7 @@ import androidx.fragment.app.FragmentContainerView; import com.google.android.material.textfield.MaterialAutoCompleteTextView; +import com.google.android.material.textfield.TextInputLayout; import java.time.LocalDate; import java.time.LocalDateTime; @@ -45,6 +46,7 @@ import projects.medicationtracker.Dialogs.WelcomeDialog; import projects.medicationtracker.Fragments.MedicationScheduleFragment; import projects.medicationtracker.Helpers.DBHelper; +import projects.medicationtracker.Helpers.NativeDbHelper; import projects.medicationtracker.Helpers.NotificationHelper; import projects.medicationtracker.Helpers.TimeFormatting; import projects.medicationtracker.SimpleClasses.Medication; @@ -56,6 +58,8 @@ public class MainActivity extends AppCompatActivity { private final DBHelper db = new DBHelper(this); private LinearLayout scheduleLayout; private LocalDate aDayThisWeek; + private NativeDbHelper nativeDb; + private TextInputLayout namesLayout; private final ActivityResultLauncher notificationPermissionLauncher = registerForActivityResult( new ActivityResultContracts.RequestPermission(), isGranted -> db.seenPermissionRequest(SEEN_NOTIFICATION_REQUEST) @@ -73,6 +77,8 @@ protected void onCreate(Bundle savedInstanceState) { dbDir = getDatabasePath(DBHelper.DATABASE_NAME).getAbsolutePath(); + nativeDb = new NativeDbHelper(dbDir); + preferences = db.getPreferences(); String theme = preferences.getString(THEME); @@ -106,6 +112,8 @@ protected void onCreate(Bundle savedInstanceState) { notificationPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS); } + namesLayout = findViewById(R.id.names_layout_main); + createMainActivityViews(); } @@ -171,7 +179,7 @@ public void createMainActivityViews() { if (db.numberOfRows() == 0) { noMeds.setVisibility(View.VISIBLE); scheduleScrollView.setVisibility(View.GONE); - patientNames.setVisibility(View.GONE); + namesLayout.setVisibility(View.GONE); this.findViewById(R.id.navButtonLayout).setVisibility(View.GONE); return; } @@ -181,14 +189,15 @@ public void createMainActivityViews() { // Load contents into spinner, or print results for only patient if (db.getPatients().size() == 1) { - patientNames.setVisibility(View.GONE); + namesLayout.setVisibility(View.GONE); createMedicationSchedule(medications, names.get(0)); } else { patientNames.setVisibility(View.VISIBLE); - if (names.contains("ME!")) + if (names.contains("ME!")) { names.set(names.indexOf("ME!"), you); + } ArrayAdapter patientAdapter = new ArrayAdapter<>( this, @@ -207,21 +216,19 @@ public void afterTextChanged(Editable s) { String name = s.toString(); - if (name.equals(you)) + if (name.equals(you)) { name = "ME!"; + } createMedicationSchedule(medications, name); patientNames.clearFocus(); } }); - // Select "You" by default if (names.contains(you)) { - for (int i = 0; i < names.size(); i++) { - if (names.get(i).equals(you)) { - patientNames.setText(names.get(i), false); - } - } + patientNames.setText(you, false); + } else { + patientNames.setText(names.get((0)), false); } } } diff --git a/app/src/main/java/projects/medicationtracker/MyMedications.java b/app/src/main/java/projects/medicationtracker/MyMedications.java index 4d447257..21b37f75 100644 --- a/app/src/main/java/projects/medicationtracker/MyMedications.java +++ b/app/src/main/java/projects/medicationtracker/MyMedications.java @@ -16,6 +16,7 @@ import androidx.fragment.app.FragmentContainerView; import com.google.android.material.textfield.MaterialAutoCompleteTextView; +import com.google.android.material.textfield.TextInputLayout; import java.util.ArrayList; import java.util.Arrays; @@ -55,6 +56,7 @@ protected void onCreate(Bundle savedInstanceState) { scrollMyMeds.setVisibility(View.VISIBLE); } + final TextInputLayout namesLayout = findViewById(R.id.names_layout); final MaterialAutoCompleteTextView namesSelector = findViewById(R.id.nameSpinner); final LinearLayout myMedsLayout = findViewById(R.id.medLayout); @@ -97,12 +99,7 @@ protected void onCreate(Bundle savedInstanceState) { ArrayAdapter adapter = new ArrayAdapter<>(this, android.R.layout.simple_dropdown_item_1line, patients); namesSelector.setAdapter(adapter); - - namesSelector.setVisibility(View.VISIBLE); - - if (Arrays.asList(patients).contains(you)) { - namesSelector.setSelection(adapter.getPosition(you)); - } + namesLayout.setVisibility(View.VISIBLE); final ArrayList>> allMedsClone = (ArrayList>>) allMeds.clone(); @@ -131,7 +128,11 @@ public void afterTextChanged(Editable s) { } }); - namesSelector.setText(adapter.getItem(0).toString(), false); + if (Arrays.asList(patients).contains(you)) { + namesSelector.setText(you, false); + } else { + namesSelector.setText(adapter.getItem(0).toString(), false); + } } } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4d44cb07..cb15109a 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -25,6 +25,7 @@ android:orientation="vertical"> + + + + +