From a8b6ba08a5bee16b4130b5fe9961565211a0272d Mon Sep 17 00:00:00 2001 From: Niklas Merkelt Date: Thu, 9 Jan 2025 10:34:20 +0100 Subject: [PATCH] Version 1.9.0 - Rework of the ui - Preparation for room database upgrades - Preparation for lunch data auto import Signed-off-by: Niklas Merkelt --- .idea/deploymentTargetSelector.xml | 3 + .idea/misc.xml | 15 +- .idea/other.xml | 340 ++++++++++++++++++ .idea/runConfigurations.xml | 17 + app/build.gradle | 25 +- .../1.json | 90 +++++ .../2.json | 116 ++++++ app/src/main/AndroidManifest.xml | 17 +- .../activity/DisplayActivity.java | 14 +- .../kitchenscanner/activity/HelpActivity.java | 14 + .../activity/ImportExportActivity.java | 231 ++++++++++++ .../kitchenscanner/activity/MainActivity.java | 72 ---- .../activity/MainBottomNavActivity.java | 58 +++ .../activity/SettingsActivity.java | 130 +------ .../activity/StatsActivity.java | 42 --- .../ui/home/HomeFragment.java} | 62 +--- .../ui/lunch_list/LunchListFragment.java | 37 ++ .../notifications/ImportExportFragment.java | 41 +++ .../activity/ui/search/SearchFragment.java | 29 ++ .../database/LunchDatabase.java | 5 +- .../kitchenscanner/database/MasterData.java | 15 + .../database/MasterDataDao.java | 22 ++ .../fragment/StatsListFragment.java | 76 ---- .../kitchenscanner/task/CsvImportTask.java | 114 ------ .../task/ProgressAsyncTask.java | 26 -- .../task/import_export/AllergyImportTask.java | 64 ++++ .../{ => import_export}/LunchExportTask.java | 17 +- .../task/import_export/LunchImportTask.java | 144 ++++++++ .../kitchenscanner/util/LunchListAdapter.java | 56 ++- .../res/drawable/ic_dashboard_black_24dp.xml | 9 + .../main/res/drawable/ic_help_white_24dp.xml | 9 + .../main/res/drawable/ic_home_black_24dp.xml | 9 + .../main/res/drawable/ic_list_black_24dp.xml | 9 + .../drawable/ic_notifications_black_24dp.xml | 9 + .../res/drawable/ic_search_black_24dp.xml | 9 + .../res/drawable/ic_swap_horiz_black_24dp.xml | 9 + .../main/res/layout/activity_bottom_nav.xml | 30 ++ .../{activity_main.xml => activity_help.xml} | 15 +- .../res/layout/activity_import_export.xml | 104 ++++++ app/src/main/res/layout/activity_stats.xml | 13 - ...t_stats_overview.xml => fragment_home.xml} | 23 +- .../res/layout/fragment_import_export.xml | 76 ++++ ...stats_list.xml => fragment_lunch_list.xml} | 10 +- app/src/main/res/layout/fragment_search.xml | 22 ++ app/src/main/res/menu/bottom_nav_menu.xml | 25 ++ app/src/main/res/menu/menu_main.xml | 10 +- app/src/main/res/menu/menu_settings.xml | 15 - app/src/main/res/menu/menu_stats_chart.xml | 12 - app/src/main/res/menu/menu_stats_list.xml | 12 - .../main/res/navigation/mobile_navigation.xml | 31 ++ app/src/main/res/values-de/strings.xml | 46 ++- app/src/main/res/values/colors.xml | 4 +- app/src/main/res/values/strings.xml | 39 +- app/src/main/res/xml/pref_kitchen_scanner.xml | 4 + build.gradle | 4 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 56 files changed, 1807 insertions(+), 645 deletions(-) create mode 100644 .idea/other.xml create mode 100644 .idea/runConfigurations.xml create mode 100644 app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/1.json create mode 100644 app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/2.json create mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/HelpActivity.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/ImportExportActivity.java delete mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/MainActivity.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/MainBottomNavActivity.java delete mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/StatsActivity.java rename app/src/main/java/de/schkola/kitchenscanner/{fragment/StatsChartFragment.java => activity/ui/home/HomeFragment.java} (51%) create mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/ui/lunch_list/LunchListFragment.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/ui/notifications/ImportExportFragment.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/activity/ui/search/SearchFragment.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/database/MasterData.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/database/MasterDataDao.java delete mode 100644 app/src/main/java/de/schkola/kitchenscanner/fragment/StatsListFragment.java delete mode 100644 app/src/main/java/de/schkola/kitchenscanner/task/CsvImportTask.java delete mode 100644 app/src/main/java/de/schkola/kitchenscanner/task/ProgressAsyncTask.java create mode 100644 app/src/main/java/de/schkola/kitchenscanner/task/import_export/AllergyImportTask.java rename app/src/main/java/de/schkola/kitchenscanner/task/{ => import_export}/LunchExportTask.java (73%) create mode 100644 app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchImportTask.java create mode 100644 app/src/main/res/drawable/ic_dashboard_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_help_white_24dp.xml create mode 100644 app/src/main/res/drawable/ic_home_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_list_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_notifications_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_search_black_24dp.xml create mode 100644 app/src/main/res/drawable/ic_swap_horiz_black_24dp.xml create mode 100644 app/src/main/res/layout/activity_bottom_nav.xml rename app/src/main/res/layout/{activity_main.xml => activity_help.xml} (69%) create mode 100644 app/src/main/res/layout/activity_import_export.xml delete mode 100644 app/src/main/res/layout/activity_stats.xml rename app/src/main/res/layout/{content_stats_overview.xml => fragment_home.xml} (88%) create mode 100644 app/src/main/res/layout/fragment_import_export.xml rename app/src/main/res/layout/{content_stats_list.xml => fragment_lunch_list.xml} (85%) create mode 100644 app/src/main/res/layout/fragment_search.xml create mode 100644 app/src/main/res/menu/bottom_nav_menu.xml delete mode 100644 app/src/main/res/menu/menu_stats_chart.xml delete mode 100644 app/src/main/res/menu/menu_stats_list.xml create mode 100644 app/src/main/res/navigation/mobile_navigation.xml diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..338890f 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -5,6 +5,9 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 8a5381f..1fde962 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -16,7 +16,7 @@ @@ -60,4 +64,11 @@ + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..b45a6e0 --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,340 @@ + + + + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml new file mode 100644 index 0000000..16660f1 --- /dev/null +++ b/.idea/runConfigurations.xml @@ -0,0 +1,17 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index b3e998a..98b6f7f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'androidx.room' android { @@ -8,27 +9,39 @@ android { applicationId "de.schkola.kitchenscanner" minSdkVersion 24 targetSdkVersion 34 - versionCode 1800 - versionName "1.8.0" + versionCode 1900 + versionName "1.9.0" } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } namespace 'de.schkola.kitchenscanner' + buildFeatures { + viewBinding true + } + room { + schemaDirectory "$projectDir/schemas" + } } dependencies { - implementation 'org.jetbrains:annotations:24.0.1' + implementation 'org.jetbrains:annotations:26.0.1' + implementation 'androidx.constraintlayout:constraintlayout:2.2.0' + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.7' + implementation 'androidx.navigation:navigation-fragment:2.8.5' + implementation 'androidx.navigation:navigation-ui:2.8.5' + implementation 'androidx.activity:activity:1.9.3' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7' runtimeOnly fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.fragment:fragment:1.8.5' implementation 'androidx.preference:preference:1.2.1' - implementation 'androidx.room:room-runtime:2.6.1' - annotationProcessor 'androidx.room:room-compiler:2.6.1' + implementation "androidx.room:room-runtime:$room_version" + annotationProcessor "androidx.room:room-compiler:$room_version" implementation 'com.google.android.material:material:1.12.0' implementation 'io.github.g00fy2.quickie:quickie-bundled:1.10.0' - implementation 'org.apache.commons:commons-csv:1.11.0' + implementation 'org.apache.commons:commons-csv:1.12.0' def lifecycle_version = "2.8.7" implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" diff --git a/app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/1.json b/app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/1.json new file mode 100644 index 0000000..dbc5bd7 --- /dev/null +++ b/app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/1.json @@ -0,0 +1,90 @@ +{ + "formatVersion": 1, + "database": { + "version": 1, + "identityHash": "e86d491c82440c2f55e948ed0b642905", + "entities": [ + { + "tableName": "customers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`xba` INTEGER NOT NULL, `name` TEXT, `grade` TEXT, `lunch` INTEGER NOT NULL, `gotLunch` INTEGER NOT NULL, PRIMARY KEY(`xba`))", + "fields": [ + { + "fieldPath": "xba", + "columnName": "xba", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lunch", + "columnName": "lunch", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gotLunch", + "columnName": "gotLunch", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "xba" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "allergies", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`allergyId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `allergy` TEXT, `xba` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "allergyId", + "columnName": "allergyId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "allergy", + "columnName": "allergy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "xba", + "columnName": "xba", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "allergyId" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e86d491c82440c2f55e948ed0b642905')" + ] + } +} \ No newline at end of file diff --git a/app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/2.json b/app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/2.json new file mode 100644 index 0000000..9d85aa4 --- /dev/null +++ b/app/schemas/de.schkola.kitchenscanner.database.LunchDatabase/2.json @@ -0,0 +1,116 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "a781fc58faae154032387af6bb4353de", + "entities": [ + { + "tableName": "customers", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`xba` INTEGER NOT NULL, `name` TEXT, `grade` TEXT, `lunch` INTEGER NOT NULL, `gotLunch` INTEGER NOT NULL, PRIMARY KEY(`xba`))", + "fields": [ + { + "fieldPath": "xba", + "columnName": "xba", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "grade", + "columnName": "grade", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "lunch", + "columnName": "lunch", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "gotLunch", + "columnName": "gotLunch", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "xba" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "allergies", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`allergyId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `allergy` TEXT, `xba` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "allergyId", + "columnName": "allergyId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "allergy", + "columnName": "allergy", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "xba", + "columnName": "xba", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "allergyId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "master_data", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "key" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a781fc58faae154032387af6bb4353de')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a2ed250..e5fa722 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -10,6 +10,7 @@ @@ -28,15 +29,19 @@ + android:parentActivityName=".activity.MainBottomNavActivity" /> + android:parentActivityName=".activity.MainBottomNavActivity" /> + android:name=".activity.HelpActivity" + android:label="@string/help_display" + android:parentActivityName=".activity.MainBottomNavActivity" /> + \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/DisplayActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/DisplayActivity.java index 97a9479..c91ab9e 100644 --- a/app/src/main/java/de/schkola/kitchenscanner/activity/DisplayActivity.java +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/DisplayActivity.java @@ -155,6 +155,14 @@ private int getRescanTime() { return 2; } + private boolean shouldPlaySuccessSound() { + SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this); + if (pref != null) { + return pref.getBoolean("success_sound", true); + } + return true; + } + /** * Starts barcode scan */ @@ -208,8 +216,10 @@ private void processResult(@NotNull String rawValue) { }, lunchResult -> { torchManager.enable(); fillInformation(lunchResult); - mediaPlayer.seekTo(0); - mediaPlayer.start(); + if (shouldPlaySuccessSound()) { + mediaPlayer.seekTo(0); + mediaPlayer.start(); + } if (isRescanEnabled()) { executorService.schedule(this::startScan, getRescanTime(), TimeUnit.SECONDS); } diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/HelpActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/HelpActivity.java new file mode 100644 index 0000000..8f18968 --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/HelpActivity.java @@ -0,0 +1,14 @@ +package de.schkola.kitchenscanner.activity; + +import android.os.Bundle; +import androidx.appcompat.app.AppCompatActivity; +import de.schkola.kitchenscanner.R; + +public class HelpActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_help); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/ImportExportActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/ImportExportActivity.java new file mode 100644 index 0000000..2bcf1c8 --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/ImportExportActivity.java @@ -0,0 +1,231 @@ +package de.schkola.kitchenscanner.activity; + +import android.app.AlertDialog; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.EditText; +import android.widget.TextView; +import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.appcompat.app.AppCompatActivity; +import de.schkola.kitchenscanner.R; +import de.schkola.kitchenscanner.database.DatabaseAccess; +import de.schkola.kitchenscanner.task.TaskRunner; +import de.schkola.kitchenscanner.task.import_export.AllergyImportTask; +import de.schkola.kitchenscanner.task.import_export.LunchExportTask; +import de.schkola.kitchenscanner.task.import_export.LunchImportTask; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; +import java.util.stream.Collectors; + +public class ImportExportActivity extends AppCompatActivity { + + public static final String EXTRA_TYPE = "TYPE"; + public static final String TYPE_LUNCH_IMPORT = "LUNCH_IMPORT"; + public static final String TYPE_ALLERGY_IMPORT = "ALLERGY_IMPORT"; + public static final String TYPE_LUNCH_EXPORT = "LUNCH_EXPORT"; + + private static final String[] mimeTypes = {"text/csv", "text/comma-separated-values", "application/octet-stream"}; + + private final ActivityResultLauncher getFileLunchExport = registerForActivityResult(new ActivityResultContracts.CreateDocument("text/csv"), this::handleSetUri); + private final ActivityResultLauncher getFileLunchImport = registerForActivityResult(new ActivityResultContracts.OpenDocument(), this::handleSetUri); + private final ActivityResultLauncher getFileAllergyImport = registerForActivityResult(new ActivityResultContracts.OpenDocument(), this::handleSetUri); + + private DatabaseAccess dbAccess; + private String importExportType; + private Uri uri; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_import_export); + + dbAccess = new DatabaseAccess(getApplicationContext()); + + Intent intent = getIntent(); + if (intent != null) { + TextView display = findViewById(R.id.importExportTypeDisplay); + importExportType = intent.getStringExtra(EXTRA_TYPE); + + switch (importExportType != null ? importExportType : "") { + case TYPE_LUNCH_IMPORT: + display.setText(R.string.import_lunch_data); + break; + case TYPE_ALLERGY_IMPORT: + display.setText(R.string.import_allergy_data); + break; + case TYPE_LUNCH_EXPORT: + display.setText(R.string.export_lunch_data); + break; + default: + new AlertDialog.Builder(this) + .setTitle(R.string.error) + .setMessage(R.string.error_parameter) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + break; + } + } + + Button fileChooseButton = findViewById(R.id.fileChooseButton); + fileChooseButton.setOnClickListener(this::fileChooseButtonClick); + EditText fileEditText = findViewById(R.id.fileEditText); + fileEditText.setOnClickListener(this::fileChooseButtonClick); + + Button startActionButton = findViewById(R.id.startActionButton); + startActionButton.setOnClickListener(this::startActionButtonClick); + } + + private void fileChooseButtonClick(View view) { + EditText fileEditText = findViewById(R.id.fileEditText); + fileEditText.setText(""); + this.uri = null; + + switch (importExportType != null ? importExportType : "") { + case TYPE_LUNCH_IMPORT: + Toast.makeText(this, R.string.choose_day, Toast.LENGTH_LONG).show(); + getFileLunchImport.launch(mimeTypes); + break; + case TYPE_ALLERGY_IMPORT: + Toast.makeText(this, R.string.choose_allergy, Toast.LENGTH_LONG).show(); + getFileAllergyImport.launch(mimeTypes); + break; + case TYPE_LUNCH_EXPORT: + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); + Date date = new Date(System.currentTimeMillis()); + getFileLunchExport.launch(String.format("export-lunch-%s.csv", formatter.format(date))); + break; + default: + new AlertDialog.Builder(this) + .setTitle(R.string.error) + .setMessage(R.string.error_parameter) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + break; + } + } + + private void startActionButtonClick(View view) { + if (this.uri == null) { + return; + } + TextView importExportLoadingText = findViewById(R.id.importExportLoadingText); + switch (importExportType != null ? importExportType : "") { + case TYPE_LUNCH_IMPORT: + importExportLoadingText.setText(R.string.import_ongoing); + findViewById(R.id.importExportLoadingScreen).setVisibility(View.VISIBLE); + startLunchImport(this.uri); + break; + case TYPE_ALLERGY_IMPORT: + importExportLoadingText.setText(R.string.import_ongoing); + findViewById(R.id.importExportLoadingScreen).setVisibility(View.VISIBLE); + startAllergyImport(this.uri); + break; + case TYPE_LUNCH_EXPORT: + importExportLoadingText.setText(R.string.export_ongoing); + findViewById(R.id.importExportLoadingScreen).setVisibility(View.VISIBLE); + startLunchExport(this.uri); + break; + default: + new AlertDialog.Builder(this) + .setTitle(R.string.error) + .setMessage(R.string.error_parameter) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + break; + } + } + + private void handleSetUri(Uri uri) { + EditText fileEditText = findViewById(R.id.fileEditText); + this.uri = uri; + if (uri == null) { + fileEditText.setText(""); + } else { + fileEditText.setText(uri.getLastPathSegment()); + } + } + + private void startLunchImport(Uri uri) { + try { + InputStream inputStream = getContentResolver().openInputStream(uri); + LunchImportTask lunchImportTask = new LunchImportTask(dbAccess.getDatabase(), inputStream, duplicates -> { + findViewById(R.id.importExportLoadingScreen).setVisibility(View.GONE); + if (duplicates.isEmpty()) { + new AlertDialog.Builder(this) + .setTitle(R.string.success) + .setMessage(R.string.import_successful) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + } else { + new AlertDialog.Builder(this) + .setTitle(R.string.duplicate_entries) + .setItems(duplicates.stream().map(duplicate -> String.format(Locale.GERMANY, "[%d] %s {%s}", duplicate.getXba(), duplicate.getName(), duplicate.getLunches().stream().map(Object::toString).collect(Collectors.joining(",")))).toArray(String[]::new), null) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + } + }); + TaskRunner.INSTANCE.executeAsyncTask(lunchImportTask); + } catch (FileNotFoundException e) { + new AlertDialog.Builder(this) + .setTitle(R.string.error) + .setMessage(R.string.error_file_not_found) + .setPositiveButton(android.R.string.ok, null) + .create().show(); + Log.e(ImportExportActivity.class.getName(), "File not found (lunch import)", e); + } + } + + private void startAllergyImport(Uri uri) { + try { + InputStream inputStream = getContentResolver().openInputStream(uri); + AllergyImportTask allergyImportTask = new AllergyImportTask(dbAccess.getDatabase(), inputStream, () -> { + findViewById(R.id.importExportLoadingScreen).setVisibility(View.GONE); + new AlertDialog.Builder(this) + .setTitle(R.string.success) + .setMessage(R.string.import_successful) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + }); + TaskRunner.INSTANCE.executeAsyncTask(allergyImportTask); + } catch (FileNotFoundException e) { + new AlertDialog.Builder(this) + .setTitle(R.string.error) + .setMessage(R.string.error_file_not_found) + .setPositiveButton(android.R.string.ok, null) + .create().show(); + Log.e(ImportExportActivity.class.getName(), "File not found (allergy import)", e); + } + } + + private void startLunchExport(Uri uri) { + try { + OutputStream outputStream = getContentResolver().openOutputStream(uri); + LunchExportTask lunchExportTask = new LunchExportTask(dbAccess.getDatabase(), outputStream, () -> { + findViewById(R.id.importExportLoadingScreen).setVisibility(View.GONE); + new AlertDialog.Builder(this) + .setTitle(R.string.success) + .setMessage(R.string.export_successful) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> finish()) + .create().show(); + }); + TaskRunner.INSTANCE.executeAsyncTask(lunchExportTask); + } catch (FileNotFoundException e) { + new AlertDialog.Builder(this) + .setTitle(R.string.error) + .setMessage(R.string.error_file_not_found) + .setPositiveButton(android.R.string.ok, null) + .create().show(); + Log.e(ImportExportActivity.class.getName(), "File not found (lunch export)", e); + } + } +} diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/MainActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/MainActivity.java deleted file mode 100644 index 5253b72..0000000 --- a/app/src/main/java/de/schkola/kitchenscanner/activity/MainActivity.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * MIT License - * - * Copyright 2016 Niklas Merkelt - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package de.schkola.kitchenscanner.activity; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuItem; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AppCompatActivity; -import com.google.android.material.floatingactionbutton.FloatingActionButton; -import de.schkola.kitchenscanner.R; - -public class MainActivity extends AppCompatActivity { - - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - //Set Content - setContentView(R.layout.activity_main); - - //Set Action Buttons - FloatingActionButton fab = findViewById(R.id.fab); - fab.setOnClickListener(view -> { - Intent intent = new Intent(this, DisplayActivity.class); - intent.setAction(Intent.ACTION_RUN); - startActivity(intent); - }); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_main, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.action_settings) { - //Start settings - startActivity(new Intent(this, SettingsActivity.class)); - return true; - } else if (item.getItemId() == R.id.action_stats) { - startActivity(new Intent(this, StatsActivity.class)); - return true; - } - return super.onOptionsItemSelected(item); - } -} diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/MainBottomNavActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/MainBottomNavActivity.java new file mode 100644 index 0000000..5dd3a7d --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/MainBottomNavActivity.java @@ -0,0 +1,58 @@ +package de.schkola.kitchenscanner.activity; + +import android.content.Intent; +import android.os.Bundle; +import android.view.Menu; +import android.view.MenuItem; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AppCompatActivity; +import androidx.navigation.NavController; +import androidx.navigation.Navigation; +import androidx.navigation.ui.AppBarConfiguration; +import androidx.navigation.ui.NavigationUI; +import com.google.android.material.bottomnavigation.BottomNavigationView; +import de.schkola.kitchenscanner.R; +import de.schkola.kitchenscanner.databinding.ActivityBottomNavBinding; + +public class MainBottomNavActivity extends AppCompatActivity { + + private ActivityBottomNavBinding binding; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + binding = ActivityBottomNavBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + BottomNavigationView navView = findViewById(R.id.nav_view); + // Passing each menu ID as a set of Ids because each + // menu should be considered as top level destinations. + AppBarConfiguration appBarConfiguration = new AppBarConfiguration.Builder( + R.id.navigation_home, R.id.navigation_lunch_list, R.id.navigation_search, R.id.navigation_import_export) + .build(); + NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment_activity_bottom_nav); + NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration); + NavigationUI.setupWithNavController(binding.navView, navController); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.menu_main, menu); + return true; + } + + @Override + public boolean onOptionsItemSelected(@NonNull MenuItem item) { + if (item.getItemId() == R.id.action_settings) { + //Start settings activity + startActivity(new Intent(this, SettingsActivity.class)); + return true; + } else if (item.getItemId() == R.id.action_help) { + //Start help activity + startActivity(new Intent(this, HelpActivity.class)); + return true; + } + return super.onOptionsItemSelected(item); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/SettingsActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/SettingsActivity.java index 1a5cad7..ecf6fe3 100644 --- a/app/src/main/java/de/schkola/kitchenscanner/activity/SettingsActivity.java +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/SettingsActivity.java @@ -25,42 +25,24 @@ package de.schkola.kitchenscanner.activity; import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; -import android.widget.Toast; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import de.schkola.kitchenscanner.R; import de.schkola.kitchenscanner.database.DatabaseAccess; -import de.schkola.kitchenscanner.task.CsvImportTask; -import de.schkola.kitchenscanner.task.LunchExportTask; +import de.schkola.kitchenscanner.database.LunchDatabase; import de.schkola.kitchenscanner.task.TaskRunner; -import java.io.FileNotFoundException; -import java.io.InputStream; -import java.io.OutputStream; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; public class SettingsActivity extends AppCompatActivity { - private DatabaseAccess dbAccess; - private final ActivityResultLauncher createDocument = registerForActivityResult(new ActivityResultContracts.CreateDocument("text/csv"), this::handleExport); - private final ActivityResultLauncher getContentDay = registerForActivityResult(new ActivityResultContracts.OpenDocument(), this::handleUriDay); - private final ActivityResultLauncher getContentAllergy = registerForActivityResult(new ActivityResultContracts.OpenDocument(), this::handleUriAllergy); - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Set Content setContentView(R.layout.activity_settings); getSupportFragmentManager().beginTransaction().replace(R.id.content_frame, new SettingsFragment()).commit(); - dbAccess = new DatabaseAccess(getApplicationContext()); } @Override @@ -71,29 +53,7 @@ public boolean onCreateOptionsMenu(Menu menu) { @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { - if (item.getItemId() == R.id.action_CSV_copy) { - //Start copy CSV-Files - new AlertDialog.Builder(this) - .setTitle(R.string.csv_import) - .setMessage(R.string.copy_request_day) - .setPositiveButton(R.string.yes, (dialogInterface, i) -> { - deleteFiles(); - startImport(false); - }) - .setNegativeButton(R.string.no, (dialog, witch) -> startImport(false)) - .setNeutralButton(R.string.cancel, null) - .create().show(); - return true; - } else if (item.getItemId() == R.id.action_CSV_copy_allergy) { - //Start copy CSV-Files - new AlertDialog.Builder(this) - .setTitle(R.string.csv_import) - .setMessage(R.string.copy_request_allergy) - .setPositiveButton(R.string.yes, (dialogInterface, i) -> startImport(true)) - .setNegativeButton(R.string.no, null) - .create().show(); - return true; - } else if (item.getItemId() == R.id.action_delete_data) { + if (item.getItemId() == R.id.action_delete_data) { //Delete Cache new AlertDialog.Builder(this) .setTitle(R.string.delete) @@ -102,90 +62,16 @@ public boolean onOptionsItemSelected(@NonNull MenuItem item) { .setNegativeButton(R.string.no, null) .create().show(); return true; - } else if (item.getItemId() == R.id.action_export_lunch_data) { - //Export Lunch Data to CSV - startExport(); - return true; } return super.onOptionsItemSelected(item); } - private void startImport(boolean allergy) { - int text = allergy ? R.string.choose_allergy : R.string.choose_day; - Toast.makeText(this, text, Toast.LENGTH_LONG).show(); - String[] mimeTypes = {"text/csv", "text/comma-separated-values", "application/octet-stream"}; - if (allergy) { - getContentAllergy.launch(mimeTypes); - } else { - getContentDay.launch(mimeTypes); - } - } - private void deleteFiles() { - TaskRunner.INSTANCE.executeAsync(() -> dbAccess.getDatabase().customerDao().deleteAll()); - } - - private void startExport() { - SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()); - Date date = new Date(System.currentTimeMillis()); - createDocument.launch(String.format("export-lunch-%s.csv", formatter.format(date))); - } - - @NonNull - private LunchExportTask createExportTask(OutputStream outputStream) { - ProgressDialog dialog = new ProgressDialog(this); - dialog.setCancelable(false); - dialog.setTitle(getString(R.string.lunch_export)); - dialog.setMessage(getString(R.string.lunch_export_ongoing)); - LunchExportTask lunchExportTask = new LunchExportTask(dbAccess.getDatabase(), outputStream); - lunchExportTask.setProgressDialog(dialog); - return lunchExportTask; - } - - @NonNull - private CsvImportTask createImportTask(boolean allergy, InputStream inputStream) { - ProgressDialog dialog = new ProgressDialog(this); - dialog.setCancelable(false); - dialog.setTitle(getString(R.string.csv_import)); - dialog.setMessage(getString(R.string.csv_import_ongoing)); - CsvImportTask csvImportTask = new CsvImportTask(dbAccess.getDatabase(), allergy, inputStream); - csvImportTask.setProgressDialog(dialog); - csvImportTask.setCsvImportListener(duplicateXba -> new AlertDialog.Builder(this) - .setTitle(R.string.duplicate_xba) - .setItems(duplicateXba.toArray(new String[0]), null) - .setPositiveButton(android.R.string.ok, null) - .create().show()); - return csvImportTask; - } - - private void handleUriDay(Uri uri) { - handleUri(uri, false); - } - - private void handleUriAllergy(Uri uri) { - handleUri(uri, true); - } - - private void handleUri(Uri data, boolean allergy) { - if (data != null) { - InputStream inputStream; - try { - inputStream = getContentResolver().openInputStream(data); - } catch (FileNotFoundException ignored) { - return; - } - TaskRunner.INSTANCE.executeAsyncTask(createImportTask(allergy, inputStream)); - } - } - - private void handleExport(Uri data) { - if (data != null) { - try { - OutputStream outputStream = getContentResolver().openOutputStream(data); - TaskRunner.INSTANCE.executeAsyncTask(createExportTask(outputStream)); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - } + DatabaseAccess dbAccess = new DatabaseAccess(getApplicationContext()); + TaskRunner.INSTANCE.executeAsync(() -> { + LunchDatabase database = dbAccess.getDatabase(); + database.customerDao().deleteAll(); + database.close(); + }); } } diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/StatsActivity.java b/app/src/main/java/de/schkola/kitchenscanner/activity/StatsActivity.java deleted file mode 100644 index 96a9136..0000000 --- a/app/src/main/java/de/schkola/kitchenscanner/activity/StatsActivity.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * MIT License - * - * Copyright 2017 Niklas Merkelt - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package de.schkola.kitchenscanner.activity; - -import android.os.Bundle; -import androidx.appcompat.app.AppCompatActivity; -import de.schkola.kitchenscanner.R; -import de.schkola.kitchenscanner.fragment.StatsChartFragment; - -public class StatsActivity extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_stats); - getSupportFragmentManager().beginTransaction() - .replace(R.id.container, new StatsChartFragment()) - .commit(); - } -} diff --git a/app/src/main/java/de/schkola/kitchenscanner/fragment/StatsChartFragment.java b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/home/HomeFragment.java similarity index 51% rename from app/src/main/java/de/schkola/kitchenscanner/fragment/StatsChartFragment.java rename to app/src/main/java/de/schkola/kitchenscanner/activity/ui/home/HomeFragment.java index 3cf8178..381f074 100644 --- a/app/src/main/java/de/schkola/kitchenscanner/fragment/StatsChartFragment.java +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/home/HomeFragment.java @@ -1,41 +1,39 @@ -package de.schkola.kitchenscanner.fragment; +package de.schkola.kitchenscanner.activity.ui.home; +import android.content.Intent; import android.os.Bundle; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.core.view.MenuProvider; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; +import com.google.android.material.floatingactionbutton.FloatingActionButton; import de.schkola.kitchenscanner.R; +import de.schkola.kitchenscanner.activity.DisplayActivity; import de.schkola.kitchenscanner.database.DatabaseAccess; import de.schkola.kitchenscanner.database.LunchDatabase; import de.schkola.kitchenscanner.task.StatTask; import de.schkola.kitchenscanner.task.TaskRunner; -public class StatsChartFragment extends Fragment { +public class HomeFragment extends Fragment { - private final OptionsMenuProvider optionsMenuProvider = new OptionsMenuProvider(); - - @Nullable @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - FragmentActivity activity = getActivity(); - if (activity != null) { - activity.addMenuProvider(optionsMenuProvider); - } - return inflater.inflate(R.layout.content_stats_overview, container, false); + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_home, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); + FloatingActionButton fab = view.findViewById(R.id.fab); + fab.setOnClickListener(v -> { + Intent intent = new Intent(getContext(), DisplayActivity.class); + intent.setAction(Intent.ACTION_RUN); + startActivity(intent); + }); LunchDatabase database = new DatabaseAccess(getContext()).getDatabase(); TaskRunner.INSTANCE.executeAsyncTask(new StatTask(database, result -> { database.close(); @@ -48,35 +46,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat ((TextView) view.findViewById(R.id.orderedS)).setText(String.valueOf(result.getLunchS())); ((TextView) view.findViewById(R.id.gotS)).setText(String.valueOf(result.getDispensedS())); ((TextView) view.findViewById(R.id.getS)).setText(String.valueOf(result.getToDispenseS().size())); - view.findViewById(R.id.statsOverviewLoadingScreen).setVisibility(View.GONE); + view.findViewById(R.id.home_loading_screen).setVisibility(View.GONE); })); } - - @Override - public void onDestroyView() { - super.onDestroyView(); - FragmentActivity activity = getActivity(); - if (activity != null) { - activity.removeMenuProvider(optionsMenuProvider); - } - } - - public class OptionsMenuProvider implements MenuProvider { - - @Override - public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { - menuInflater.inflate(R.menu.menu_stats_chart, menu); - } - - @Override - public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { - if (menuItem.getItemId() == R.id.action_list) { - getParentFragmentManager().beginTransaction() - .replace(R.id.container, new StatsListFragment()) - .commit(); - return true; - } - return false; - } - } -} +} \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/ui/lunch_list/LunchListFragment.java b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/lunch_list/LunchListFragment.java new file mode 100644 index 0000000..6f5984e --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/lunch_list/LunchListFragment.java @@ -0,0 +1,37 @@ +package de.schkola.kitchenscanner.activity.ui.lunch_list; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ExpandableListView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import de.schkola.kitchenscanner.R; +import de.schkola.kitchenscanner.database.DatabaseAccess; +import de.schkola.kitchenscanner.database.LunchDatabase; +import de.schkola.kitchenscanner.task.StatTask; +import de.schkola.kitchenscanner.task.TaskRunner; +import de.schkola.kitchenscanner.util.LunchListAdapter; + +public class LunchListFragment extends Fragment { + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_lunch_list, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + LunchDatabase database = new DatabaseAccess(getContext()).getDatabase(); + TaskRunner.INSTANCE.executeAsyncTask(new StatTask(database, result -> { + database.close(); + ExpandableListView listView = view.findViewById(R.id.lunch_list_view); + listView.setAdapter(new LunchListAdapter(getContext(), result.getToDispenseA(), result.getToDispenseB(), result.getToDispenseS())); + view.findViewById(R.id.lunch_list_loading_screen).setVisibility(View.GONE); + })); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/ui/notifications/ImportExportFragment.java b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/notifications/ImportExportFragment.java new file mode 100644 index 0000000..7b635f3 --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/notifications/ImportExportFragment.java @@ -0,0 +1,41 @@ +package de.schkola.kitchenscanner.activity.ui.notifications; + +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.RadioGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import de.schkola.kitchenscanner.R; +import de.schkola.kitchenscanner.activity.ImportExportActivity; + +public class ImportExportFragment extends Fragment { + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_import_export, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + Button importExportStartButton = view.findViewById(R.id.import_export_start); + importExportStartButton.setOnClickListener(v -> { + RadioGroup importExportType = view.findViewById(R.id.import_export_type); + Intent intent = new Intent(getContext(), ImportExportActivity.class); + if (importExportType.getCheckedRadioButtonId() == R.id.type_lunch_import) { + intent.putExtra(ImportExportActivity.EXTRA_TYPE, ImportExportActivity.TYPE_LUNCH_IMPORT); + } else if (importExportType.getCheckedRadioButtonId() == R.id.type_allergy_import) { + intent.putExtra(ImportExportActivity.EXTRA_TYPE, ImportExportActivity.TYPE_ALLERGY_IMPORT); + } else if (importExportType.getCheckedRadioButtonId() == R.id.type_lunch_export) { + intent.putExtra(ImportExportActivity.EXTRA_TYPE, ImportExportActivity.TYPE_LUNCH_EXPORT); + } + startActivity(intent); + }); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/activity/ui/search/SearchFragment.java b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/search/SearchFragment.java new file mode 100644 index 0000000..5910550 --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/activity/ui/search/SearchFragment.java @@ -0,0 +1,29 @@ +package de.schkola.kitchenscanner.activity.ui.search; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import de.schkola.kitchenscanner.R; + +public class SearchFragment extends Fragment { + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, + ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_search, container, false); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + } +} \ No newline at end of file diff --git a/app/src/main/java/de/schkola/kitchenscanner/database/LunchDatabase.java b/app/src/main/java/de/schkola/kitchenscanner/database/LunchDatabase.java index 5670999..59d47b9 100644 --- a/app/src/main/java/de/schkola/kitchenscanner/database/LunchDatabase.java +++ b/app/src/main/java/de/schkola/kitchenscanner/database/LunchDatabase.java @@ -1,11 +1,14 @@ package de.schkola.kitchenscanner.database; +import androidx.room.AutoMigration; import androidx.room.Database; import androidx.room.RoomDatabase; -@Database(entities = {Customer.class, Allergy.class}, version = 1) +@Database(entities = {Customer.class, Allergy.class, MasterData.class}, version = 2, autoMigrations = {@AutoMigration(from = 1, to = 2)}) public abstract class LunchDatabase extends RoomDatabase { public abstract CustomerDao customerDao(); public abstract AllergyDao allergyDao(); public abstract LunchDao lunchDao(); + + public abstract MasterDataDao masterDataDao(); } diff --git a/app/src/main/java/de/schkola/kitchenscanner/database/MasterData.java b/app/src/main/java/de/schkola/kitchenscanner/database/MasterData.java new file mode 100644 index 0000000..d627afc --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/database/MasterData.java @@ -0,0 +1,15 @@ +package de.schkola.kitchenscanner.database; + +import androidx.annotation.NonNull; +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "master_data") +public class MasterData { + + @PrimaryKey + @NonNull + public String key; + @NonNull + public String value; +} diff --git a/app/src/main/java/de/schkola/kitchenscanner/database/MasterDataDao.java b/app/src/main/java/de/schkola/kitchenscanner/database/MasterDataDao.java new file mode 100644 index 0000000..91d3a3a --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/database/MasterDataDao.java @@ -0,0 +1,22 @@ +package de.schkola.kitchenscanner.database; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +@Dao +public interface MasterDataDao { + + @Insert + void insertData(MasterData masterData); + + @Update + void updateData(MasterData masterData); + + @Query("SELECT value FROM master_data WHERE `key` = :key") + String getValue(String key); + + @Query("DELETE FROM master_data WHERE `key` = :key") + void deleteKey(String key); +} diff --git a/app/src/main/java/de/schkola/kitchenscanner/fragment/StatsListFragment.java b/app/src/main/java/de/schkola/kitchenscanner/fragment/StatsListFragment.java deleted file mode 100644 index 1687805..0000000 --- a/app/src/main/java/de/schkola/kitchenscanner/fragment/StatsListFragment.java +++ /dev/null @@ -1,76 +0,0 @@ -package de.schkola.kitchenscanner.fragment; - -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ExpandableListView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.view.MenuProvider; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import de.schkola.kitchenscanner.R; -import de.schkola.kitchenscanner.database.DatabaseAccess; -import de.schkola.kitchenscanner.database.LunchDatabase; -import de.schkola.kitchenscanner.task.StatTask; -import de.schkola.kitchenscanner.task.TaskRunner; -import de.schkola.kitchenscanner.util.LunchListAdapter; - -public class StatsListFragment extends Fragment { - - private final OptionsMenuProvider optionsMenuProvider = new OptionsMenuProvider(); - - @Nullable - @Override - public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - FragmentActivity activity = getActivity(); - if (activity != null) { - activity.addMenuProvider(optionsMenuProvider); - } - return inflater.inflate(R.layout.content_stats_list, container, false); - } - - @Override - public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - LunchDatabase database = new DatabaseAccess(getContext()).getDatabase(); - TaskRunner.INSTANCE.executeAsyncTask(new StatTask(database, result -> { - database.close(); - ExpandableListView listView = view.findViewById(R.id.listview); - listView.setAdapter(new LunchListAdapter(getContext(), result.getToDispenseA(), result.getToDispenseB(), result.getToDispenseS())); - view.findViewById(R.id.statsListLoadingScreen).setVisibility(View.GONE); - })); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - FragmentActivity activity = getActivity(); - if (activity != null) { - activity.removeMenuProvider(optionsMenuProvider); - } - } - - public class OptionsMenuProvider implements MenuProvider { - - @Override - public void onCreateMenu(@NonNull Menu menu, @NonNull MenuInflater menuInflater) { - menuInflater.inflate(R.menu.menu_stats_list, menu); - } - - @Override - public boolean onMenuItemSelected(@NonNull MenuItem menuItem) { - if (menuItem.getItemId() == R.id.action_chart) { - getParentFragmentManager().beginTransaction() - .replace(R.id.container, new StatsChartFragment()) - .commit(); - return true; - } - return false; - } - } -} diff --git a/app/src/main/java/de/schkola/kitchenscanner/task/CsvImportTask.java b/app/src/main/java/de/schkola/kitchenscanner/task/CsvImportTask.java deleted file mode 100644 index bd9d47a..0000000 --- a/app/src/main/java/de/schkola/kitchenscanner/task/CsvImportTask.java +++ /dev/null @@ -1,114 +0,0 @@ -package de.schkola.kitchenscanner.task; - -import de.schkola.kitchenscanner.database.Allergy; -import de.schkola.kitchenscanner.database.Customer; -import de.schkola.kitchenscanner.database.LunchDatabase; -import de.schkola.kitchenscanner.util.StringUtil; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import org.apache.commons.csv.CSVFormat; -import org.apache.commons.csv.CSVParser; -import org.apache.commons.csv.CSVRecord; -import org.apache.commons.csv.QuoteMode; - -public class CsvImportTask extends ProgressAsyncTask { - - private final LunchDatabase database; - private final boolean allergy; - private final InputStream inputStream; - private final Set duplicateXba = Collections.synchronizedSet(new LinkedHashSet<>()); - private CsvImportTask.CsvImportListener cil; - - public CsvImportTask(LunchDatabase db, boolean allergy, InputStream inputStream) { - this.database = db; - this.allergy = allergy; - this.inputStream = inputStream; - } - - public void setCsvImportListener(CsvImportTask.CsvImportListener cil) { - this.cil = cil; - } - - @Override - public Void doInBackground() { - try { - CSVFormat format = CSVFormat.Builder.create(CSVFormat.DEFAULT) - .setAllowMissingColumnNames(true) - .build(); - if (!allergy) { - format = CSVFormat.Builder.create(format) - .setQuoteMode(QuoteMode.MINIMAL) - .setSkipHeaderRecord(true) - .setHeader() - .build(); - } - - CSVParser csvParser = CSVParser.parse(inputStream, StandardCharsets.UTF_8, format); - if (allergy) { - database.allergyDao().deleteAll(); - scanAllergy(csvParser, database); - } else { - scanDay(csvParser, database); - } - } catch (IOException e) { - e.printStackTrace(); - } finally { - try { - inputStream.close(); - } catch (IOException ignored) { - // Empty on purpose - } - } - return null; - } - - @Override - public void onPostExecute(Void result) { - super.onPostExecute(result); - if (cil != null && !duplicateXba.isEmpty()) { - cil.onDuplicateXba(duplicateXba); - } - } - - private void scanDay(CSVParser csvParser, LunchDatabase database) { - try { - for (CSVRecord r : csvParser) { - - Customer customer = new Customer(); - customer.grade = r.get("Klasse"); - customer.xba = Integer.parseInt(r.get("XBA")); - customer.name = r.get("Name"); - customer.lunch = StringUtil.getLunch(r.get("Gericht")); - Customer checkCustomer = database.customerDao().getCustomer(customer.xba); - if (checkCustomer != null) { - duplicateXba.add(String.format("[%s]: %s", r.get("XBA"), r.get("Name"))); - continue; - } - database.customerDao().insertCustomer(customer); - } - } catch (Exception ex) { - throw new RuntimeException("Error in reading CSV file: ", ex); - } - } - - private void scanAllergy(CSVParser csvParser, LunchDatabase database) { - try { - for (CSVRecord r : csvParser) { - Allergy a = new Allergy(); - a.xba = Integer.parseInt(r.get(0)); - a.allergy = r.get(1); - database.allergyDao().insertAllergy(a); - } - } catch (Exception ex) { - throw new RuntimeException("Error in reading CSV file: " + ex); - } - } - - public interface CsvImportListener { - void onDuplicateXba(Set duplicateXba); - } -} diff --git a/app/src/main/java/de/schkola/kitchenscanner/task/ProgressAsyncTask.java b/app/src/main/java/de/schkola/kitchenscanner/task/ProgressAsyncTask.java deleted file mode 100644 index 7e81d71..0000000 --- a/app/src/main/java/de/schkola/kitchenscanner/task/ProgressAsyncTask.java +++ /dev/null @@ -1,26 +0,0 @@ -package de.schkola.kitchenscanner.task; - -import android.app.ProgressDialog; - -public abstract class ProgressAsyncTask implements AsyncTask { - - private ProgressDialog dialog; - - public void setProgressDialog(ProgressDialog dialog) { - this.dialog = dialog; - } - - @Override - public void onPreExecute() { - if (dialog != null) - dialog.show(); - } - - @Override - public void onPostExecute(R result) { - if (dialog != null) { - dialog.dismiss(); - dialog.cancel(); - } - } -} diff --git a/app/src/main/java/de/schkola/kitchenscanner/task/import_export/AllergyImportTask.java b/app/src/main/java/de/schkola/kitchenscanner/task/import_export/AllergyImportTask.java new file mode 100644 index 0000000..a19836a --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/task/import_export/AllergyImportTask.java @@ -0,0 +1,64 @@ +package de.schkola.kitchenscanner.task.import_export; + +import android.util.Log; +import de.schkola.kitchenscanner.database.Allergy; +import de.schkola.kitchenscanner.database.LunchDatabase; +import de.schkola.kitchenscanner.task.AsyncTask; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; + +public class AllergyImportTask implements AsyncTask { + + private final LunchDatabase database; + private final InputStream inputStream; + private final Runnable resultListener; + + public AllergyImportTask(LunchDatabase db, InputStream inputStream, Runnable resultListener) { + this.database = db; + this.inputStream = inputStream; + this.resultListener = resultListener; + } + + @Override + public Void doInBackground() { + try { + CSVFormat format = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setAllowMissingColumnNames(true) + .build(); + CSVParser csvParser = CSVParser.parse(inputStream, StandardCharsets.UTF_8, format); + database.allergyDao().deleteAll(); + scanAllergy(csvParser, database); + } catch (IOException e) { + Log.e("AllergyImportTask", "Error in allergy import", e); + } finally { + try { + inputStream.close(); + } catch (IOException ignored) { + // Empty on purpose + } + } + return null; + } + + @Override + public void onPostExecute(Void result) { + resultListener.run(); + } + + private void scanAllergy(CSVParser csvParser, LunchDatabase database) { + try { + for (CSVRecord r : csvParser) { + Allergy a = new Allergy(); + a.xba = Integer.parseInt(r.get(0)); + a.allergy = r.get(1); + database.allergyDao().insertAllergy(a); + } + } catch (Exception ex) { + throw new RuntimeException("Error in reading CSV file: " + ex); + } + } +} diff --git a/app/src/main/java/de/schkola/kitchenscanner/task/LunchExportTask.java b/app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchExportTask.java similarity index 73% rename from app/src/main/java/de/schkola/kitchenscanner/task/LunchExportTask.java rename to app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchExportTask.java index 46eefb8..6d294c9 100644 --- a/app/src/main/java/de/schkola/kitchenscanner/task/LunchExportTask.java +++ b/app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchExportTask.java @@ -1,7 +1,9 @@ -package de.schkola.kitchenscanner.task; +package de.schkola.kitchenscanner.task.import_export; +import android.util.Log; import de.schkola.kitchenscanner.database.Customer; import de.schkola.kitchenscanner.database.LunchDatabase; +import de.schkola.kitchenscanner.task.AsyncTask; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; @@ -10,14 +12,16 @@ import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVPrinter; -public class LunchExportTask extends ProgressAsyncTask { +public class LunchExportTask implements AsyncTask { private final LunchDatabase database; private final OutputStream outputStream; + private final Runnable resultListener; - public LunchExportTask(LunchDatabase database, OutputStream outputStream) { + public LunchExportTask(LunchDatabase database, OutputStream outputStream, Runnable resultListener) { this.database = database; this.outputStream = outputStream; + this.resultListener = resultListener; } @Override @@ -29,7 +33,7 @@ public Void doInBackground() { printer.printRecord(customer.xba, customer.name, customer.lunch); } } catch (IOException e) { - e.printStackTrace(); + Log.e("LunchExportTask", "Error in lunch export", e); } finally { try { outputStream.flush(); @@ -40,4 +44,9 @@ public Void doInBackground() { } return null; } + + @Override + public void onPostExecute(Void result) { + resultListener.run(); + } } diff --git a/app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchImportTask.java b/app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchImportTask.java new file mode 100644 index 0000000..b2d8720 --- /dev/null +++ b/app/src/main/java/de/schkola/kitchenscanner/task/import_export/LunchImportTask.java @@ -0,0 +1,144 @@ +package de.schkola.kitchenscanner.task.import_export; + +import android.util.Log; +import de.schkola.kitchenscanner.database.Customer; +import de.schkola.kitchenscanner.database.LunchDatabase; +import de.schkola.kitchenscanner.task.AsyncTask; +import de.schkola.kitchenscanner.util.StringUtil; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; +import org.apache.commons.csv.CSVFormat; +import org.apache.commons.csv.CSVParser; +import org.apache.commons.csv.CSVRecord; +import org.apache.commons.csv.QuoteMode; + +public class LunchImportTask implements AsyncTask { + + private final LunchDatabase database; + private final InputStream inputStream; + private final Map duplicateEntries = Collections.synchronizedMap(new HashMap<>()); + private final Consumer> resultListener; + + public LunchImportTask(LunchDatabase db, InputStream inputStream, Consumer> resultListener) { + this.database = db; + this.inputStream = inputStream; + this.resultListener = resultListener; + } + + @Override + public Void doInBackground() { + try { + CSVFormat format = CSVFormat.Builder.create(CSVFormat.DEFAULT) + .setAllowMissingColumnNames(true) + .setQuoteMode(QuoteMode.MINIMAL) + .setSkipHeaderRecord(true) + .setHeader() + .build(); + + CSVParser csvParser = CSVParser.parse(inputStream, StandardCharsets.UTF_8, format); + database.customerDao().deleteAll(); + scanDay(csvParser, database); + } catch (IOException e) { + Log.e("LunchImportTask", "Error in lunch import", e); + } finally { + try { + inputStream.close(); + } catch (IOException ignored) { + // Empty on purpose + } + } + return null; + } + + @Override + public void onPostExecute(Void result) { + resultListener.accept(duplicateEntries.values()); + } + + private void scanDay(CSVParser csvParser, LunchDatabase database) { + try { + for (CSVRecord r : csvParser) { + Customer customer = new Customer(); + customer.grade = r.get("Klasse"); + customer.xba = Integer.parseInt(r.get("XBA")); + customer.name = r.get("Name"); + customer.lunch = StringUtil.getLunch(r.get("Gericht")); + Customer checkCustomer = database.customerDao().getCustomer(customer.xba); + if (checkCustomer != null) { + Duplicate duplicate = duplicateEntries.get(customer.xba); + if (duplicate == null) { + duplicate = new Duplicate(customer.name, customer.xba); + duplicate.lunches.add(checkCustomer.lunch); + } + duplicate.addLunch(customer.lunch); + duplicateEntries.put(customer.xba, duplicate); + continue; + } + database.customerDao().insertCustomer(customer); + } + } catch (Exception ex) { + throw new RuntimeException("Error in reading CSV file: ", ex); + } + } + + public static class Duplicate { + private final int xba; + private final String name; + private final List lunches = new ArrayList<>(); + + public Duplicate(String name, int xba) { + this.name = name; + this.xba = xba; + } + + public List getLunches() { + return lunches; + } + + public String getName() { + return name; + } + + public boolean isPlausible() { + byte compare = -1; + for (Byte lunch : lunches) { + if (compare == -1) { + compare = lunch; + } else if (compare != lunch) { + return false; + } + } + return true; + } + + public int getXba() { + return xba; + } + + public void addLunch(byte lunch) { + lunches.add(lunch); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Duplicate)) return false; + Duplicate duplicate = (Duplicate) o; + return xba == duplicate.xba; + } + + @Override + public int hashCode() { + return Objects.hashCode(xba); + } + } +} diff --git a/app/src/main/java/de/schkola/kitchenscanner/util/LunchListAdapter.java b/app/src/main/java/de/schkola/kitchenscanner/util/LunchListAdapter.java index fb9e348..6788961 100644 --- a/app/src/main/java/de/schkola/kitchenscanner/util/LunchListAdapter.java +++ b/app/src/main/java/de/schkola/kitchenscanner/util/LunchListAdapter.java @@ -14,15 +14,15 @@ public class LunchListAdapter extends BaseExpandableListAdapter { private final Context context; - private final List getLunchA; - private final List getLunchB; - private final List getLunchS; + private final List lunchA; + private final List lunchB; + private final List lunchS; - public LunchListAdapter(Context context, List getLunchA, List getLunchB, List getLunchS) { + public LunchListAdapter(Context context, List lunchA, List lunchB, List lunchS) { this.context = context; - this.getLunchA = getLunchA; - this.getLunchB = getLunchB; - this.getLunchS = getLunchS; + this.lunchA = lunchA; + this.lunchB = lunchB; + this.lunchS = lunchS; } @Override @@ -58,11 +58,17 @@ public int getChildrenCount(int groupPosition) { public List getGroup(int groupPosition) { switch (groupPosition) { case 0: - return getLunchA; + if (!lunchA.isEmpty()) { + return lunchA; + } case 1: - return getLunchB; + if (!lunchB.isEmpty()) { + return lunchB; + } case 2: - return getLunchS; + if (!lunchS.isEmpty()) { + return lunchS; + } default: return Collections.emptyList(); } @@ -70,7 +76,17 @@ public List getGroup(int groupPosition) { @Override public int getGroupCount() { - return 3; + int i = 0; + if (!lunchA.isEmpty()) { + i++; + } + if (!lunchB.isEmpty()) { + i++; + } + if (!lunchS.isEmpty()) { + i++; + } + return i; } @Override @@ -83,14 +99,20 @@ public View getGroupView(int groupPosition, boolean isExpanded, View convertView int headerTitle; switch (groupPosition) { case 0: - headerTitle = R.string.getLunchA; - break; + if (!lunchA.isEmpty()) { + headerTitle = R.string.getLunchA; + break; + } case 1: - headerTitle = R.string.getLunchB; - break; + if (!lunchB.isEmpty()) { + headerTitle = R.string.getLunchB; + break; + } case 2: - headerTitle = R.string.getLunchS; - break; + if (!lunchS.isEmpty()) { + headerTitle = R.string.getLunchS; + break; + } default: headerTitle = 0; } diff --git a/app/src/main/res/drawable/ic_dashboard_black_24dp.xml b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml new file mode 100644 index 0000000..46fc8de --- /dev/null +++ b/app/src/main/res/drawable/ic_dashboard_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_help_white_24dp.xml b/app/src/main/res/drawable/ic_help_white_24dp.xml new file mode 100644 index 0000000..e6fd648 --- /dev/null +++ b/app/src/main/res/drawable/ic_help_white_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_home_black_24dp.xml b/app/src/main/res/drawable/ic_home_black_24dp.xml new file mode 100644 index 0000000..f8bb0b5 --- /dev/null +++ b/app/src/main/res/drawable/ic_home_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_list_black_24dp.xml b/app/src/main/res/drawable/ic_list_black_24dp.xml new file mode 100644 index 0000000..9922b56 --- /dev/null +++ b/app/src/main/res/drawable/ic_list_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_notifications_black_24dp.xml b/app/src/main/res/drawable/ic_notifications_black_24dp.xml new file mode 100644 index 0000000..78b75c3 --- /dev/null +++ b/app/src/main/res/drawable/ic_notifications_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_search_black_24dp.xml b/app/src/main/res/drawable/ic_search_black_24dp.xml new file mode 100644 index 0000000..3cd9ae0 --- /dev/null +++ b/app/src/main/res/drawable/ic_search_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_swap_horiz_black_24dp.xml b/app/src/main/res/drawable/ic_swap_horiz_black_24dp.xml new file mode 100644 index 0000000..eb9ef4c --- /dev/null +++ b/app/src/main/res/drawable/ic_swap_horiz_black_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/activity_bottom_nav.xml b/app/src/main/res/layout/activity_bottom_nav.xml new file mode 100644 index 0000000..5770b6d --- /dev/null +++ b/app/src/main/res/layout/activity_bottom_nav.xml @@ -0,0 +1,30 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_help.xml similarity index 69% rename from app/src/main/res/layout/activity_main.xml rename to app/src/main/res/layout/activity_help.xml index 109981c..a58ff6b 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_help.xml @@ -1,11 +1,10 @@ - + tools:context=".activity.HelpActivity"> - - - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_import_export.xml b/app/src/main/res/layout/activity_import_export.xml new file mode 100644 index 0000000..f6fd31e --- /dev/null +++ b/app/src/main/res/layout/activity_import_export.xml @@ -0,0 +1,104 @@ + + + + + + + + + +