diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..ca17e4f
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..30c8cce
Binary files /dev/null and b/android/app/src/main/res/drawable-hdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..9c98ad5
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..252c319
Binary files /dev/null and b/android/app/src/main/res/drawable-mdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..679d576
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..e9c062d
Binary files /dev/null and b/android/app/src/main/res/drawable-xhdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..cd943f7
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..490c199
Binary files /dev/null and b/android/app/src/main/res/drawable-xxhdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 0000000..2372679
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png
new file mode 100644
index 0000000..6623284
Binary files /dev/null and b/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_monochrome.png differ
diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 0000000..d506366
--- /dev/null
+++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
index db77bb4..27b9a78 100644
Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
index 17987b7..97a48f9 100644
Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
index 09d4391..5c34c68 100644
Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
index d5f1c8d..3e404b0 100644
Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
index 4d6372e..efa79eb 100644
Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/android/app/src/main/res/values/colors.xml b/android/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..610f420
--- /dev/null
+++ b/android/app/src/main/res/values/colors.xml
@@ -0,0 +1,4 @@
+
+
+ #1B1D21
+
\ No newline at end of file
diff --git a/assets/icon/icon.png b/assets/icon/icon.png
new file mode 100644
index 0000000..439cf86
Binary files /dev/null and b/assets/icon/icon.png differ
diff --git a/assets/icon/icon.svg b/assets/icon/icon.svg
new file mode 100644
index 0000000..4fcb929
--- /dev/null
+++ b/assets/icon/icon.svg
@@ -0,0 +1,9 @@
+
diff --git a/assets/icon/icon_foreground.png b/assets/icon/icon_foreground.png
new file mode 100644
index 0000000..b425862
Binary files /dev/null and b/assets/icon/icon_foreground.png differ
diff --git a/assets/icon/icon_foreground.svg b/assets/icon/icon_foreground.svg
new file mode 100644
index 0000000..89731a1
--- /dev/null
+++ b/assets/icon/icon_foreground.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/icon/icon_monochrome.png b/assets/icon/icon_monochrome.png
new file mode 100644
index 0000000..87e2855
Binary files /dev/null and b/assets/icon/icon_monochrome.png differ
diff --git a/assets/icon/icon_monochrome.svg b/assets/icon/icon_monochrome.svg
new file mode 100644
index 0000000..c278c8f
--- /dev/null
+++ b/assets/icon/icon_monochrome.svg
@@ -0,0 +1,8 @@
+
diff --git a/linux/icons/128/todo.png b/linux/icons/128/todo.png
new file mode 100644
index 0000000..8c080b9
Binary files /dev/null and b/linux/icons/128/todo.png differ
diff --git a/linux/icons/16/todo.png b/linux/icons/16/todo.png
new file mode 100644
index 0000000..06b86e6
Binary files /dev/null and b/linux/icons/16/todo.png differ
diff --git a/linux/icons/24/todo.png b/linux/icons/24/todo.png
new file mode 100644
index 0000000..3c0b1a5
Binary files /dev/null and b/linux/icons/24/todo.png differ
diff --git a/linux/icons/256/todo.png b/linux/icons/256/todo.png
new file mode 100644
index 0000000..0efb2a1
Binary files /dev/null and b/linux/icons/256/todo.png differ
diff --git a/linux/icons/32/todo.png b/linux/icons/32/todo.png
new file mode 100644
index 0000000..3c45b30
Binary files /dev/null and b/linux/icons/32/todo.png differ
diff --git a/linux/icons/48/todo.png b/linux/icons/48/todo.png
new file mode 100644
index 0000000..defd2a4
Binary files /dev/null and b/linux/icons/48/todo.png differ
diff --git a/linux/icons/512/todo.png b/linux/icons/512/todo.png
new file mode 100644
index 0000000..a9cc144
Binary files /dev/null and b/linux/icons/512/todo.png differ
diff --git a/linux/icons/64/todo.png b/linux/icons/64/todo.png
new file mode 100644
index 0000000..76d01f8
Binary files /dev/null and b/linux/icons/64/todo.png differ
diff --git a/linux/install_desktop_entry.sh b/linux/install_desktop_entry.sh
new file mode 100755
index 0000000..f10bf5f
--- /dev/null
+++ b/linux/install_desktop_entry.sh
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+# ============================================================================
+# Install the todo launcher icon and .desktop entry for the Linux desktop.
+#
+# Flutter's GTK runner has no icon of its own: my_application.cc calls
+# gtk_window_set_icon_name("todo"), which resolves through the hicolor icon
+# theme. This script is what puts the icon into that theme, plus a .desktop
+# entry so the app appears in application menus.
+#
+# Icon PNGs are pre-rendered and committed under linux/icons/, so this script
+# needs no image tooling at run time. Regenerate them with:
+# PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons \
+# generate --app todo --linux-out linux/icons
+# ============================================================================
+
+set -euo pipefail
+
+SCRIPT_NAME="$(basename "$0")"
+LINUX_DIR="$(cd "$(dirname "$0")" && pwd)"
+REPO_DIR="$(dirname "$LINUX_DIR")"
+readonly SCRIPT_NAME LINUX_DIR REPO_DIR
+readonly ICON_NAME="todo"
+readonly BINARY_NAME="todo"
+# GTK derives WM_CLASS from the GApplication id in linux/CMakeLists.txt,
+# not from the binary name; verified with xprop on the running window.
+readonly WM_CLASS="dev.kuhy.todo"
+readonly ICON_SRC_DIR="$LINUX_DIR/icons"
+readonly ICON_THEME_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons/hicolor"
+readonly DESKTOP_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/applications"
+readonly BUNDLE_BIN="$REPO_DIR/build/linux/x64/release/bundle/$BINARY_NAME"
+
+usage() {
+ echo "Usage: $SCRIPT_NAME"
+ echo "Installs the todo icon into the hicolor theme and writes a .desktop entry."
+ exit 0
+}
+
+validate_requirements() {
+ if [[ ! -d "$ICON_SRC_DIR" ]]; then
+ echo "Error: $ICON_SRC_DIR is missing; regenerate the icons first" >&2
+ exit 1
+ fi
+}
+
+ensure_theme_index() {
+ # A user-local hicolor tree created from scratch has no index.theme, and
+ # without it gtk-update-icon-cache refuses to build a cache ("No theme
+ # index file"). Seed it from the system theme when one is available.
+ local system_index="/usr/share/icons/hicolor/index.theme"
+ if [[ ! -f "$ICON_THEME_DIR/index.theme" && -f "$system_index" ]]; then
+ install -Dm644 "$system_index" "$ICON_THEME_DIR/index.theme"
+ fi
+}
+
+install_icons() {
+ local size_dir size icon
+ for size_dir in "$ICON_SRC_DIR"/*/; do
+ size="$(basename "$size_dir")"
+ icon="$size_dir$ICON_NAME.png"
+ [[ -f "$icon" ]] || continue
+ install -Dm644 "$icon" \
+ "$ICON_THEME_DIR/${size}x${size}/apps/$ICON_NAME.png"
+ done
+ # Refresh the theme cache so GTK picks the icon up without a re-login.
+ # Harmless if the tool is absent: the icon still resolves, just slower.
+ if command -v gtk-update-icon-cache >/dev/null; then
+ gtk-update-icon-cache --quiet --force "$ICON_THEME_DIR" || true
+ fi
+}
+
+install_desktop_entry() {
+ mkdir -p "$DESKTOP_DIR"
+ # StartupWMClass must match the window's actual WM class, or the taskbar
+ # shows a generic icon for the running window even though the launcher
+ # entry itself looks right.
+ cat > "$DESKTOP_DIR/$ICON_NAME.desktop" </dev/null; then
+ update-desktop-database "$DESKTOP_DIR" || true
+ fi
+}
+
+main() {
+ validate_requirements
+ ensure_theme_index
+ install_icons
+ install_desktop_entry
+ echo "Installed $ICON_NAME icon into $ICON_THEME_DIR"
+ echo "Installed $DESKTOP_DIR/$ICON_NAME.desktop"
+ if [[ ! -x "$BUNDLE_BIN" ]]; then
+ echo "Note: $BUNDLE_BIN does not exist yet."
+ echo " Run 'flutter build linux --release' to make the entry launchable."
+ fi
+}
+
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ -h|--help)
+ usage
+ ;;
+ *)
+ echo "Unknown option: $1" >&2
+ exit 1
+ ;;
+ esac
+done
+
+main "$@"
diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc
index 37cfb39..9d02fa3 100644
--- a/linux/runner/my_application.cc
+++ b/linux/runner/my_application.cc
@@ -54,6 +54,11 @@ static void my_application_activate(GApplication* application) {
gtk_window_set_default_size(window, 1280, 720);
+ // Resolved from the hicolor icon theme, which linux/install_desktop_entry.sh
+ // populates from linux/icons/. Without this the GTK runner shows a generic
+ // placeholder in the taskbar, since Flutter never sets a window icon itself.
+ gtk_window_set_icon_name(window, "todo");
+
g_autoptr(FlDartProject) project = fl_dart_project_new();
fl_dart_project_set_dart_entrypoint_arguments(
project, self->dart_entrypoint_arguments);
diff --git a/pubspec.lock b/pubspec.lock
index 6f116ea..fa49fbe 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,6 +1,14 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
+ archive:
+ dependency: transitive
+ description:
+ name: archive
+ sha256: a96e8b390886ee8abb49b7bd3ac8df6f451c621619f52a26e815fdcf568959ff
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.0.9"
args:
dependency: transitive
description:
@@ -41,6 +49,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
+ checked_yaml:
+ dependency: transitive
+ description:
+ name: checked_yaml
+ sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
+ url: "https://pub.dev"
+ source: hosted
+ version: "2.0.4"
+ cli_util:
+ dependency: transitive
+ description:
+ name: cli_util
+ sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.4.2"
clock:
dependency: transitive
description:
@@ -223,6 +247,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_launcher_icons:
+ dependency: "direct dev"
+ description:
+ name: flutter_launcher_icons
+ sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
+ url: "https://pub.dev"
+ source: hosted
+ version: "0.14.4"
flutter_lints:
dependency: "direct dev"
description:
@@ -321,6 +353,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "4.1.2"
+ image:
+ dependency: transitive
+ description:
+ name: image
+ sha256: "6300175e00616bbc832e2fc91bfa4d776af5402c81c7151bee6905bb08473c52"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.9.1"
jni:
dependency: transitive
description:
@@ -337,6 +377,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.1"
+ json_annotation:
+ dependency: transitive
+ description:
+ name: json_annotation
+ sha256: "2a743920d81b7910627f68ee2c9ac1fc0bfee32b9fc3403587d7c6791ca12f80"
+ url: "https://pub.dev"
+ source: hosted
+ version: "4.12.0"
leak_tracker:
dependency: transitive
description:
@@ -489,6 +537,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.0"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.2"
platform:
dependency: transitive
description:
@@ -513,6 +569,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.5.2"
+ posix:
+ dependency: transitive
+ description:
+ name: posix
+ sha256: "185ef7606574f789b40f289c233efa52e96dead518aed988e040a10737febb07"
+ url: "https://pub.dev"
+ source: hosted
+ version: "6.5.0"
process_run:
dependency: transitive
description:
@@ -846,6 +910,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4"
+ url: "https://pub.dev"
+ source: hosted
+ version: "7.0.1"
yaml:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index a2b574e..f572762 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -57,6 +57,9 @@ dependencies:
# libsecret) instead of plaintext SharedPreferences.
flutter_secure_storage: ^10.3.1
dev_dependencies:
+ # Generates the Android launcher icons from assets/icon/ (see
+ # python_pkg/app_icons in testsAndMisc for the shared style).
+ flutter_launcher_icons: ^0.14.4
flutter_test:
sdk: flutter
@@ -117,3 +120,15 @@ flutter:
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/to/font-from-package
+
+# Launcher icon generation. Source art is produced by the shared generator:
+# PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app
+# then run: dart run flutter_launcher_icons
+flutter_launcher_icons:
+ android: true
+ ios: false
+ image_path: "assets/icon/icon.png"
+ adaptive_icon_background: "#1B1D21"
+ adaptive_icon_foreground: "assets/icon/icon_foreground.png"
+ adaptive_icon_monochrome: "assets/icon/icon_monochrome.png"
+ min_sdk_android: 21