From a13918467c4e4a058286d98f622c94f2e1b639a1 Mon Sep 17 00:00:00 2001 From: Bastiaan van der Plaat Date: Sun, 1 Mar 2026 08:32:10 +0100 Subject: [PATCH 1/2] General: Update copyright year to 2026 --- LICENSE | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LICENSE b/LICENSE index 908dd7f2..6f129937 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2018-2025 Bastiaan van der Plaat +Copyright (c) 2018-2026 Bastiaan van der Plaat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 84a1eff1..81422b48 100644 --- a/README.md +++ b/README.md @@ -102,6 +102,6 @@ A collection of various Android apps that I created for myself and others ## License -Copyright © 2018-2025 [Bastiaan van der Plaat](https://github.com/bplaat) +Copyright © 2018-2026 [Bastiaan van der Plaat](https://github.com/bplaat) Licensed under the [MIT](LICENSE) license. From 4b3d07bdb885a0445446631ca5c0420aed8c9113 Mon Sep 17 00:00:00 2001 From: Bastiaan van der Plaat Date: Sun, 1 Mar 2026 11:07:14 +0100 Subject: [PATCH 2/2] DrankCounter: Add initial app codebase --- .github/workflows/drankcounter-release.yml | 40 +++ README.md | 17 +- bin/drankcounter/AndroidManifest.xml | 36 +++ bin/drankcounter/README.md | 3 + bin/drankcounter/bob.toml | 12 + .../docs/images/icon-maskable.svg | 7 + bin/drankcounter/docs/images/icon.svg | 13 + .../drawable-anydpi/ic_account_supervisor.xml | 4 + .../res/drawable-anydpi/ic_arrow_left.xml | 4 + .../res/drawable-anydpi/ic_cup.xml | 4 + .../res/drawable-anydpi/ic_cup_white.xml | 4 + .../res/drawable-anydpi/ic_dots_vertical.xml | 4 + .../res/drawable-anydpi/ic_glass_mug.xml | 4 + .../drawable-anydpi/ic_glass_mug_white.xml | 4 + .../res/drawable-anydpi/ic_glass_wine.xml | 4 + .../drawable-anydpi/ic_glass_wine_white.xml | 4 + .../res/drawable-anydpi/ic_information.xml | 4 + .../res/drawable-anydpi/ic_invert_colors.xml | 4 + .../ic_launcher_foreground.xml | 6 + .../res/drawable-anydpi/ic_plus.xml | 4 + .../res/drawable-anydpi/ic_share_variant.xml | 4 + .../res/drawable-anydpi/ic_star.xml | 4 + .../res/drawable-anydpi/ic_web.xml | 4 + .../drawable/app_bar_icon_button_ripple.xml | 4 + .../res/drawable/widget_background.xml | 6 + .../res/layout-night/widget_drankcounter.xml | 38 +++ bin/drankcounter/res/layout/activity_main.xml | 24 ++ .../res/layout/activity_settings.xml | 109 ++++++++ bin/drankcounter/res/layout/item_drink.xml | 20 ++ .../res/layout/item_drink_section_header.xml | 3 + .../res/layout/main_drink_header.xml | 37 +++ .../res/layout/widget_drankcounter.xml | 38 +++ .../res/menu/item_drink_options.xml | 5 + bin/drankcounter/res/menu/options.xml | 5 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3163 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2199 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4625 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7169 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 9985 bytes bin/drankcounter/res/values-night/colors.xml | 11 + bin/drankcounter/res/values-night/themes.xml | 4 + bin/drankcounter/res/values-nl/strings.xml | 54 ++++ bin/drankcounter/res/values/colors.xml | 13 + bin/drankcounter/res/values/strings.xml | 58 +++++ bin/drankcounter/res/values/styles.xml | 237 ++++++++++++++++++ bin/drankcounter/res/values/themes.xml | 24 ++ .../res/xml/widget_drankcounter.xml | 11 + .../plaatsoft/drankcounter/DrinkDatabase.java | 37 +++ .../drankcounter/DrinkDatabaseHelper.java | 92 +++++++ .../nl/plaatsoft/drankcounter/Settings.java | 44 ++++ .../drankcounter/WidgetProvider.java | 96 +++++++ .../drankcounter/activities/BaseActivity.java | 69 +++++ .../drankcounter/activities/MainActivity.java | 147 +++++++++++ .../activities/SettingsActivity.java | 135 ++++++++++ .../drankcounter/components/DrinkAdapter.java | 204 +++++++++++++++ .../plaatsoft/drankcounter/models/Drink.java | 13 + 57 files changed, 1730 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/drankcounter-release.yml create mode 100644 bin/drankcounter/AndroidManifest.xml create mode 100644 bin/drankcounter/README.md create mode 100644 bin/drankcounter/bob.toml create mode 100644 bin/drankcounter/docs/images/icon-maskable.svg create mode 100644 bin/drankcounter/docs/images/icon.svg create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_account_supervisor.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_arrow_left.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_cup.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_cup_white.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_dots_vertical.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_glass_mug.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_glass_mug_white.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_glass_wine.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_glass_wine_white.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_information.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_invert_colors.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_launcher_foreground.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_plus.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_share_variant.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_star.xml create mode 100644 bin/drankcounter/res/drawable-anydpi/ic_web.xml create mode 100644 bin/drankcounter/res/drawable/app_bar_icon_button_ripple.xml create mode 100644 bin/drankcounter/res/drawable/widget_background.xml create mode 100644 bin/drankcounter/res/layout-night/widget_drankcounter.xml create mode 100644 bin/drankcounter/res/layout/activity_main.xml create mode 100644 bin/drankcounter/res/layout/activity_settings.xml create mode 100644 bin/drankcounter/res/layout/item_drink.xml create mode 100644 bin/drankcounter/res/layout/item_drink_section_header.xml create mode 100644 bin/drankcounter/res/layout/main_drink_header.xml create mode 100644 bin/drankcounter/res/layout/widget_drankcounter.xml create mode 100644 bin/drankcounter/res/menu/item_drink_options.xml create mode 100644 bin/drankcounter/res/menu/options.xml create mode 100644 bin/drankcounter/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 bin/drankcounter/res/mipmap-hdpi/ic_launcher.png create mode 100644 bin/drankcounter/res/mipmap-mdpi/ic_launcher.png create mode 100644 bin/drankcounter/res/mipmap-xhdpi/ic_launcher.png create mode 100644 bin/drankcounter/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 bin/drankcounter/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 bin/drankcounter/res/values-night/colors.xml create mode 100644 bin/drankcounter/res/values-night/themes.xml create mode 100644 bin/drankcounter/res/values-nl/strings.xml create mode 100644 bin/drankcounter/res/values/colors.xml create mode 100644 bin/drankcounter/res/values/strings.xml create mode 100644 bin/drankcounter/res/values/styles.xml create mode 100644 bin/drankcounter/res/values/themes.xml create mode 100644 bin/drankcounter/res/xml/widget_drankcounter.xml create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabase.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabaseHelper.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/Settings.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/WidgetProvider.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/BaseActivity.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/MainActivity.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/SettingsActivity.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/components/DrinkAdapter.java create mode 100644 bin/drankcounter/src/nl/plaatsoft/drankcounter/models/Drink.java diff --git a/.github/workflows/drankcounter-release.yml b/.github/workflows/drankcounter-release.yml new file mode 100644 index 00000000..69744682 --- /dev/null +++ b/.github/workflows/drankcounter-release.yml @@ -0,0 +1,40 @@ +name: drankcounter-release + +on: + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + drankcounter-release: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + - name: Install bob + run: cargo install --git https://github.com/bplaat/crates.git bob + - name: Setup Android SDK + uses: android-actions/setup-android@v3 + with: + cmdline-tools-version: 14742923 + - name: Build release + working-directory: bin/drankcounter + run: | + export JAVA_HOME=$JAVA_HOME_21_X64 + export PATH=$JAVA_HOME/bin:$PATH + echo "${{ secrets.DRANKCOUNTER_KEYSTORE }}" | base64 --decode > keystore.jks + bob build --release + - name: Create Git tag and GitHub Release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + working-directory: bin/drankcounter + run: | + TAG_VERSION=$(sed -nE 's/^[[:space:]]*version[[:space:]]*=[[:space:]]*"([^"]+)".*/\1/p' bob.toml) + gh release create "drankcounter/v${TAG_VERSION}" \ + --title "DrankCounter v${TAG_VERSION}" \ + --notes "Download the \`.apk\` file and open it to install the application" \ + target/release/drankcounter-${TAG_VERSION}.apk diff --git a/README.md b/README.md index 81422b48..e34d0743 100644 --- a/README.md +++ b/README.md @@ -31,13 +31,19 @@ A collection of various Android apps that I created for myself and others + + DrankCounter icon
+ DrankCounter +
+ + + + HackerNews icon
HackerNews
- - NOS icon
@@ -45,12 +51,6 @@ A collection of various Android apps that I created for myself and others
- - ReactTest icon
- ReactTest -
- - RedSquare icon
RedSquare @@ -75,6 +75,7 @@ A collection of various Android apps that I created for myself and others - [BassieTest](bin/bassietest/) A example test app for the bob build tool and sandbox for some ideas - [Bible](bin/bible/) An offline Android Bible app containing multiple bible translations - [CoinList](bin/coinlist/) A cryptocurrency information app similar to the [coinlist](https://github.com/bplaat/coinlist) website +- [DrankCounter](bin/drankcounter/) A basic alcohol tracker app with home screen widget - [HackerNews](bin/hackernews/) A simple [HackerNews](https://news.ycombinator.com/) webview app because installed PWA's suck sadly - [NOS](bin/nos/) A simple sync-once [NOS](https://nos.nl/) feed reader app - [ReactTest](bin/reacttest/) A test app for the [ReactDroid](lib/reactdroid/) library diff --git a/bin/drankcounter/AndroidManifest.xml b/bin/drankcounter/AndroidManifest.xml new file mode 100644 index 00000000..38c1b99b --- /dev/null +++ b/bin/drankcounter/AndroidManifest.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/README.md b/bin/drankcounter/README.md new file mode 100644 index 00000000..5a557a72 --- /dev/null +++ b/bin/drankcounter/README.md @@ -0,0 +1,3 @@ +# DrankCounter Android App + +A basic alcohol tracker app with home screen widget diff --git a/bin/drankcounter/bob.toml b/bin/drankcounter/bob.toml new file mode 100644 index 00000000..d44f25c9 --- /dev/null +++ b/bin/drankcounter/bob.toml @@ -0,0 +1,12 @@ +[package] +name = "drankcounter" +id = "nl.plaatsoft.drankcounter" +version = "1.0.0" + +[dependencies] +alerts = { path = "../../lib/alerts" } +compat = { path = "../../lib/compat" } +jspecify = { maven = "org.jspecify:jspecify:1.0.0" } + +[package.metadata.android] +main_activity = ".activities.MainActivity" diff --git a/bin/drankcounter/docs/images/icon-maskable.svg b/bin/drankcounter/docs/images/icon-maskable.svg new file mode 100644 index 00000000..fd7ef404 --- /dev/null +++ b/bin/drankcounter/docs/images/icon-maskable.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/bin/drankcounter/docs/images/icon.svg b/bin/drankcounter/docs/images/icon.svg new file mode 100644 index 00000000..78d09ba4 --- /dev/null +++ b/bin/drankcounter/docs/images/icon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_account_supervisor.xml b/bin/drankcounter/res/drawable-anydpi/ic_account_supervisor.xml new file mode 100644 index 00000000..a24a59c8 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_account_supervisor.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_arrow_left.xml b/bin/drankcounter/res/drawable-anydpi/ic_arrow_left.xml new file mode 100644 index 00000000..c0b69291 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_arrow_left.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_cup.xml b/bin/drankcounter/res/drawable-anydpi/ic_cup.xml new file mode 100644 index 00000000..1440e211 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_cup.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_cup_white.xml b/bin/drankcounter/res/drawable-anydpi/ic_cup_white.xml new file mode 100644 index 00000000..b55bb219 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_cup_white.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_dots_vertical.xml b/bin/drankcounter/res/drawable-anydpi/ic_dots_vertical.xml new file mode 100644 index 00000000..2ff93fd6 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_dots_vertical.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_glass_mug.xml b/bin/drankcounter/res/drawable-anydpi/ic_glass_mug.xml new file mode 100644 index 00000000..6c5c9a39 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_glass_mug.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_glass_mug_white.xml b/bin/drankcounter/res/drawable-anydpi/ic_glass_mug_white.xml new file mode 100644 index 00000000..fda4777b --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_glass_mug_white.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_glass_wine.xml b/bin/drankcounter/res/drawable-anydpi/ic_glass_wine.xml new file mode 100644 index 00000000..69185592 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_glass_wine.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_glass_wine_white.xml b/bin/drankcounter/res/drawable-anydpi/ic_glass_wine_white.xml new file mode 100644 index 00000000..c09a7d7d --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_glass_wine_white.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_information.xml b/bin/drankcounter/res/drawable-anydpi/ic_information.xml new file mode 100644 index 00000000..bc7e9810 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_information.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_invert_colors.xml b/bin/drankcounter/res/drawable-anydpi/ic_invert_colors.xml new file mode 100644 index 00000000..052dc564 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_invert_colors.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_launcher_foreground.xml b/bin/drankcounter/res/drawable-anydpi/ic_launcher_foreground.xml new file mode 100644 index 00000000..31ea9d88 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_launcher_foreground.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_plus.xml b/bin/drankcounter/res/drawable-anydpi/ic_plus.xml new file mode 100644 index 00000000..beb8df90 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_plus.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_share_variant.xml b/bin/drankcounter/res/drawable-anydpi/ic_share_variant.xml new file mode 100644 index 00000000..355c9f93 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_share_variant.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_star.xml b/bin/drankcounter/res/drawable-anydpi/ic_star.xml new file mode 100644 index 00000000..3422aa05 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_star.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable-anydpi/ic_web.xml b/bin/drankcounter/res/drawable-anydpi/ic_web.xml new file mode 100644 index 00000000..39b40751 --- /dev/null +++ b/bin/drankcounter/res/drawable-anydpi/ic_web.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/drawable/app_bar_icon_button_ripple.xml b/bin/drankcounter/res/drawable/app_bar_icon_button_ripple.xml new file mode 100644 index 00000000..81e0df40 --- /dev/null +++ b/bin/drankcounter/res/drawable/app_bar_icon_button_ripple.xml @@ -0,0 +1,4 @@ + + diff --git a/bin/drankcounter/res/drawable/widget_background.xml b/bin/drankcounter/res/drawable/widget_background.xml new file mode 100644 index 00000000..6faf0f02 --- /dev/null +++ b/bin/drankcounter/res/drawable/widget_background.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/bin/drankcounter/res/layout-night/widget_drankcounter.xml b/bin/drankcounter/res/layout-night/widget_drankcounter.xml new file mode 100644 index 00000000..719a9d88 --- /dev/null +++ b/bin/drankcounter/res/layout-night/widget_drankcounter.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/layout/activity_main.xml b/bin/drankcounter/res/layout/activity_main.xml new file mode 100644 index 00000000..9c579329 --- /dev/null +++ b/bin/drankcounter/res/layout/activity_main.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/layout/activity_settings.xml b/bin/drankcounter/res/layout/activity_settings.xml new file mode 100644 index 00000000..400d2adb --- /dev/null +++ b/bin/drankcounter/res/layout/activity_settings.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/layout/item_drink.xml b/bin/drankcounter/res/layout/item_drink.xml new file mode 100644 index 00000000..ba787a03 --- /dev/null +++ b/bin/drankcounter/res/layout/item_drink.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/layout/item_drink_section_header.xml b/bin/drankcounter/res/layout/item_drink_section_header.xml new file mode 100644 index 00000000..7e690036 --- /dev/null +++ b/bin/drankcounter/res/layout/item_drink_section_header.xml @@ -0,0 +1,3 @@ + + diff --git a/bin/drankcounter/res/layout/main_drink_header.xml b/bin/drankcounter/res/layout/main_drink_header.xml new file mode 100644 index 00000000..406a2e27 --- /dev/null +++ b/bin/drankcounter/res/layout/main_drink_header.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/layout/widget_drankcounter.xml b/bin/drankcounter/res/layout/widget_drankcounter.xml new file mode 100644 index 00000000..719a9d88 --- /dev/null +++ b/bin/drankcounter/res/layout/widget_drankcounter.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/menu/item_drink_options.xml b/bin/drankcounter/res/menu/item_drink_options.xml new file mode 100644 index 00000000..7bbfac16 --- /dev/null +++ b/bin/drankcounter/res/menu/item_drink_options.xml @@ -0,0 +1,5 @@ + + + + diff --git a/bin/drankcounter/res/menu/options.xml b/bin/drankcounter/res/menu/options.xml new file mode 100644 index 00000000..59d23d08 --- /dev/null +++ b/bin/drankcounter/res/menu/options.xml @@ -0,0 +1,5 @@ + + + + diff --git a/bin/drankcounter/res/mipmap-anydpi-v26/ic_launcher.xml b/bin/drankcounter/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..35c2a772 --- /dev/null +++ b/bin/drankcounter/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/bin/drankcounter/res/mipmap-hdpi/ic_launcher.png b/bin/drankcounter/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..6426a355d1aa9cd6e3bea821355960c350f89c0f GIT binary patch literal 3163 zcmV-h45agkP)4+XaDpL_#t zkM{19-|u(*llEXeSoh>g$H~A^%_cK$q}Sot^!pt|fHTF8zK%dFV37ah)7j=VjU6 zYaECA9S#&^7!;&w#2bx>*XvQ=&Yh^Qv{Vd_AM*wS3YbhNK+|x%@OM2fS8s>IQTL_8 z!NPls6?PA;3|6gP^A$$vV%f7i$A$V`E)=v{Q2;RD!T|svN=o8Kr;|caQnXscKGM>-LS@#{sy@_YYbiihV6Bk1W1ON$vApY41ZECYkf5R|u zz_~!{t(vO2=yclrWo3gfCxnVI7)XHth5$$ke7=ui1{*9E13tH`m8_-=Q(oTM4vu>V zw^JlwHUkVq4y25;c%U6S#NcC~?IIq#{jJh1TOM19YEpgbbZULOcKOAsQNUReAUTd4 z2vS@OKZVclVHn?Oy}oEUXOrp!v$>#`Wp9X86O)wBA7vmhaGaN>M{1V|NmXyPQasC! z^CnX&aF!NGtrpS;&(ZX_h?FXvTC+LN&$7QM;4C+g97h6@knrPPnjU*kr`rZdslnm6 zVG(EbR*xJAQdA@$d9Bvr*R|SbmU3`sJeQlh$;)wGBAk^60vs`Y`0);31h3WUbmh=N3sn(3xu%_AE+h{lublAE5K3nK8w@7GfyjpRoXCKPaUcZ+LR$6u z&#}Ry3k_=(&ibD$mQh6%ISdIQom?EJZ8 zvAj=!vM`X(H}`Ip5C~wdA2=X<5tLd;Lb$ePj|en!=#YRHyCQe*qT!mF1USk-%@p-6 zwpbfGhv30FkI8gJ0fYnya1sIm&d+=IqG&iQ-)}`CFpFdx(($Gy6mLZ*CeXm{-9*&` zxk6DbFpF_iu>(5$d!uoH5Js#N84yJ!0Fv73kCvAC7aE>Gq2^{ZP+1v+BL#Nze|B^T zh;!GiGiQjZ2hyd{1hK{AIGM3P8@u)TQ9>LNAb=6`*MH@lTWl0n7=WpEYb~$^QrHV? zyKaCLc6QFp&X#1$#am)bK@x}rd{9F$@yUw%WE$TvCi(yxMFJQFDHQ}QgcfZe6)XkW zt-w%$5N=9sxNN0RE^R4{=Py5W=wbIvPj=ExILVj5UQTxM4i3^|6PHnR$gr&s{72UdY=>sQ%rxq}+@;7zxRI0;!2W9M!nzqq`3|0&aT!AnPV#L@gh>$^3CCgPQpq@*Y;QG6Ds;iCO&Q5VbIK4g$h2BhkvG`Sl-?2Wgm8$Ip-D^PftXnUDk!LfCN?$ieW2qRuErK#stM>xoeKcGm~B3*#_qF zDcHl>3c1?apRx_ zZ3u^dg4N6F4Gg)I(TgWeoYibiikZO`s~`t~E3KJoXDcc!%Slk?`+P6s?%84a_7{q8 zovK#r94VO+1pQtyMUiMrZrU`cmFrts{(+9Qgh|d;qDU?*j5J209chFxiT47%kk`I# z8w1owX3no%$s}U<|38(LMso9J=JSwO(g4$ni5wn{gmi?Q&Qj~q!{#E-Cz4e8XS%4- zwDd$G42&2~GUim~LNwEUJ`M&yf|(Y1+R! zE5rsd_Q}~vNZa!+*Nd=~5u94=RKpBEexBPst3g$hx`-71bh2GegDfXsak*yWo@WG( zEhFu5?9jK+=bO7*RsVxX5|SbwHR5t;f4|A{6%?cycHoGLS=Y;FNPyf^+CfQAJ37lcL;7D2k!mC`HGJX(HrKRXDtJ&fQh& z8H^eyB`+JE=aFNIrqMXxr!4e(7px11Ya0mZ+%L<0138q5G6AJPpV0SONvd7o^)948 zQ=$}8#+afeC+i+=LMgz|F2)=XSrP~=StCiCx6>76FAgq_13joHkW~B7k;4GK2cL=K zci_9w$7)I1Li>E5_N`H-;0jfQdC@WSQNu4!94KluPCY-$QYPf*KUWb5y!&Y|xcnPQ z`guz@oTw$_)Gk6U?IENafPcel@ZOqGsOGa^@bh>4{tEam?L$Q=0g4wRW;ka$Wv!@O z$`d$%QCJ*~M`06jpfV0xqeKx6Uz71OlQ8M}DC1E42<$soKup#?ugqZ*=EQ*(p*R=C zjkr)m5q`#r-^m~F)Tx|DNY2PZ)Pzun=x_fHj~Z8 z#7Bm6;$s<`6~mn35MLNUMZlJ}AihwiMqz@=ON6uEIUKl^!-2aNb${$6-n*DPo7}uW4}iR>d|V~JPmb~WFF||H8$Uj9yIybHo1VV-a7IRvBR##TIz1h^ z%3(A%m8PW??KK(~Z-ZmuJYQ
wbaz{9!bvm6%KxXGTVAwb6*GQ&W+1;zVRmNI-V2 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 literal 0 HcmV?d00001 diff --git a/bin/drankcounter/res/mipmap-xxhdpi/ic_launcher.png b/bin/drankcounter/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..3c65f87dc0364af7bc0ced2c61bd192977f49741 GIT binary patch literal 7169 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 DPVj0P literal 0 HcmV?d00001 diff --git a/bin/drankcounter/res/mipmap-xxxhdpi/ic_launcher.png b/bin/drankcounter/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..732d945a8e6d9df8646b2e3db27b0e8586f945d5 GIT binary patch literal 9985 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 A8vp + + #222 + #111 + #fff + #888 + #fff + #333 + #1E1E1E + #2B2B2B + diff --git a/bin/drankcounter/res/values-night/themes.xml b/bin/drankcounter/res/values-night/themes.xml new file mode 100644 index 00000000..21bf2f02 --- /dev/null +++ b/bin/drankcounter/res/values-night/themes.xml @@ -0,0 +1,4 @@ + + + + diff --git a/bin/drankcounter/res/values-nl/strings.xml b/bin/drankcounter/res/values-nl/strings.xml new file mode 100644 index 00000000..d78ce97c --- /dev/null +++ b/bin/drankcounter/res/values-nl/strings.xml @@ -0,0 +1,54 @@ + + + + Instellingen + + + Bier + Wijn + Sterke drank + Nog geen drankjes geregistreerd + + + Vandaag + Gisteren + + + Bier + Wijn + Sterke drank + + + Verwijderen + + + Instellingen + + Weergave + + Taal + Gebruik systeemstandaard + + Thema + Licht + Donker + Ingesteld door batterijbesparing + Gebruik systeemstandaard + + Informatie + App versie + Who, jij bent een geweldige hacker! + Beoordeel deze app + Deel deze app + Bekijk deze coole app: %s + Over deze app + Deze applicatie is gemaakt door Bastiaan van der Plaat + + Over deze app + Deze applicatie is gemaakt door Bastiaan van der Plaat + Website + OK + + + Registreer je dagelijkse drankjes + diff --git a/bin/drankcounter/res/values/colors.xml b/bin/drankcounter/res/values/colors.xml new file mode 100644 index 00000000..4df0b0b8 --- /dev/null +++ b/bin/drankcounter/res/values/colors.xml @@ -0,0 +1,13 @@ + + + #D32F2F + #fff + #D32F2F + #fff + #111 + #777 + #333 + #ddd + #F0F0F0 + #FFFFFF + diff --git a/bin/drankcounter/res/values/strings.xml b/bin/drankcounter/res/values/strings.xml new file mode 100644 index 00000000..cdf30d21 --- /dev/null +++ b/bin/drankcounter/res/values/strings.xml @@ -0,0 +1,58 @@ + + + DrankCounter + + + Settings + + + Beer + Wine + Liqueur + No drinks recorded yet + + + Today + Yesterday + + + Beer + Wine + Liqueur + + + Delete + + + Settings + + Display + + Language + English + Nederlands + Use system default + + Theme + Light + Dark + Set by Battery Saver + Use system default + + Information + App version + Who, you are an amazing hacker! + Rate this app + Share this app + Check out this cool app: %s + About this app + This application is made by Bastiaan van der Plaat + + About this app + This application is made by Bastiaan van der Plaat + Website + OK + + + Track your daily drinks + diff --git a/bin/drankcounter/res/values/styles.xml b/bin/drankcounter/res/values/styles.xml new file mode 100644 index 00000000..108f4e68 --- /dev/null +++ b/bin/drankcounter/res/values/styles.xml @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bin/drankcounter/res/values/themes.xml b/bin/drankcounter/res/values/themes.xml new file mode 100644 index 00000000..e15f65a8 --- /dev/null +++ b/bin/drankcounter/res/values/themes.xml @@ -0,0 +1,24 @@ + + + + + + diff --git a/bin/drankcounter/res/xml/widget_drankcounter.xml b/bin/drankcounter/res/xml/widget_drankcounter.xml new file mode 100644 index 00000000..4d509294 --- /dev/null +++ b/bin/drankcounter/res/xml/widget_drankcounter.xml @@ -0,0 +1,11 @@ + + diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabase.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabase.java new file mode 100644 index 00000000..7db88be9 --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabase.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +public class DrinkDatabase extends SQLiteOpenHelper { + private static final String DB_NAME = "drankcounter.db"; + private static final int DB_VERSION = 1; + + public static final String TABLE_DRINKS = "drinks"; + public static final String COLUMN_ID = "id"; + public static final String COLUMN_TYPE = "type"; + public static final String COLUMN_CREATED_AT = "created_at"; + + public DrinkDatabase(Context context) { + super(context, DB_NAME, null, DB_VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + var createTableSQL = "CREATE TABLE " + TABLE_DRINKS + " (" + COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + COLUMN_TYPE + " INTEGER NOT NULL, " + COLUMN_CREATED_AT + " INTEGER NOT NULL)"; + db.execSQL(createTableSQL); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + // No upgrades for now + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabaseHelper.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabaseHelper.java new file mode 100644 index 00000000..8fd5b35e --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/DrinkDatabaseHelper.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import nl.plaatsoft.drankcounter.models.Drink; + +public class DrinkDatabaseHelper { + private final DrinkDatabase dbHelper; + + public DrinkDatabaseHelper(Context context) { + this.dbHelper = new DrinkDatabase(context); + } + + public long insertDrink(int type, long createdAt) { + var db = dbHelper.getWritableDatabase(); + var values = new ContentValues(); + values.put(DrinkDatabase.COLUMN_TYPE, type); + values.put(DrinkDatabase.COLUMN_CREATED_AT, createdAt); + return db.insert(DrinkDatabase.TABLE_DRINKS, null, values); + } + + public List getAllDrinks() { + var db = dbHelper.getReadableDatabase(); + var drinks = new ArrayList(); + var cursor = db.query( + DrinkDatabase.TABLE_DRINKS, null, null, null, null, null, DrinkDatabase.COLUMN_CREATED_AT + " DESC"); + if (cursor.moveToFirst()) { + do { + var id = cursor.getLong(cursor.getColumnIndexOrThrow(DrinkDatabase.COLUMN_ID)); + var type = cursor.getInt(cursor.getColumnIndexOrThrow(DrinkDatabase.COLUMN_TYPE)); + var createdAt = cursor.getLong(cursor.getColumnIndexOrThrow(DrinkDatabase.COLUMN_CREATED_AT)); + drinks.add(new Drink(id, type, createdAt)); + } while (cursor.moveToNext()); + } + cursor.close(); + return drinks; + } + + public List getTodaysDrinks() { + var cal = Calendar.getInstance(); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + var startOfDay = cal.getTimeInMillis() / 1000; + var db = dbHelper.getReadableDatabase(); + var drinks = new ArrayList(); + var cursor = db.query(DrinkDatabase.TABLE_DRINKS, null, DrinkDatabase.COLUMN_CREATED_AT + " >= ?", + new String[] {String.valueOf(startOfDay)}, null, null, DrinkDatabase.COLUMN_CREATED_AT + " DESC"); + if (cursor.moveToFirst()) { + do { + var id = cursor.getLong(cursor.getColumnIndexOrThrow(DrinkDatabase.COLUMN_ID)); + var type = cursor.getInt(cursor.getColumnIndexOrThrow(DrinkDatabase.COLUMN_TYPE)); + var createdAt = cursor.getLong(cursor.getColumnIndexOrThrow(DrinkDatabase.COLUMN_CREATED_AT)); + drinks.add(new Drink(id, type, createdAt)); + } while (cursor.moveToNext()); + } + cursor.close(); + return drinks; + } + + public static int countByType(List drinks, int type) { + var count = 0; + for (var drink : drinks) { + if (drink.type() == type) + count++; + } + return count; + } + + public void deleteDrink(long id) { + var db = dbHelper.getWritableDatabase(); + db.delete(DrinkDatabase.TABLE_DRINKS, DrinkDatabase.COLUMN_ID + " = ?", new String[] {String.valueOf(id)}); + } + + public void close() { + dbHelper.close(); + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/Settings.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/Settings.java new file mode 100644 index 00000000..8aa6ffdb --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/Settings.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter; + +import android.content.Context; +import android.content.SharedPreferences; + +public class Settings { + private final SharedPreferences prefs; + + public Settings(Context context) { + prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE); + } + + // Language + public static final int LANGUAGE_ENGLISH = 0; + public static final int LANGUAGE_DUTCH = 1; + public static final int LANGUAGE_SYSTEM = 2; + + public int getLanguage() { + return prefs.getInt("language", LANGUAGE_SYSTEM); + } + + public void setLanguage(int language) { + prefs.edit().putInt("language", language).apply(); + } + + // Theme + public static final int THEME_LIGHT = 0; + public static final int THEME_DARK = 1; + public static final int THEME_SYSTEM = 2; + + public int getTheme() { + return prefs.getInt("theme", THEME_SYSTEM); + } + + public void setTheme(int theme) { + prefs.edit().putInt("theme", theme).apply(); + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/WidgetProvider.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/WidgetProvider.java new file mode 100644 index 00000000..17ee4241 --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/WidgetProvider.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter; + +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.widget.RemoteViews; + +import nl.plaatsoft.drankcounter.models.Drink; + +public class WidgetProvider extends AppWidgetProvider { + private static final String ACTION_ADD_DRINK = "nl.plaatsoft.drankcounter.ACTION_ADD_DRINK"; + private static final String EXTRA_DRINK_TYPE = "drink_type"; + + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + for (var id : appWidgetIds) { + updateWidget(context, appWidgetManager, id); + } + } + + @Override + public void onReceive(Context context, Intent intent) { + super.onReceive(context, intent); + if (ACTION_ADD_DRINK.equals(intent.getAction())) { + var type = intent.getIntExtra(EXTRA_DRINK_TYPE, Drink.TYPE_BEER); + var dbHelper = new DrinkDatabaseHelper(context); + dbHelper.insertDrink(type, System.currentTimeMillis() / 1000); + dbHelper.close(); + updateAllWidgets(context); + } + } + + public static void updateAllWidgets(Context context) { + var appWidgetManager = AppWidgetManager.getInstance(context); + var ids = appWidgetManager.getAppWidgetIds(new ComponentName(context, WidgetProvider.class)); + for (var id : ids) { + updateWidget(context, appWidgetManager, id); + } + } + + private static void updateWidget(Context context, AppWidgetManager appWidgetManager, int widgetId) { + var dbHelper = new DrinkDatabaseHelper(context); + var todayDrinks = dbHelper.getTodaysDrinks(); + dbHelper.close(); + + var beerCount = DrinkDatabaseHelper.countByType(todayDrinks, Drink.TYPE_BEER); + var wineCount = DrinkDatabaseHelper.countByType(todayDrinks, Drink.TYPE_WINE); + var liqueurCount = DrinkDatabaseHelper.countByType(todayDrinks, Drink.TYPE_LIQUEUR); + + var lightViews = new RemoteViews(context.getPackageName(), R.layout.widget_drankcounter); + applyCountsAndClicks(context, lightViews, beerCount, wineCount, liqueurCount); + + RemoteViews views; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + var darkViews = new RemoteViews(context.getPackageName(), R.layout.widget_drankcounter); + applyCountsAndClicks(context, darkViews, beerCount, wineCount, liqueurCount); + views = new RemoteViews(lightViews, darkViews); + } else { + views = lightViews; + } + + appWidgetManager.updateAppWidget(widgetId, views); + } + + private static void applyCountsAndClicks( + Context context, RemoteViews views, int beerCount, int wineCount, int liqueurCount) { + views.setTextViewText(R.id.widget_beer_count, String.valueOf(beerCount)); + views.setTextViewText(R.id.widget_wine_count, String.valueOf(wineCount)); + views.setTextViewText(R.id.widget_liqueur_count, String.valueOf(liqueurCount)); + + views.setOnClickPendingIntent(R.id.widget_beer_button, buildAddDrinkIntent(context, Drink.TYPE_BEER)); + views.setOnClickPendingIntent(R.id.widget_wine_button, buildAddDrinkIntent(context, Drink.TYPE_WINE)); + views.setOnClickPendingIntent(R.id.widget_liqueur_button, buildAddDrinkIntent(context, Drink.TYPE_LIQUEUR)); + } + + private static PendingIntent buildAddDrinkIntent(Context context, int drinkType) { + var intent = new Intent(context, WidgetProvider.class); + intent.setAction(ACTION_ADD_DRINK); + intent.putExtra(EXTRA_DRINK_TYPE, drinkType); + var flags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + flags |= PendingIntent.FLAG_IMMUTABLE; + } + return PendingIntent.getBroadcast(context, drinkType, intent, flags); + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/BaseActivity.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/BaseActivity.java new file mode 100644 index 00000000..13cfcf40 --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/BaseActivity.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter.activities; + +import java.util.Locale; + +import android.app.Activity; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Build; +import android.os.PowerManager; +import android.view.ViewGroup; +import android.window.OnBackInvokedCallback; +import android.window.OnBackInvokedDispatcher; + +import nl.plaatsoft.android.compat.CompatActivity; +import nl.plaatsoft.android.compat.WindowInsetsCompat; +import nl.plaatsoft.drankcounter.Settings; + +import org.jspecify.annotations.Nullable; + +public abstract class BaseActivity extends CompatActivity { + protected @SuppressWarnings("null") Settings settings; + + @Override + public void attachBaseContext(@SuppressWarnings("null") Context context) { + settings = new Settings(context); + var language = settings.getLanguage(); + var theme = settings.getTheme(); + + // Update configuration when different from system defaults + if (language != Settings.LANGUAGE_SYSTEM || theme != Settings.THEME_SYSTEM + || (theme == Settings.THEME_SYSTEM && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q)) { + var configuration = new Configuration(context.getResources().getConfiguration()); + + if (language == Settings.LANGUAGE_ENGLISH) + configuration.setLocale(Locale.forLanguageTag("en")); + if (language == Settings.LANGUAGE_DUTCH) + configuration.setLocale(Locale.forLanguageTag("nl")); + + if (theme == Settings.THEME_LIGHT) { + configuration.uiMode |= Configuration.UI_MODE_NIGHT_NO; + configuration.uiMode &= ~Configuration.UI_MODE_NIGHT_YES; + } + if (theme == Settings.THEME_DARK) { + configuration.uiMode |= Configuration.UI_MODE_NIGHT_YES; + configuration.uiMode &= ~Configuration.UI_MODE_NIGHT_NO; + } + // Set dark mode on when in battery saver mode + if (theme == Settings.THEME_SYSTEM && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + if (((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isPowerSaveMode()) { + configuration.uiMode |= Configuration.UI_MODE_NIGHT_YES; + configuration.uiMode &= ~Configuration.UI_MODE_NIGHT_NO; + } else { + configuration.uiMode |= Configuration.UI_MODE_NIGHT_NO; + configuration.uiMode &= ~Configuration.UI_MODE_NIGHT_YES; + } + } + + super.attachBaseContext(context.createConfigurationContext(configuration)); + return; + } + super.attachBaseContext(context); + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/MainActivity.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/MainActivity.java new file mode 100644 index 00000000..e16ec076 --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/MainActivity.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter.activities; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.ListView; +import android.widget.PopupMenu; +import android.widget.TextView; + +import nl.plaatsoft.android.alerts.RatingAlert; +import nl.plaatsoft.android.alerts.UpdateAlert; +import nl.plaatsoft.android.compat.ContextCompat; +import nl.plaatsoft.android.fetch.FetchDataTask; +import nl.plaatsoft.android.fetch.FetchImageTask; +import nl.plaatsoft.drankcounter.DrinkDatabaseHelper; +import nl.plaatsoft.drankcounter.R; +import nl.plaatsoft.drankcounter.WidgetProvider; +import nl.plaatsoft.drankcounter.components.DrinkAdapter; +import nl.plaatsoft.drankcounter.models.Drink; + +import org.jspecify.annotations.Nullable; + +public class MainActivity extends BaseActivity implements PopupMenu.OnMenuItemClickListener { + private static final int SETTINGS_REQUEST_CODE = 1; + + private Handler handler = new Handler(Looper.getMainLooper()); + private int oldLanguage = -1; + private int oldTheme = -1; + + private DrinkDatabaseHelper dbHelper; + private DrinkAdapter drinkAdapter; + private ListView drinkList; + private TextView emptyText; + private TextView beerCountView; + private TextView wineCountView; + private TextView liqueurCountView; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + // Initialize database + dbHelper = new DrinkDatabaseHelper(this); + + // Setup list with drink buttons as scrollable header + drinkList = findViewById(R.id.main_drink_list); + useWindowInsets(drinkList); + emptyText = findViewById(R.id.main_empty_list); + + var headerView = LayoutInflater.from(this).inflate(R.layout.main_drink_header, drinkList, false); + drinkList.addHeaderView(headerView, null, false); + + drinkAdapter = new DrinkAdapter(this, dbHelper); + drinkAdapter.setOnDeleteListener(this::refreshDrinkList); + drinkList.setAdapter(drinkAdapter); + + beerCountView = headerView.findViewById(R.id.main_beer_count); + wineCountView = headerView.findViewById(R.id.main_wine_count); + liqueurCountView = headerView.findViewById(R.id.main_liqueur_count); + + // Options menu button + findViewById(R.id.main_options_menu_button).setOnClickListener(view -> { + var optionsMenu = new PopupMenu(this, view, Gravity.TOP | Gravity.RIGHT); + optionsMenu.getMenuInflater().inflate(R.menu.options, optionsMenu.getMenu()); + optionsMenu.setOnMenuItemClickListener(this); + optionsMenu.show(); + }); + + // Add drink buttons (in scrollable header) + headerView.findViewById(R.id.main_add_beer_button).setOnClickListener(view -> addDrink(Drink.TYPE_BEER)); + headerView.findViewById(R.id.main_add_wine_button).setOnClickListener(view -> addDrink(Drink.TYPE_WINE)); + headerView.findViewById(R.id.main_add_liqueur_button).setOnClickListener(view -> addDrink(Drink.TYPE_LIQUEUR)); + + // Show update alert + UpdateAlert.checkAndShow(this, + "https://raw.githubusercontent.com/bplaat/android-apps/refs/heads/master/bin/drankcounter/bob.toml", + SettingsActivity.STORE_PAGE_URL); + } + + private void addDrink(int type) { + dbHelper.insertDrink(type, System.currentTimeMillis() / 1000); + refreshDrinkList(); + } + + @Override + protected void onResume() { + super.onResume(); + refreshDrinkList(); + } + + private void refreshDrinkList() { + var drinks = dbHelper.getAllDrinks(); + var todayDrinks = dbHelper.getTodaysDrinks(); + + emptyText.setVisibility(drinks.isEmpty() ? View.VISIBLE : View.GONE); + drinkAdapter.setDrinks(drinks); + + beerCountView.setText(String.valueOf(DrinkDatabaseHelper.countByType(todayDrinks, Drink.TYPE_BEER))); + wineCountView.setText(String.valueOf(DrinkDatabaseHelper.countByType(todayDrinks, Drink.TYPE_WINE))); + liqueurCountView.setText(String.valueOf(DrinkDatabaseHelper.countByType(todayDrinks, Drink.TYPE_LIQUEUR))); + + WidgetProvider.updateAllWidgets(this); + } + + @Override + public boolean onMenuItemClick(@SuppressWarnings("null") MenuItem item) { + if (item.getItemId() == R.id.menu_options_settings) { + oldLanguage = settings.getLanguage(); + oldTheme = settings.getTheme(); + startActivityForResult(new Intent(this, SettingsActivity.class), SETTINGS_REQUEST_CODE); + return true; + } + return false; + } + + @Override + public void onActivityResult(int requestCode, int resultCode, @SuppressWarnings("null") Intent data) { + // When settings activity is closed check for restart + if (requestCode == SETTINGS_REQUEST_CODE) { + if (oldLanguage != -1 && oldTheme != -1) { + if (oldLanguage != settings.getLanguage() || oldTheme != settings.getTheme()) { + handler.post(() -> recreate()); + } + } + } + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (dbHelper != null) { + dbHelper.close(); + } + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/SettingsActivity.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/SettingsActivity.java new file mode 100644 index 00000000..bc6b3da3 --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/activities/SettingsActivity.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter.activities; + +import android.app.AlertDialog; +import android.content.Intent; +import android.content.pm.PackageManager.NameNotFoundException; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; + +import nl.plaatsoft.drankcounter.R; + +import org.jspecify.annotations.Nullable; + +@SuppressWarnings("this-escape") +public class SettingsActivity extends BaseActivity { + public static final String STORE_PAGE_URL = "https://github.com/bplaat/android-apps/tree/master/bin/drankcounter"; + private static final String ABOUT_WEBSITE_URL = "https://bplaat.nl/"; + + private int versionButtonClickCounter = 0; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_settings); + useWindowInsets(); + + // Back button + findViewById(R.id.settings_back_button).setOnClickListener(view -> finish()); + + // Language button + var languages = new String[] {getResources().getString(R.string.settings_language_english), + getResources().getString(R.string.settings_language_dutch), + getResources().getString(R.string.settings_language_system)}; + var language = settings.getLanguage(); + ((TextView)findViewById(R.id.settings_language_label)).setText(languages[language]); + findViewById(R.id.settings_language_button).setOnClickListener(view -> { + var alertDialog = new AlertDialog.Builder(this) + .setTitle(R.string.settings_language_button) + .setSingleChoiceItems(languages, language, + (dialog, which) -> { + dialog.dismiss(); + if (language != which) { + settings.setLanguage(which); + recreate(); + } + }) + .show(); + var density = getResources().getDisplayMetrics().density; + alertDialog.getListView().setPadding(0, 0, 0, (int)(16 * density)); + }); + + // Themes button + var themes = new String[] {getResources().getString(R.string.settings_theme_light), + getResources().getString(R.string.settings_theme_dark), + Build.VERSION.SDK_INT < Build.VERSION_CODES.Q + ? getResources().getString(R.string.settings_theme_battery_saver) + : getResources().getString(R.string.settings_theme_system)}; + var theme = settings.getTheme(); + ((TextView)findViewById(R.id.settings_theme_label)).setText(themes[theme]); + findViewById(R.id.settings_theme_button).setOnClickListener(view -> { + var alertDialog = new AlertDialog.Builder(this) + .setTitle(R.string.settings_theme_button) + .setSingleChoiceItems(themes, theme, + (dialog, which) -> { + dialog.dismiss(); + if (theme != which) { + settings.setTheme(which); + recreate(); + } + }) + .show(); + var density = getResources().getDisplayMetrics().density; + alertDialog.getListView().setPadding(0, 0, 0, (int)(16 * density)); + }); + + // Version button easter egg + try { + ((TextView)findViewById(R.id.settings_version_label)) + .setText("v" + getPackageManager().getPackageInfo(getPackageName(), 0).versionName); + } catch (NameNotFoundException exception) { + Log.e(getPackageName(), "Can't get app version", exception); + } + findViewById(R.id.settings_version_button).setOnClickListener(view -> { + versionButtonClickCounter++; + if (versionButtonClickCounter == 8) { + versionButtonClickCounter = 0; + Toast.makeText(this, R.string.settings_version_message, Toast.LENGTH_SHORT).show(); + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("https://youtu.be/dQw4w9WgXcQ?t=43"))); + } + }); + + // Rate button + findViewById(R.id.settings_rate_button).setOnClickListener(view -> { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(STORE_PAGE_URL))); + }); + + // Share button + findViewById(R.id.settings_share_button).setOnClickListener(view -> { + var intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.setType("text/plain"); + intent.putExtra( + Intent.EXTRA_TEXT, getResources().getString(R.string.settings_share_message, STORE_PAGE_URL)); + startActivity(Intent.createChooser(intent, null)); + }); + + // About button + findViewById(R.id.settings_about_button).setOnClickListener(view -> { + new AlertDialog.Builder(this) + .setTitle(R.string.settings_about_alert_title_label) + .setMessage(R.string.settings_about_alert_message_label) + .setNegativeButton(R.string.settings_about_alert_website_button, + (dialog, which) -> { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(ABOUT_WEBSITE_URL))); }) + .setPositiveButton(R.string.settings_about_alert_ok_button, null) + .show(); + }); + + // Footer button + findViewById(R.id.settings_footer_button).setOnClickListener(view -> { + startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(ABOUT_WEBSITE_URL))); + }); + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/components/DrinkAdapter.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/components/DrinkAdapter.java new file mode 100644 index 00000000..3acf0d0a --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/components/DrinkAdapter.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter.components; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; +import java.util.Objects; + +import android.content.Context; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.PopupMenu; +import android.widget.TextView; + +import nl.plaatsoft.drankcounter.DrinkDatabaseHelper; +import nl.plaatsoft.drankcounter.R; +import nl.plaatsoft.drankcounter.Settings; +import nl.plaatsoft.drankcounter.models.Drink; + +import org.jspecify.annotations.Nullable; + +public class DrinkAdapter extends BaseAdapter { + private static final int VIEW_TYPE_HEADER = 0; + private static final int VIEW_TYPE_DRINK = 1; + + public interface OnDeleteListener { + void onDrinkDeleted(); + } + + private static class DrinkViewHolder { + public @SuppressWarnings("null") ImageView drinkIcon; + public @SuppressWarnings("null") TextView drinkType; + public @SuppressWarnings("null") TextView drinkTime; + public @SuppressWarnings("null") ImageButton drinkMenuButton; + } + + private final Context context; + private final DrinkDatabaseHelper dbHelper; + private final SimpleDateFormat dayKeyFormat; + private final SimpleDateFormat dayDisplayFormat; + private final SimpleDateFormat timeFormat; + private final String todayLabel; + private final String yesterdayLabel; + private final List items = new ArrayList<>(); + private @Nullable OnDeleteListener onDeleteListener; + + public DrinkAdapter(Context context, DrinkDatabaseHelper dbHelper) { + this.context = context; + this.dbHelper = dbHelper; + var settings = new Settings(context); + var locale = getLocaleFromSettings(settings); + this.dayKeyFormat = new SimpleDateFormat("yyyyMMdd", Locale.getDefault()); + this.dayDisplayFormat = new SimpleDateFormat("d MMMM", locale); + this.timeFormat = new SimpleDateFormat("HH:mm", locale); + this.todayLabel = context.getString(R.string.day_header_today); + this.yesterdayLabel = context.getString(R.string.day_header_yesterday); + } + + private Locale getLocaleFromSettings(Settings settings) { + return switch (settings.getLanguage()) { + case Settings.LANGUAGE_ENGLISH -> Locale.forLanguageTag("en"); + case Settings.LANGUAGE_DUTCH -> Locale.forLanguageTag("nl"); + default -> Locale.getDefault(); + }; + } + + public void setOnDeleteListener(OnDeleteListener listener) { + this.onDeleteListener = listener; + } + + public void setDrinks(List drinks) { + items.clear(); + var today = dayKeyFormat.format(new Date()); + var cal = Calendar.getInstance(); + cal.add(Calendar.DAY_OF_YEAR, -1); + var yesterday = dayKeyFormat.format(cal.getTime()); + + String lastDayKey = null; + for (var drink : drinks) { + var date = new Date(drink.createdAt() * 1000); + var dayKey = dayKeyFormat.format(date); + if (!dayKey.equals(lastDayKey)) { + if (dayKey.equals(today)) { + items.add(todayLabel); + } else if (dayKey.equals(yesterday)) { + items.add(yesterdayLabel); + } else { + items.add(dayDisplayFormat.format(date)); + } + lastDayKey = dayKey; + } + items.add(drink); + } + notifyDataSetChanged(); + } + + @Override + public int getCount() { + return items.size(); + } + + @Override + public Object getItem(int position) { + return items.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public int getViewTypeCount() { + return 2; + } + + @Override + public int getItemViewType(int position) { + return items.get(position) instanceof Drink ? VIEW_TYPE_DRINK : VIEW_TYPE_HEADER; + } + + @Override + public boolean isEnabled(int position) { + return false; + } + + private int getIconResource(int type) { + return switch (type) { + case Drink.TYPE_BEER -> R.drawable.ic_glass_mug; + case Drink.TYPE_WINE -> R.drawable.ic_glass_wine; + case Drink.TYPE_LIQUEUR -> R.drawable.ic_cup; + default -> 0; + }; + } + + private String getTypeString(int type) { + return switch (type) { + case Drink.TYPE_BEER -> context.getString(R.string.drink_type_beer); + case Drink.TYPE_WINE -> context.getString(R.string.drink_type_wine); + case Drink.TYPE_LIQUEUR -> context.getString(R.string.drink_type_liqueur); + default -> "Unknown"; + }; + } + + @Override + public View getView(int position, @Nullable View convertView, @SuppressWarnings("null") ViewGroup parent) { + if (getItemViewType(position) == VIEW_TYPE_HEADER) { + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.item_drink_section_header, parent, false); + } + ((TextView)Objects.requireNonNull(convertView)).setText((String)items.get(position)); + return convertView; + } + + DrinkViewHolder viewHolder; + if (convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.item_drink, parent, false); + viewHolder = new DrinkViewHolder(); + viewHolder.drinkIcon = Objects.requireNonNull(convertView).findViewById(R.id.drink_icon); + viewHolder.drinkType = convertView.findViewById(R.id.drink_type); + viewHolder.drinkTime = convertView.findViewById(R.id.drink_datetime); + viewHolder.drinkMenuButton = convertView.findViewById(R.id.drink_menu_button); + convertView.setTag(viewHolder); + } else { + viewHolder = (DrinkViewHolder)convertView.getTag(); + } + + var drink = (Drink)items.get(position); + viewHolder.drinkIcon.setImageResource(getIconResource(drink.type())); + viewHolder.drinkType.setText(getTypeString(drink.type())); + viewHolder.drinkTime.setText(timeFormat.format(new Date(drink.createdAt() * 1000))); + + viewHolder.drinkMenuButton.setOnClickListener(view -> { + var menu = new PopupMenu(context, view, Gravity.END); + menu.getMenuInflater().inflate(R.menu.item_drink_options, menu.getMenu()); + menu.setOnMenuItemClickListener(item -> { + if (item.getItemId() == R.id.menu_item_drink_delete) { + dbHelper.deleteDrink(drink.id()); + if (onDeleteListener != null) { + onDeleteListener.onDrinkDeleted(); + } + return true; + } + return false; + }); + menu.show(); + }); + + return convertView; + } +} diff --git a/bin/drankcounter/src/nl/plaatsoft/drankcounter/models/Drink.java b/bin/drankcounter/src/nl/plaatsoft/drankcounter/models/Drink.java new file mode 100644 index 00000000..405bf3c6 --- /dev/null +++ b/bin/drankcounter/src/nl/plaatsoft/drankcounter/models/Drink.java @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2026 Bastiaan van der Plaat + * + * SPDX-License-Identifier: MIT + */ + +package nl.plaatsoft.drankcounter.models; + +public record Drink(long id, int type, long createdAt) { + public static final int TYPE_BEER = 0; + public static final int TYPE_WINE = 1; + public static final int TYPE_LIQUEUR = 2; +}