├── .devcontainer ├── Dockerfile ├── build-in-container.sh └── devcontainer.json ├── .github ├── FUNDING.yml ├── release.yml └── workflows │ ├── build-android.yml │ ├── build-linux.yml │ ├── build-website.yml │ ├── build-windows.yml │ ├── flatpak.yml │ ├── pre-release.yml │ ├── pull_request.yml │ ├── release-android.yml │ ├── release-windows-store.yml │ ├── release.yml │ └── tests.yml ├── .gitignore ├── .metadata ├── .vscode ├── launch.json ├── settings.json └── tasks.json ├── BUILDING.md ├── LICENSE ├── PRIVACY_POLICY.md ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle.kts │ ├── google-services.json │ ├── proguard-rules.pro │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── adventure_list │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ ├── app_widget_background.xml │ │ │ ├── app_widget_inner_view_background.xml │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ ├── app_icon.png │ │ │ ├── launch_background.xml │ │ │ └── widget_background.xml │ │ │ ├── layout │ │ │ └── example_layout.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── raw │ │ │ └── keep.xml │ │ │ ├── values-night-v31 │ │ │ └── themes.xml │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ ├── values-v21 │ │ │ └── styles.xml │ │ │ ├── values-v31 │ │ │ ├── styles.xml │ │ │ └── themes.xml │ │ │ ├── values │ │ │ ├── attrs.xml │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── themes.xml │ │ │ └── xml │ │ │ └── home_widget_example.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle.kts ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle.kts ├── assets ├── fonts │ ├── NotoSans-Regular.ttf │ └── OFL.txt └── icons │ ├── codes.merritt.adventurelist-symbolic-with-notification-badge.ico │ ├── codes.merritt.adventurelist-symbolic-with-notification-badge.svg │ ├── codes.merritt.adventurelist-symbolic.ico │ ├── codes.merritt.adventurelist-symbolic.svg │ ├── codes.merritt.adventurelist-with-notification-badge.ico │ ├── codes.merritt.adventurelist-with-notification-badge.svg │ ├── codes.merritt.adventurelist.Source.svg │ ├── codes.merritt.adventurelist.ico │ ├── codes.merritt.adventurelist.png │ └── codes.merritt.adventurelist.svg ├── build.yaml ├── devtools_options.yaml ├── lib ├── firebase_options.dart ├── main.dart └── src │ ├── app.dart │ ├── app │ ├── app.dart │ └── cubit │ │ ├── app_cubit.dart │ │ ├── app_state.dart │ │ └── cubit.dart │ ├── authentication │ ├── authentication.dart │ ├── cubit │ │ ├── authentication_cubit.dart │ │ └── authentication_state.dart │ ├── google_auth.dart │ └── sign_in_page.dart │ ├── autostart │ └── autostart_service.dart │ ├── background_tasks │ ├── background_tasks.dart │ └── background_tasks_service.dart │ ├── core │ ├── constants.dart │ ├── core.dart │ ├── helpers │ │ ├── date_time.dart │ │ ├── error_handler.dart │ │ └── helpers.dart │ └── widgets │ │ ├── border.dart │ │ ├── input_dialog.dart │ │ ├── measure_size.dart │ │ └── widgets.dart │ ├── home_widget │ ├── home_widget.dart │ ├── home_widget_manager.dart │ └── widgets │ │ ├── home_screen_widget.dart │ │ ├── home_widget_config_page.dart │ │ └── widgets.dart │ ├── logs │ ├── logging_manager.dart │ └── src │ │ ├── log_file_service.dart │ │ └── src.dart │ ├── notifications │ ├── cubit │ │ ├── notifications_cubit.dart │ │ └── notifications_state.dart │ ├── models │ │ ├── models.dart │ │ └── notification.dart │ └── notifications.dart │ ├── settings │ ├── cubit │ │ ├── cubit.dart │ │ ├── settings_cubit.dart │ │ └── settings_state.dart │ ├── models │ │ ├── desktop_widget_settings.dart │ │ └── models.dart │ ├── settings.dart │ └── widgets │ │ ├── export_data_page.dart │ │ ├── settings_page.dart │ │ └── widgets.dart │ ├── shortcuts │ ├── app_shortcuts.dart │ └── shortcuts_manager.dart │ ├── storage │ ├── helpers.dart │ ├── storage.dart │ └── storage_repository.dart │ ├── system_tray │ └── system_tray_manager.dart │ ├── tasks │ ├── commands │ │ ├── command.dart │ │ ├── commands.dart │ │ ├── delete_completed_tasks_command.dart │ │ ├── delete_task_command.dart │ │ └── set_task_completed_command.dart │ ├── cubit │ │ ├── tasks_cubit.dart │ │ └── tasks_state.dart │ ├── enums │ │ ├── enums.dart │ │ └── recurrence_end_type.dart │ ├── helpers │ │ ├── date_time.dart │ │ ├── frequency.dart │ │ ├── helpers.dart │ │ ├── recurrence_rule.dart │ │ └── weekdays.dart │ ├── models │ │ ├── models.dart │ │ ├── task.dart │ │ └── task_list.dart │ ├── repository │ │ ├── repository.dart │ │ ├── src │ │ │ ├── google_calendar.dart │ │ │ └── src.dart │ │ └── tasks_repository.dart │ ├── services │ │ ├── recurrence_rule_service.dart │ │ └── services.dart │ ├── tasks.dart │ ├── validators │ │ ├── task_list_validator.dart │ │ └── validators.dart │ └── widgets │ │ ├── navigation_area.dart │ │ ├── task_details │ │ ├── recurrence_widget.dart │ │ ├── sub_tasks_list_widget.dart │ │ ├── task_details.dart │ │ └── task_details_widget.dart │ │ ├── task_tile.dart │ │ ├── tasks_page.dart │ │ ├── tasks_view.dart │ │ └── widgets.dart │ ├── theme │ ├── app_theme.dart │ └── theme.dart │ ├── updates │ ├── models │ │ ├── models.dart │ │ └── version_info.dart │ ├── update_service.dart │ └── updates.dart │ └── window │ ├── app_window.dart │ ├── cubit │ ├── cubit.dart │ ├── window_cubit.dart │ └── window_state.dart │ └── window.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ └── CMakeLists.txt ├── main.cc ├── my_application.cc └── my_application.h ├── packaging ├── linux │ ├── codes.merritt.adventurelist.desktop │ └── codes.merritt.adventurelist.metainfo.xml └── windows │ └── inno_setup.iss ├── pubspec.lock ├── pubspec.yaml ├── test └── src │ ├── background_tasks │ └── background_tasks_service_test.dart │ └── tasks │ ├── cubit │ └── tasks_cubit_test.dart │ ├── helpers │ └── recurrence_rule_test.dart │ ├── models │ ├── task_list_test.dart │ └── task_test.dart │ ├── repository │ └── src │ │ └── google_calendar_test.dart │ ├── validators │ └── task_list_validator_test.dart │ └── widgets │ └── task_details │ ├── recurrence_widget_test.dart │ └── task_details_widget_test.dart ├── translations ├── de.json ├── en.json ├── it.json └── pt-BR.json ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png ├── index.html └── manifest.json ├── website ├── .gitignore ├── README.md ├── analysis_options.yaml ├── l10n.yaml ├── lib │ ├── main.dart │ └── src │ │ ├── app.dart │ │ ├── localization │ │ └── app_en.arb │ │ ├── pages │ │ ├── home_page.dart │ │ └── privacy_policy.dart │ │ ├── sample_feature │ │ ├── sample_item.dart │ │ ├── sample_item_details_view.dart │ │ └── sample_item_list_view.dart │ │ ├── settings │ │ ├── settings_controller.dart │ │ ├── settings_service.dart │ │ └── settings_view.dart │ │ └── widgets │ │ └── custom_app_bar.dart ├── linux │ ├── .gitignore │ ├── CMakeLists.txt │ ├── flutter │ │ └── CMakeLists.txt │ ├── main.cc │ ├── my_application.cc │ └── my_application.h ├── pubspec.lock ├── pubspec.yaml ├── static │ ├── index.html │ └── privacy_policy.html └── web │ ├── favicon.png │ ├── icons │ ├── Icon-192.png │ ├── Icon-512.png │ ├── Icon-maskable-192.png │ └── Icon-maskable-512.png │ ├── index.html │ └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter └── CMakeLists.txt └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/base:ubuntu-20.04 2 | 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | 5 | 6 | # ------------------------ Prepare docker environment ------------------------ # 7 | 8 | RUN apt-get update && \ 9 | # Install general utilities 10 | apt-get -y install tree && \ 11 | # Install Flutter dependencies 12 | apt-get -y install curl file git unzip xz-utils zip clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev && \ 13 | # Install app-specific dependencies 14 | apt-get -y install keybinder-3.0 appindicator3-0.1 libappindicator3-1 libappindicator3-dev libnotify-dev 15 | 16 | 17 | # ------------------------------ Install Flutter ----------------------------- # 18 | 19 | RUN git clone https://github.com/flutter/flutter.git -b stable /home/vscode/flutter 20 | RUN git config --global --add safe.directory /home/vscode/flutter 21 | ENV PATH="$PATH:/home/vscode/flutter/bin" 22 | RUN flutter upgrade 23 | RUN chown -R vscode:vscode /home/vscode/flutter 24 | 25 | 26 | # ------------------------------ Install Flatpak ----------------------------- # 27 | 28 | RUN apt-get install -y flatpak flatpak-builder 29 | RUN flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo 30 | RUN flatpak install -y org.freedesktop.Sdk/x86_64/21.08 31 | RUN flatpak install -y org.freedesktop.Platform/x86_64/21.08 32 | RUN flatpak install -y flathub org.freedesktop.appstream-glib 33 | -------------------------------------------------------------------------------- /.devcontainer/build-in-container.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | # When run from the vscode dev container this will build and package the app. 5 | 6 | 7 | set -x 8 | 9 | 10 | projectName=AdventureList 11 | archiveName=$projectName-Linux-Portable.tar.gz 12 | 13 | # ----------------------------- Build Flutter app ---------------------------- # 14 | 15 | flutter pub get 16 | flutter build linux 17 | 18 | workspace=$PWD 19 | cd build/linux/x64/release/bundle || exit 20 | touch PORTABLE 21 | tar -czaf $archiveName ./* 22 | cp -r $archiveName "$workspace"/ 23 | cd "$workspace" || exit 24 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu 3 | { 4 | "name": "Ubuntu", 5 | 6 | // "image": "mcr.microsoft.com/devcontainers/base:focal", 7 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 8 | "build": { 9 | "dockerfile": "Dockerfile" 10 | }, 11 | 12 | // Features to add to the dev container. More info: https://containers.dev/features. 13 | "features": { 14 | "ghcr.io/rocker-org/devcontainer-features/apt-packages:1": {} 15 | }, 16 | 17 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 18 | // "forwardPorts": [], 19 | 20 | // Use 'postCreateCommand' to run commands after the container is created. 21 | // "postCreateCommand": "", 22 | 23 | // Configure tool-specific properties. 24 | // "customizations": {}, 25 | 26 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 27 | // "remoteUser": "root" 28 | 29 | "mounts": [ 30 | "source=/home/merritt/Development,target=/development,type=bind,consistency=cached" 31 | ], 32 | 33 | "privileged": true, 34 | 35 | "remoteEnv": { 36 | // Add Flutter to the PATH variable. 37 | "PATH": "${containerEnv:PATH}:/home/vscode/flutter/bin" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: Merrit 2 | ko_fi: merrit 3 | custom: ["https://paypal.me/KristenMcWilliam", "https://www.buymeacoffee.com/Merritt"] 4 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | changelog: 2 | exclude: 3 | labels: 4 | - ignore-for-release 5 | categories: 6 | - title: Breaking Changes 🛠 7 | labels: 8 | - Semver-Major 9 | - breaking-change 10 | - title: New Features 🎉 11 | labels: 12 | - Semver-Minor 13 | - enhancement 14 | - title: Fixes ✨ 15 | labels: 16 | - bugfix 17 | - title: Code Cleanup 🪥 18 | labels: 19 | - cleanup 20 | - title: Other Changes 21 | labels: 22 | - "*" 23 | -------------------------------------------------------------------------------- /.github/workflows/build-android.yml: -------------------------------------------------------------------------------- 1 | name: Build Android 2 | on: 3 | # Enable manual run 4 | workflow_dispatch: 5 | # Allow being called by other workflows 6 | workflow_call: 7 | inputs: 8 | pre-release: 9 | description: "Whether the build is for a pre-release" 10 | required: false 11 | default: false 12 | type: boolean 13 | 14 | env: 15 | app-display-name: "Adventure List" 16 | author: "Merritt Codes" 17 | identifier: "codes.merritt.adventurelist" 18 | 19 | jobs: 20 | build-android: 21 | name: Build Android 22 | runs-on: ubuntu-latest 23 | 24 | # ----------------------------------- Setup ------------------------------ # 25 | 26 | steps: 27 | - name: Setup Flutter 28 | uses: subosito/flutter-action@v2 29 | 30 | - name: Install Android dependencies 31 | uses: actions/setup-java@v4 32 | with: 33 | distribution: 'zulu' 34 | java-version: '17' 35 | 36 | - name: Checkout code 37 | uses: actions/checkout@v4 38 | 39 | - name: Configure Keystore for Android 40 | run: | 41 | echo "$PLAY_STORE_UPLOAD_KEY" | base64 --decode > app/upload-keystore.jks 42 | echo "storeFile=upload-keystore.jks" >> key.properties 43 | echo "keyAlias=$KEYSTORE_KEY_ALIAS" >> key.properties 44 | echo "storePassword=$KEYSTORE_STORE_PASSWORD" >> key.properties 45 | echo "keyPassword=$KEYSTORE_KEY_PASSWORD" >> key.properties 46 | env: 47 | PLAY_STORE_UPLOAD_KEY: ${{ secrets.PLAY_STORE_UPLOAD_KEY }} 48 | KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }} 49 | KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} 50 | KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} 51 | working-directory: android 52 | 53 | - name: Prepare for build 54 | run: | 55 | flutter pub get 56 | 57 | # ----------------------------------- Build ---------------------------- # 58 | 59 | - name: Generate localizations 60 | run: dart run easy_localization:generate --source-dir translations --output-dir lib/generated --output-file locale_keys.g.dart --format keys --skip-unnecessary-keys 61 | 62 | - name: Run build script 63 | env: 64 | GITHUB_TOKEN: ${{ secrets.RELEASES_TOKEN }} 65 | run: flutter pub run flutter_app_builder -v --platforms=android 66 | 67 | # ---------------------------------- Upload ---------------------------- # 68 | 69 | - name: Upload artifacts to workflow 70 | uses: actions/upload-artifact@v4 71 | with: 72 | name: android-artifacts 73 | path: output/* 74 | -------------------------------------------------------------------------------- /.github/workflows/build-linux.yml: -------------------------------------------------------------------------------- 1 | name: Build Linux 2 | on: 3 | # Enable manual run 4 | workflow_dispatch: 5 | # Allow being called by other workflows 6 | workflow_call: 7 | inputs: 8 | pre-release: 9 | description: "Whether the build is for a pre-release" 10 | required: false 11 | default: false 12 | type: boolean 13 | 14 | env: 15 | app-display-name: "Adventure List" 16 | author: "Merritt Codes" 17 | identifier: "codes.merritt.adventurelist" 18 | 19 | jobs: 20 | build-linux: 21 | name: Build Linux 22 | runs-on: ubuntu-20.04 23 | 24 | # ----------------------------------- Setup ------------------------------ # 25 | 26 | steps: 27 | - name: Setup Linux build requirements 28 | run: | 29 | sudo apt-get update 30 | # libappindicator required for tray_manager 31 | sudo apt-get install appindicator3-0.1 libappindicator3-dev 32 | # libnotify-dev required for notifications, only until the 33 | # following issue is resolved: 34 | # https://github.com/MaikuB/flutter_local_notifications/issues/746 35 | sudo apt-get install libnotify-dev 36 | # keybinder required for global hotkeys 37 | sudo apt-get install keybinder-3.0 38 | 39 | - name: Set pre-release environment variable 40 | if: inputs.pre-release == true 41 | run: echo "prerelease=true" >> $GITHUB_ENV 42 | 43 | - name: Setup Flutter 44 | uses: subosito/flutter-action@v2 45 | 46 | - name: Checkout code 47 | uses: actions/checkout@v4 48 | 49 | - name: Prepare for build 50 | run: | 51 | flutter config --enable-linux-desktop 52 | flutter pub get 53 | 54 | # ----------------------------------- Build ---------------------------- # 55 | 56 | - name: Generate localizations 57 | run: dart run easy_localization:generate --source-dir translations --output-dir lib/generated --output-file locale_keys.g.dart --format keys --skip-unnecessary-keys 58 | 59 | - name: Run build script 60 | env: 61 | GITHUB_TOKEN: ${{ secrets.RELEASES_TOKEN }} 62 | run: flutter pub run flutter_app_builder -v --platforms=linux 63 | 64 | # ---------------------------------- Upload ---------------------------- # 65 | 66 | - name: Upload artifacts to workflow 67 | uses: actions/upload-artifact@v4 68 | with: 69 | name: linux-artifacts 70 | path: output/* 71 | -------------------------------------------------------------------------------- /.github/workflows/build-website.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - disabledworkflow 5 | 6 | jobs: 7 | nonejob: 8 | runs-on: ubuntu-20.04 9 | steps: 10 | - run: donothingdisabled 11 | # Currently resides in and manages by the `website` branch. 12 | 13 | # # Automatically build and deploy website to GitHub Pages. 14 | 15 | # name: Build Website 16 | 17 | # on: 18 | # push: 19 | # branches: 20 | # - main 21 | # pull_request: 22 | 23 | # jobs: 24 | # deploy: 25 | # runs-on: ubuntu-20.04 26 | # permissions: 27 | # contents: write 28 | # concurrency: 29 | # group: ${{ github.workflow }}-${{ github.ref }} 30 | # steps: 31 | # - uses: actions/checkout@v4 32 | 33 | # # Using static site for now as it seems required to verify with Google 34 | # # Cloud Console. ¯\_(ツ)_/¯ 35 | # - name: Deploy 36 | # uses: peaceiris/actions-gh-pages@v3 37 | # if: ${{ github.ref == 'refs/heads/main' }} 38 | # with: 39 | # github_token: ${{ secrets.GITHUB_TOKEN }} 40 | # publish_dir: ${{ github.workspace }}/website/static 41 | # cname: adventurelist.merritt.codes 42 | 43 | # # - name: Setup Flutter 44 | # # run: | 45 | # # git clone https://github.com/flutter/flutter.git --depth 1 -b stable _flutter 46 | # # echo "${GITHUB_WORKSPACE}/_flutter/bin" >> ${GITHUB_PATH} 47 | 48 | # # - name: Install 49 | # # run: | 50 | # # flutter config --enable-web 51 | # # flutter pub get 52 | 53 | # # - name: Build 54 | # # run: flutter build web 55 | # # working-directory: ${{ github.workspace }}/website 56 | 57 | # # - name: Deploy 58 | # # uses: peaceiris/actions-gh-pages@v3 59 | # # if: ${{ github.ref == 'refs/heads/main' }} 60 | # # with: 61 | # # github_token: ${{ secrets.GITHUB_TOKEN }} 62 | # # publish_dir: ${{ github.workspace }}/website/build/web 63 | # # cname: adventurelist.merritt.codes 64 | -------------------------------------------------------------------------------- /.github/workflows/build-windows.yml: -------------------------------------------------------------------------------- 1 | name: Build Windows 2 | on: 3 | # Enable manual run 4 | workflow_dispatch: 5 | # Allow being called by other workflows 6 | workflow_call: 7 | inputs: 8 | pre-release: 9 | description: "Whether the build is for a pre-release" 10 | required: false 11 | default: false 12 | type: boolean 13 | 14 | env: 15 | app-display-name: "Adventure List" 16 | author: "Merritt Codes" 17 | identifier: "codes.merritt.adventurelist" 18 | msix-icon-path: "assets\\icons\\codes.merritt.adventurelist.png" 19 | 20 | jobs: 21 | build-windows: 22 | name: Build Windows 23 | runs-on: windows-latest 24 | 25 | # ----------------------------------- Setup ------------------------------ # 26 | 27 | steps: 28 | - name: Set pre-release environment variable 29 | if: inputs.pre-release == true 30 | run: echo "prerelease=true" >> $GITHUB_ENV 31 | 32 | - name: Setup Flutter 33 | uses: subosito/flutter-action@v2 34 | 35 | - name: Checkout code 36 | uses: actions/checkout@v4 37 | 38 | - name: Prepare for build 39 | run: | 40 | # Fix error for git packages with long names 41 | git config --system core.longpaths true 42 | # Setup Flutter 43 | flutter config --enable-windows-desktop 44 | flutter pub get 45 | 46 | # ----------------------------------- Build ---------------------------- # 47 | 48 | - name: Generate localizations 49 | run: dart run easy_localization:generate --source-dir translations --output-dir lib/generated --output-file locale_keys.g.dart --format keys --skip-unnecessary-keys 50 | 51 | - name: Run build script 52 | env: 53 | GITHUB_TOKEN: ${{ secrets.RELEASES_TOKEN }} 54 | run: flutter pub run flutter_app_builder -v --platforms=windows 55 | 56 | # ---------------------------------- Upload ---------------------------- # 57 | 58 | - name: Upload Windows Store MSIX artifact to workflow 59 | uses: actions/upload-artifact@v4 60 | with: 61 | name: windows-store-artifact 62 | path: output/*.msix 63 | 64 | # MSIX is only for publishing to the Windows Store. 65 | - name: Remove Windows Store artifact from release files 66 | run: rm output/*.msix 67 | 68 | - name: Upload artifacts to workflow 69 | uses: actions/upload-artifact@v4 70 | with: 71 | name: windows-artifacts 72 | path: output/* 73 | -------------------------------------------------------------------------------- /.github/workflows/pre-release.yml: -------------------------------------------------------------------------------- 1 | # Create a new prerelease with every push on main 2 | 3 | name: Pre-Release 4 | 5 | on: 6 | push: 7 | branches: 8 | - "main" 9 | tags-ignore: 10 | - "*" 11 | 12 | concurrency: 13 | group: ci-pre-release-${{ github.ref }}-1 14 | cancel-in-progress: true 15 | 16 | jobs: 17 | call-tests: 18 | uses: ./.github/workflows/tests.yml 19 | 20 | call-build-linux: 21 | needs: call-tests 22 | uses: ./.github/workflows/build-linux.yml 23 | with: 24 | pre-release: true 25 | secrets: inherit 26 | call-build-windows: 27 | needs: call-tests 28 | uses: ./.github/workflows/build-windows.yml 29 | with: 30 | pre-release: true 31 | secrets: inherit 32 | call-build-android: 33 | needs: call-tests 34 | uses: ./.github/workflows/build-android.yml 35 | secrets: inherit 36 | 37 | pre-release: 38 | name: "Pre Release" 39 | needs: [call-build-linux, call-build-windows, call-build-android] 40 | runs-on: "ubuntu-latest" 41 | 42 | steps: 43 | - uses: actions/checkout@v4 44 | 45 | - name: Download artifacts 46 | uses: actions/download-artifact@v4 47 | with: 48 | path: artifacts 49 | 50 | - name: Create Development Release & Upload artifacts 51 | uses: marvinpinto/action-automatic-releases@v1.2.1 52 | with: 53 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 54 | automatic_release_tag: "latest" 55 | prerelease: true 56 | title: "Development Build" 57 | files: | 58 | ${{ github.workspace }}/artifacts/linux-artifacts/* 59 | ${{ github.workspace }}/artifacts/windows-artifacts/* 60 | ${{ github.workspace }}/artifacts/android-artifacts/* 61 | -------------------------------------------------------------------------------- /.github/workflows/pull_request.yml: -------------------------------------------------------------------------------- 1 | # Verify tests pass and builds succeed for pull requests. 2 | 3 | name: Verify Pull Request 4 | 5 | on: 6 | # Enable manual run 7 | workflow_dispatch: 8 | pull_request: 9 | 10 | concurrency: 11 | group: ci-verify-pr-${{ github.ref }}-1 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | call-tests: 16 | uses: ./.github/workflows/tests.yml 17 | 18 | call-build-linux: 19 | needs: call-tests 20 | uses: ./.github/workflows/build-linux.yml 21 | with: 22 | pre-release: true 23 | secrets: inherit 24 | call-build-windows: 25 | needs: call-tests 26 | uses: ./.github/workflows/build-windows.yml 27 | with: 28 | pre-release: true 29 | secrets: inherit 30 | call-build-android: 31 | needs: call-tests 32 | uses: ./.github/workflows/build-android.yml 33 | with: 34 | pre-release: true 35 | secrets: inherit 36 | 37 | verify-pull-request: 38 | name: Verify Pull Request 39 | needs: 40 | [call-tests, call-build-linux, call-build-windows, call-build-android] 41 | runs-on: ubuntu-latest 42 | steps: 43 | - run: echo "Requirements passed, PR looks good!" 44 | -------------------------------------------------------------------------------- /.github/workflows/release-android.yml: -------------------------------------------------------------------------------- 1 | name: Publish Android build to Google Play 2 | 3 | on: 4 | # Enable manual run 5 | workflow_dispatch: 6 | # Build & deploy for published releases 7 | release: 8 | types: 9 | - published 10 | 11 | env: 12 | appname-without-spaces: "AdventureList" 13 | packageName: "codes.merritt.adventurelist" 14 | repository: "merrit/adventure_list" 15 | repositoryUrl: https://github.com/Merrit/adventure_list 16 | 17 | jobs: 18 | release: 19 | runs-on: ubuntu-latest 20 | 21 | steps: 22 | - name: Download .aab artifact 23 | run: wget ${{env.repositoryUrl}}/releases/latest/download/${{env.appname-without-spaces}}-Android.aab 24 | 25 | # Works with the r0adkll/upload-google-play action. 26 | - name: Download changelog 27 | run: | 28 | mkdir whatsNewDirectory 29 | gh release view --json body --jq .body --repo ${{env.repository}} >> whatsNewDirectory/whatsnew-en-US 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | 33 | - name: Access Google Play API key 34 | env: 35 | GOOGLE_PLAY_API_JSON: ${{ secrets.GOOGLE_PLAY_API_JSON }} 36 | run: echo "$GOOGLE_PLAY_API_JSON" | base64 --decode > google_play_api.json 37 | 38 | - name: Release Android build on Google Play 39 | uses: r0adkll/upload-google-play@v1 40 | with: 41 | serviceAccountJson: google_play_api.json 42 | packageName: ${{env.packageName}} 43 | releaseFiles: ${{env.appname-without-spaces}}-Android.aab 44 | track: production 45 | -------------------------------------------------------------------------------- /.github/workflows/release-windows-store.yml: -------------------------------------------------------------------------------- 1 | name: Publish MSIX to Microsoft Store 2 | 3 | on: 4 | # Enable manual run 5 | workflow_dispatch: 6 | # Build & deploy for published releases 7 | release: 8 | types: 9 | - published 10 | 11 | concurrency: 12 | group: ci-release-${{ github.ref }}-1 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | call-build-windows: 17 | uses: ./.github/workflows/build-windows.yml 18 | with: 19 | pre-release: false 20 | secrets: inherit 21 | 22 | docker: 23 | needs: call-build-windows 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Download artifacts 27 | uses: actions/download-artifact@v4 28 | with: 29 | path: artifacts 30 | 31 | - name: Publish to Store 32 | uses: isaacrlevin/windows-store-action@main 33 | with: 34 | tenant-id: ${{ secrets.AZURE_AD_TENANT_ID }} 35 | client-id: ${{ secrets.AZURE_AD_APPLICATION_CLIENT_ID }} 36 | client-secret: ${{ secrets.AZURE_AD_APPLICATION_SECRET }} 37 | # "app-id" is the Store ID as listed in Partner Center 38 | # https://github.com/isaacrlevin/windows-store-action/issues/5#issuecomment-1086893615 39 | app-id: ${{ secrets.MICROSOFT_STORE_APP_ID }} 40 | package-path: "${{ github.workspace }}/artifacts/windows-store-artifact/" 41 | delete-pending: true 42 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # Create a new release 2 | 3 | name: Release 4 | 5 | on: 6 | # Build & deploy for tag events matching v*, i.e. v1.0.0, v20.15.10 7 | push: 8 | tags: 9 | - "v*" 10 | 11 | concurrency: 12 | group: ci-release-${{ github.ref }}-1 13 | cancel-in-progress: true 14 | 15 | jobs: 16 | call-tests: 17 | uses: ./.github/workflows/tests.yml 18 | 19 | call-build-linux: 20 | needs: call-tests 21 | uses: ./.github/workflows/build-linux.yml 22 | with: 23 | pre-release: false 24 | secrets: inherit 25 | call-build-windows: 26 | needs: call-tests 27 | uses: ./.github/workflows/build-windows.yml 28 | with: 29 | pre-release: false 30 | secrets: inherit 31 | call-build-android: 32 | needs: call-tests 33 | uses: ./.github/workflows/build-android.yml 34 | with: 35 | pre-release: false 36 | secrets: inherit 37 | 38 | release: 39 | name: "Release" 40 | needs: [call-build-linux, call-build-windows, call-build-android] 41 | runs-on: "ubuntu-latest" 42 | 43 | steps: 44 | - uses: actions/checkout@v4 45 | 46 | - name: Download artifacts 47 | uses: actions/download-artifact@v4 48 | with: 49 | path: artifacts 50 | 51 | - name: Create Draft Release & Upload artifacts 52 | uses: marvinpinto/action-automatic-releases@v1.2.1 53 | with: 54 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 55 | draft: true 56 | prerelease: false 57 | files: | 58 | ${{ github.workspace }}/artifacts/linux-artifacts/* 59 | ${{ github.workspace }}/artifacts/windows-artifacts/* 60 | ${{ github.workspace }}/artifacts/android-artifacts/* 61 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | # Allow being called by other workflows 5 | workflow_call: 6 | # Allow being called manually 7 | workflow_dispatch: 8 | 9 | jobs: 10 | flutter_test: 11 | name: Run Tests 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | 16 | - name: Setup Flutter 17 | uses: subosito/flutter-action@v2 18 | 19 | - name: Setup 20 | run: | 21 | flutter pub get 22 | 23 | - name: Verify formatting 24 | run: dart format -o none --set-exit-if-changed --line-length=90 . 25 | 26 | - name: Run translations generation 27 | run: dart run easy_localization:generate --source-dir translations --output-dir lib/generated --output-file locale_keys.g.dart --format keys --skip-unnecessary-keys 28 | 29 | - name: Run code generation 30 | run: dart run build_runner build --delete-conflicting-outputs 31 | 32 | - name: Run tests 33 | run: flutter test 34 | 35 | - name: Print directory structure 36 | # Ensure this step runs even after a failure, but not when cancelled. 37 | if: success() || failure() 38 | run: tree -a 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------- # 2 | # Custom # 3 | # ---------------------------------------------------------------------------- # 4 | 5 | # Freezed 6 | *.freezed.dart 7 | *.g.dart 8 | 9 | # Generated code 10 | generated* 11 | Generated* 12 | *.mocks.dart 13 | 14 | # Packaging 15 | output/ 16 | *.tar.gz 17 | 18 | # Test coverage 19 | lcov.info 20 | 21 | 22 | # ---------------------------------------------------------------------------- # 23 | # Default # 24 | # ---------------------------------------------------------------------------- # 25 | 26 | # Miscellaneous 27 | *.class 28 | *.log 29 | *.pyc 30 | *.swp 31 | .DS_Store 32 | .atom/ 33 | .buildlog/ 34 | .history 35 | .svn/ 36 | migrate_working_dir/ 37 | 38 | # IntelliJ related 39 | *.iml 40 | *.ipr 41 | *.iws 42 | .idea/ 43 | 44 | # The .vscode folder contains launch configuration and tasks you configure in 45 | # VS Code which you may wish to be included in version control, so this line 46 | # is commented out by default. 47 | #.vscode/ 48 | 49 | # Flutter/Dart/Pub related 50 | **/doc/api/ 51 | **/ios/Flutter/.last_build_id 52 | .dart_tool/ 53 | .flutter-plugins 54 | .flutter-plugins-dependencies 55 | .packages 56 | .pub-cache/ 57 | .pub/ 58 | /build/ 59 | 60 | 61 | # Web related 62 | lib/generated_plugin_registrant.dart 63 | 64 | # Symbolication related 65 | app.*.symbols 66 | 67 | # Obfuscation related 68 | app.*.map.json 69 | 70 | # Android Studio will place build artifacts here 71 | /android/app/debug 72 | /android/app/profile 73 | /android/app/release 74 | .hugo_build.lock 75 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: "35c388afb57ef061d06a39b537336c87e0e3d1b1" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 17 | base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 18 | - platform: android 19 | create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 20 | base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | // Debug 8 | { 9 | "name": "Debug", 10 | "request": "launch", 11 | "type": "dart", 12 | "program": "lib/main.dart", 13 | "preLaunchTask": "${defaultBuildTask}", 14 | "flutterMode": "debug", 15 | }, 16 | // Debug with verbose logging 17 | { 18 | "name": "Debug verbose", 19 | "request": "launch", 20 | "type": "dart", 21 | "flutterMode": "debug", 22 | "program": "lib/main.dart", 23 | "preLaunchTask": "${defaultBuildTask}", 24 | "toolArgs": [ 25 | "--dart-define", 26 | "VERBOSE=true" 27 | ] 28 | }, 29 | // Debug with verbose logging and verbose Flutter logging 30 | { 31 | "name": "Debug verbose: App + Flutter", 32 | "request": "launch", 33 | "type": "dart", 34 | "flutterMode": "debug", 35 | "program": "lib/main.dart", 36 | "preLaunchTask": "${defaultBuildTask}", 37 | "args": [ 38 | "--dart-entrypoint-args", 39 | "--verbose", 40 | ], 41 | }, 42 | // Debug with arguments 43 | { 44 | "name": "Debug with arguments", 45 | "request": "launch", 46 | "type": "dart", 47 | "flutterMode": "debug", 48 | "program": "lib/main.dart", 49 | "preLaunchTask": "${defaultBuildTask}", 50 | // "args": [ 51 | // "--dart-entrypoint-args", 52 | // "--toggle,--verbose", 53 | // ], 54 | }, 55 | // Profile 56 | { 57 | "name": "Profile", 58 | "request": "launch", 59 | "type": "dart", 60 | "program": "lib/main.dart", 61 | "preLaunchTask": "${defaultBuildTask}", 62 | "flutterMode": "profile", 63 | }, 64 | // Release 65 | { 66 | "name": "Release", 67 | "request": "launch", 68 | "type": "dart", 69 | "program": "lib/main.dart", 70 | "preLaunchTask": "${defaultBuildTask}", 71 | "flutterMode": "release", 72 | }, 73 | // Test current file 74 | { 75 | "name": "Test current file", 76 | "type": "dart", 77 | "request": "launch", 78 | "program": "${file}" 79 | } 80 | ] 81 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "java.configuration.updateBuildConfiguration": "interactive", 3 | "git.detectSubmodules": false, 4 | "git.pullTags": false, 5 | "dart.flutterTestAdditionalArgs": [ 6 | "--coverage" 7 | ], 8 | "dart.lineLength": 90, 9 | "[dart]": { 10 | "editor.rulers": [ 11 | 90 12 | ], 13 | } 14 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | // Code generation (continuous) 5 | { 6 | "label": "build_runner watch", 7 | "detail": "Code generation (continuous)", 8 | "type": "flutter", 9 | "command": "flutter", 10 | "args": [ 11 | "pub", 12 | "run", 13 | "build_runner", 14 | "watch", 15 | "--delete-conflicting-outputs" 16 | ], 17 | "problemMatcher": [ 18 | "$dart-build_runner" 19 | ], 20 | "group": { 21 | "kind": "build", 22 | "isDefault": true 23 | }, 24 | "presentation": { 25 | "panel": "dedicated", 26 | }, 27 | "dependsOn": [ 28 | "easy_localization:generate" 29 | ], 30 | }, 31 | // Translations generation with easy_localization 32 | { 33 | "label": "easy_localization:generate", 34 | "detail": "Translations generation with easy_localization", 35 | "type": "flutter", 36 | "command": "flutter", 37 | "args": [ 38 | "pub", 39 | "run", 40 | "easy_localization:generate", 41 | "--source-dir", 42 | "translations", 43 | "--output-dir", 44 | "lib/generated", 45 | "--output-file", 46 | "locale_keys.g.dart", 47 | "--format", 48 | "keys", 49 | "--skip-unnecessary-keys", 50 | ], 51 | "group": { 52 | "kind": "build", 53 | "isDefault": false 54 | }, 55 | "presentation": { 56 | "reveal": "silent" 57 | }, 58 | } 59 | ] 60 | } -------------------------------------------------------------------------------- /BUILDING.md: -------------------------------------------------------------------------------- 1 | # Building from source 2 | 3 | ## Requirements 4 | 5 | 1. Requires a working instance of [Flutter](https://docs.flutter.dev/get-started/install). 6 | 7 | 2. Linux requires `libappindicator` and `keybinder`. 8 | 9 | Fedora: 10 | 11 | ``` 12 | sudo dnf install libappindicator-gtk3 libappindicator-gtk3-devel keybinder keybinder3 keybinder3-devel 13 | ``` 14 | 15 | Ubuntu: 16 | 17 | ``` 18 | sudo apt-get install appindicator3-0.1 libappindicator3-dev keybinder-3.0 19 | ``` 20 | 21 | 22 | ## Build 23 | 24 | Run these commands from the root directory of the repo: 25 | 26 | 1. `flutter clean` 27 | 2. `flutter pub get` 28 | 3. `dart run build_runner build --delete-conflicting-outputs` 29 | 4. `flutter pub run easy_localization:generate --source-dir translations --output-dir lib/generated --output-file locale_keys.g.dart --format keys --skip-unnecessary-keys` 30 | 5. Run the build command for the desired platform(s): 31 | - `flutter build linux` 32 | - `flutter build windows` 33 | - `flutter build apk --debug` 34 | - `flutter build appbundle --debug` 35 | 36 | 37 | Compiled app location: 38 | 39 | Linux: `build/linux/x64/release/bundle` 40 | 41 | Windows: `build\windows\runner\Release` 42 | 43 | Android APK: `build/app/outputs/flutter-apk/app-release.apk` 44 | 45 | Android App Bundle: `build/app/outputs/bundle/release/app-release.aab` 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | - [About](#about) 2 | - [Screenshots](#screenshots) 3 | - [Download](#download) 4 | - [Roadmap](#roadmap) 5 | 6 | 7 | ## About 8 | 9 | Are you ready for an adventure? Adventure List is a Todo list app that helps you organize your tasks and goals in a fun and easy way. Whether you want to plan a trip, learn a new skill, or just get things done, Adventure List is the app for you. 10 | 11 | Here are some of the features that make Adventure List great: 12 | 13 | - Due dates: Set due dates for your tasks and get reminders when they're due. 14 | - Recurring due dates: Set tasks to recur on a regular basis, so you never forget to do them. 15 | - Notifications: Get notifications when tasks are due, so you can stay on top of your to-do list. 16 | - Android widget: Add a widget to your Android home screen to quickly view your to-do list. 17 | - Desktop widget mode: Pin your to-do list to your computer desktop as a widget, so you can always see what you need to do. 18 | - Cross-platform: Available on Linux, Windows, and Android, so you can access your lists from anywhere. 19 | - Open source: Adventure List is open source, so you can contribute to its development or customize it to your liking. 20 | 21 | Adventure List is ready to help you get things done. Download it today and start your adventure! 22 | 23 | 24 | ## Screenshots 25 | 26 | 27 | 28 | 29 | 30 | 31 |
32 | 33 | 34 | ## Download 35 | 36 | [GitHub Releases](https://github.com/Merrit/adventure_list/releases) 37 | 38 | [Flathub](https://flathub.org/apps/codes.merritt.adventurelist) 39 | 40 | [Microsoft Store](https://apps.microsoft.com/store/detail/adventure-list/9NR8M88MSCC2) 41 | 42 | [Google Play](https://play.google.com/store/apps/details?id=codes.merritt.adventurelist) 43 | 44 | 45 | ## Roadmap 46 | 47 | **Planned** 48 | 49 | - ~~Due dates / reminders~~ ✅ 50 | - ~~Recurring tasks~~ ✅ 51 | - Offline mode 52 | - Improve offline capabilities so the app can reliably be used offline 53 | - Collaboration on lists with other users 54 | - ~~Desktop widget~~ ✅ 55 | - ~~Desktop notifications~~ ✅ 56 | - ~~Notification center / popup / tray icon badge / etc~~ ✅ 57 | 58 | **Considering** 59 | 60 | - Improve Android widget (could use assistance) 61 | - Web version 62 | - iOS/iPadOS/macOS versions 63 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | prefer_final_locals: true 26 | prefer_relative_imports: true 27 | sort_pub_dependencies: true 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | 31 | analyzer: 32 | errors: 33 | # Ignore lints for @JsonKey on Freezed classes 34 | invalid_annotation_target: ignore 35 | # Exclude generated files from analysis 36 | exclude: 37 | - "**/*.g.dart" 38 | - "**/*.freezed.dart" 39 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | .cxx/ 9 | 10 | # Remember to never publicly share your keystore. 11 | # See https://flutter.dev/to/reference-keystore 12 | key.properties 13 | **/*.keystore 14 | **/*.jks 15 | -------------------------------------------------------------------------------- /android/app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import java.util.Properties 2 | import java.io.FileInputStream 3 | 4 | plugins { 5 | id("com.android.application") 6 | id("kotlin-android") 7 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. 8 | id("dev.flutter.flutter-gradle-plugin") 9 | } 10 | 11 | fun isRunningOnCI(): Boolean { 12 | return System.getenv("CI") != null || 13 | System.getenv("GITHUB_ACTIONS") != null || 14 | System.getenv("GITLAB_CI") != null 15 | } 16 | 17 | val keystoreProperties = Properties() 18 | val keystorePropertiesFile = rootProject.file("key.properties") 19 | if (keystorePropertiesFile.exists()) { 20 | keystoreProperties.load(FileInputStream(keystorePropertiesFile)) 21 | } else if (isRunningOnCI()) { 22 | // If running on CI, the keystore properties must be set as environment variables. 23 | keystoreProperties["keyAlias"] = System.getenv("ALIAS") 24 | keystoreProperties["storePassword"] = System.getenv("KEY_STORE_PASSWORD") 25 | keystoreProperties["storeFile"] = System.getenv("KEY_PATH") 26 | keystoreProperties["keyPassword"] = System.getenv("KEY_PASSWORD") 27 | } 28 | 29 | android { 30 | namespace = "codes.merritt.adventurelist" 31 | compileSdk = flutter.compileSdkVersion 32 | // ndkVersion = flutter.ndkVersion 33 | // ndkVersion = "27.0.12077973" 34 | ndkVersion = "25.1.8937393" 35 | 36 | compileOptions { 37 | sourceCompatibility = JavaVersion.VERSION_17 38 | targetCompatibility = JavaVersion.VERSION_17 39 | 40 | isCoreLibraryDesugaringEnabled = true 41 | } 42 | 43 | kotlinOptions { 44 | jvmTarget = JavaVersion.VERSION_17.toString() 45 | } 46 | 47 | defaultConfig { 48 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 49 | applicationId = "codes.merritt.adventurelist" 50 | // You can update the following values to match your application needs. 51 | // For more information, see: https://flutter.dev/to/review-gradle-config. 52 | // minSdk = flutter.minSdkVersion 53 | minSdk = 23 54 | targetSdk = flutter.targetSdkVersion 55 | versionCode = flutter.versionCode 56 | versionName = flutter.versionName 57 | } 58 | 59 | signingConfigs { 60 | create("release") { 61 | if (keystorePropertiesFile.exists() || isRunningOnCI()) { 62 | storeFile = file(keystoreProperties["storeFile"]) 63 | storePassword = keystoreProperties["storePassword"] as String 64 | keyAlias = keystoreProperties["keyAlias"] as String 65 | keyPassword = keystoreProperties["keyPassword"] as String 66 | } else { 67 | throw GradleException("key.properties not found") 68 | } 69 | } 70 | } 71 | 72 | buildTypes { 73 | release { 74 | // TODO: Add your own signing config for the release build. 75 | // Signing with the debug keys for now, so `flutter run --release` works. 76 | // signingConfig = signingConfigs.getByName("debug") 77 | signingConfig = signingConfigs.getByName("release") 78 | } 79 | 80 | debug { 81 | signingConfig = signingConfigs.getByName("debug") 82 | } 83 | } 84 | } 85 | 86 | dependencies { 87 | coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.2.2") 88 | } 89 | 90 | flutter { 91 | source = "../.." 92 | } 93 | -------------------------------------------------------------------------------- /android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "478765689275", 4 | "project_id": "adventure-list-354516", 5 | "storage_bucket": "adventure-list-354516.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:478765689275:android:751873c2d07e57e0da3c9e", 11 | "android_client_info": { 12 | "package_name": "codes.merritt.adventurelist" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "478765689275-2qneu0pfdhm2m4ej0lqdv69rm96shsg4.apps.googleusercontent.com", 18 | "client_type": 1, 19 | "android_info": { 20 | "package_name": "codes.merritt.adventurelist", 21 | "certificate_hash": "8124b413662fe1254ce85357f68cae94eea91de2" 22 | } 23 | }, 24 | { 25 | "client_id": "478765689275-54bv4bf8lsbnjial5o39db2gaukjd9s9.apps.googleusercontent.com", 26 | "client_type": 1, 27 | "android_info": { 28 | "package_name": "codes.merritt.adventurelist", 29 | "certificate_hash": "a7ba2285590bdbb07f3b33dffd53ad2283266292" 30 | } 31 | }, 32 | { 33 | "client_id": "478765689275-f5us0hu5mlu5s122dsrbc1kn1qb92sb1.apps.googleusercontent.com", 34 | "client_type": 1, 35 | "android_info": { 36 | "package_name": "codes.merritt.adventurelist", 37 | "certificate_hash": "e257f8738edcbbf1bc7ed4478ec1f83845380757" 38 | } 39 | } 40 | ], 41 | "api_key": [ 42 | { 43 | "current_key": "AIzaSyDFYBvnQaRl6szsW6_unb2Xhi0H_IJOy8M" 44 | } 45 | ], 46 | "services": { 47 | "appinvite_service": { 48 | "other_platform_oauth_client": [] 49 | } 50 | } 51 | } 52 | ], 53 | "configuration_version": "1" 54 | } -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # This file was added specifically for the flutter_local_notifications plugin. 2 | 3 | 4 | ## Gson rules 5 | # Gson uses generic type information stored in a class file when working with fields. Proguard 6 | # removes such information by default, so configure it to keep all of it. 7 | -keepattributes Signature 8 | 9 | # For using GSON @Expose annotation 10 | -keepattributes *Annotation* 11 | 12 | # Gson specific classes 13 | -dontwarn sun.misc.** 14 | #-keep class com.google.gson.stream.** { *; } 15 | 16 | # Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory, 17 | # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) 18 | -keep class * extends com.google.gson.TypeAdapter 19 | -keep class * implements com.google.gson.TypeAdapterFactory 20 | -keep class * implements com.google.gson.JsonSerializer 21 | -keep class * implements com.google.gson.JsonDeserializer 22 | 23 | # Prevent R8 from leaving Data object members always null 24 | -keepclassmembers,allowobfuscation class * { 25 | @com.google.gson.annotations.SerializedName ; 26 | } 27 | 28 | # Retain generic signatures of TypeToken and its subclasses with R8 version 3.0 and higher. 29 | -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken 30 | -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken 31 | 32 | # file_picker plugin 33 | # https://github.com/miguelpruivo/flutter_file_picker/wiki/Setup 34 | -keep class androidx.lifecycle.DefaultLifecycleObserver 35 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/adventure_list/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package codes.merritt.adventurelist 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity : FlutterActivity() 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/app_widget_background.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/app_widget_inner_view_background.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/app_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/android/app/src/main/res/drawable/app_icon.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/widget_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/res/layout/example_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 13 | 14 | 19 | 20 | 31 | 32 | 39 | 40 | 41 | 42 | 49 | 50 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/raw/keep.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 10 | 16 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-v31/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 11 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | #FFE1F5FE 3 | #FF81D4FA 4 | #FF039BE5 5 | #FF01579B 6 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 0dp 9 | 10 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | EXAMPLE 4 | Configure 5 | Add widget 6 | This is an app widget description 7 | Open App 8 | Select List 9 | Configure Widget 10 | Select List 11 | Task Name here 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | 23 | 24 | 28 | 29 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /android/app/src/main/res/xml/home_widget_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle.kts: -------------------------------------------------------------------------------- 1 | allprojects { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | } 6 | } 7 | 8 | val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() 9 | rootProject.layout.buildDirectory.value(newBuildDir) 10 | 11 | subprojects { 12 | val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) 13 | project.layout.buildDirectory.value(newSubprojectBuildDir) 14 | } 15 | subprojects { 16 | project.evaluationDependsOn(":app") 17 | } 18 | 19 | tasks.register("clean") { 20 | delete(rootProject.layout.buildDirectory) 21 | } 22 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | val flutterSdkPath = run { 3 | val properties = java.util.Properties() 4 | file("local.properties").inputStream().use { properties.load(it) } 5 | val flutterSdkPath = properties.getProperty("flutter.sdk") 6 | require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } 7 | flutterSdkPath 8 | } 9 | 10 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") 11 | 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | } 17 | } 18 | 19 | plugins { 20 | id("dev.flutter.flutter-plugin-loader") version "1.0.0" 21 | // id("com.android.application") version "8.7.0" apply false 22 | // id("org.jetbrains.kotlin.android") version "1.8.22" apply false 23 | id("com.android.application") version "8.3.2" apply false 24 | id("org.jetbrains.kotlin.android") version "2.0.20" apply false 25 | } 26 | 27 | include(":app") 28 | -------------------------------------------------------------------------------- /assets/fonts/NotoSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/assets/fonts/NotoSans-Regular.ttf -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist-symbolic-with-notification-badge.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/assets/icons/codes.merritt.adventurelist-symbolic-with-notification-badge.ico -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist-symbolic-with-notification-badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 35 | 38 | 41 | 44 | 47 | 48 | 54 | 55 | -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist-symbolic.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/assets/icons/codes.merritt.adventurelist-symbolic.ico -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist-symbolic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist-with-notification-badge.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/assets/icons/codes.merritt.adventurelist-with-notification-badge.ico -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/assets/icons/codes.merritt.adventurelist.ico -------------------------------------------------------------------------------- /assets/icons/codes.merritt.adventurelist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Merrit/adventure_list/c3da3d0963f6d209c5b2f79a335f2b4bd380c4e7/assets/icons/codes.merritt.adventurelist.png -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | json_serializable: 5 | options: 6 | # Options configure how source code is generated for every 7 | # `@JsonSerializable`-annotated class in the package. 8 | any_map: true 9 | explicit_to_json: true 10 | -------------------------------------------------------------------------------- /devtools_options.yaml: -------------------------------------------------------------------------------- 1 | description: This file stores settings for Dart & Flutter DevTools. 2 | documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states 3 | extensions: 4 | -------------------------------------------------------------------------------- /lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | return web; 21 | } 22 | switch (defaultTargetPlatform) { 23 | case TargetPlatform.android: 24 | return android; 25 | case TargetPlatform.iOS: 26 | throw UnsupportedError( 27 | 'DefaultFirebaseOptions have not been configured for ios - ' 28 | 'you can reconfigure this by running the FlutterFire CLI again.', 29 | ); 30 | case TargetPlatform.macOS: 31 | throw UnsupportedError( 32 | 'DefaultFirebaseOptions have not been configured for macos - ' 33 | 'you can reconfigure this by running the FlutterFire CLI again.', 34 | ); 35 | case TargetPlatform.windows: 36 | throw UnsupportedError( 37 | 'DefaultFirebaseOptions have not been configured for windows - ' 38 | 'you can reconfigure this by running the FlutterFire CLI again.', 39 | ); 40 | case TargetPlatform.linux: 41 | throw UnsupportedError( 42 | 'DefaultFirebaseOptions have not been configured for linux - ' 43 | 'you can reconfigure this by running the FlutterFire CLI again.', 44 | ); 45 | default: 46 | throw UnsupportedError( 47 | 'DefaultFirebaseOptions are not supported for this platform.', 48 | ); 49 | } 50 | } 51 | 52 | static const FirebaseOptions web = FirebaseOptions( 53 | apiKey: 'AIzaSyBlOvM_3kmgBpmbmWW1BIZ6hOxIFACy2xQ', 54 | appId: '1:478765689275:web:073d88ff3aad6880da3c9e', 55 | messagingSenderId: '478765689275', 56 | projectId: 'adventure-list-354516', 57 | authDomain: 'adventure-list-354516.firebaseapp.com', 58 | storageBucket: 'adventure-list-354516.appspot.com', 59 | measurementId: 'G-WK9C8EPC0N', 60 | ); 61 | 62 | static const FirebaseOptions android = FirebaseOptions( 63 | apiKey: 'AIzaSyDFYBvnQaRl6szsW6_unb2Xhi0H_IJOy8M', 64 | appId: '1:478765689275:android:751873c2d07e57e0da3c9e', 65 | messagingSenderId: '478765689275', 66 | projectId: 'adventure-list-354516', 67 | storageBucket: 'adventure-list-354516.appspot.com', 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/app/app.dart: -------------------------------------------------------------------------------- 1 | export 'cubit/cubit.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/app/cubit/app_state.dart: -------------------------------------------------------------------------------- 1 | part of 'app_cubit.dart'; 2 | 3 | @freezed 4 | class AppState with _$AppState { 5 | const factory AppState({ 6 | /// True if this is the first run of the app. 7 | required bool firstRun, 8 | required String runningVersion, 9 | required String? updateVersion, 10 | required bool updateAvailable, 11 | required bool showUpdateButton, 12 | 13 | /// True if a condition has been met requiring the user to be 14 | /// prompted to purchase the pro upgrade. 15 | required bool promptForProUpgrade, 16 | 17 | /// Release notes for the current version. 18 | required ReleaseNotes? releaseNotes, 19 | }) = _AppState; 20 | 21 | factory AppState.initial() { 22 | return const AppState( 23 | firstRun: false, 24 | runningVersion: '', 25 | updateVersion: null, 26 | updateAvailable: false, 27 | showUpdateButton: false, 28 | promptForProUpgrade: false, 29 | releaseNotes: null, 30 | ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/src/app/cubit/cubit.dart: -------------------------------------------------------------------------------- 1 | export 'app_cubit.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/authentication/authentication.dart: -------------------------------------------------------------------------------- 1 | export 'cubit/authentication_cubit.dart'; 2 | export 'google_auth.dart'; 3 | -------------------------------------------------------------------------------- /lib/src/authentication/cubit/authentication_cubit.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter_bloc/flutter_bloc.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | import 'package:googleapis_auth/googleapis_auth.dart'; 6 | 7 | import '../../logs/logging_manager.dart'; 8 | import '../../storage/storage_repository.dart'; 9 | import '../google_auth.dart'; 10 | 11 | part 'authentication_state.dart'; 12 | part 'authentication_cubit.freezed.dart'; 13 | 14 | late final AuthenticationCubit authCubit; 15 | 16 | class AuthenticationCubit extends Cubit { 17 | final GoogleAuth _googleAuth; 18 | final StorageRepository _storageRepository; 19 | 20 | AuthenticationCubit._( 21 | this._googleAuth, 22 | this._storageRepository, { 23 | required AuthenticationState initialState, 24 | }) : super(initialState) { 25 | authCubit = this; 26 | } 27 | 28 | static Future initialize({ 29 | required GoogleAuth googleAuth, 30 | required StorageRepository storageRepository, 31 | }) async { 32 | final String? savedCredentials = await storageRepository.get( 33 | 'accessCredentials', 34 | ); 35 | 36 | AccessCredentials? credentials; 37 | if (savedCredentials != null) { 38 | credentials = AccessCredentials.fromJson(jsonDecode(savedCredentials)); 39 | } 40 | 41 | return AuthenticationCubit._( 42 | googleAuth, 43 | storageRepository, 44 | initialState: AuthenticationState( 45 | accessCredentials: credentials, 46 | signedIn: (credentials != null), 47 | ), 48 | ); 49 | } 50 | 51 | Future signIn() async { 52 | assert(!state.signedIn); 53 | 54 | log.i('Signing in...'); 55 | 56 | final accessCredentials = await _googleAuth.signin(); 57 | if (accessCredentials == null) { 58 | log.w('Unable to sign in'); 59 | return; 60 | } else { 61 | log.i('Signed in successfully.'); 62 | } 63 | 64 | emit(state.copyWith( 65 | accessCredentials: accessCredentials, 66 | signedIn: true, 67 | )); 68 | 69 | await _storageRepository.save( 70 | key: 'accessCredentials', 71 | value: jsonEncode(accessCredentials.toJson()), 72 | ); 73 | } 74 | 75 | Future signOut() async { 76 | await _googleAuth.signOut(); 77 | await _storageRepository.delete('accessCredentials'); 78 | 79 | emit(state.copyWith( 80 | accessCredentials: null, 81 | signedIn: false, 82 | )); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /lib/src/authentication/cubit/authentication_state.dart: -------------------------------------------------------------------------------- 1 | part of 'authentication_cubit.dart'; 2 | 3 | @freezed 4 | class AuthenticationState with _$AuthenticationState { 5 | const factory AuthenticationState({ 6 | required AccessCredentials? accessCredentials, 7 | required bool signedIn, 8 | }) = _AuthenticationState; 9 | } 10 | -------------------------------------------------------------------------------- /lib/src/authentication/sign_in_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter_bloc/flutter_bloc.dart'; 6 | import 'package:flutter_signin_button/button_list.dart'; 7 | import 'package:flutter_signin_button/button_view.dart'; 8 | import 'package:window_to_front/window_to_front.dart'; 9 | 10 | import '../tasks/tasks.dart'; 11 | import 'cubit/authentication_cubit.dart'; 12 | 13 | class SignInPage extends StatelessWidget { 14 | static const routeName = '/signin_page'; 15 | 16 | const SignInPage({super.key}); 17 | 18 | @override 19 | Widget build(BuildContext context) { 20 | return Scaffold( 21 | body: BlocBuilder( 22 | builder: (context, state) { 23 | return Column( 24 | mainAxisSize: MainAxisSize.max, 25 | mainAxisAlignment: MainAxisAlignment.center, 26 | children: [ 27 | // Signed in, inform user and proceed to load app. 28 | if (state.signedIn) 29 | Builder(builder: (context) { 30 | if (Platform.isLinux || Platform.isWindows) { 31 | // Focus app window after authentication. 32 | WindowToFront.activate(); 33 | } 34 | 35 | Timer(const Duration(seconds: 2), () { 36 | Navigator.pushReplacementNamed( 37 | context, 38 | TasksPage.routeName, 39 | ); 40 | }); 41 | 42 | return const Center( 43 | child: Padding( 44 | padding: EdgeInsets.only(bottom: 30), 45 | child: Icon( 46 | Icons.check_circle_outline, 47 | size: 100, 48 | color: Colors.green, 49 | ), 50 | ), 51 | ); 52 | }), 53 | 54 | // Not signed in, prompt for authentication. 55 | Center( 56 | child: SignInButton( 57 | Buttons.GoogleDark, 58 | onPressed: () async { 59 | await authCubit.signIn(); 60 | }, 61 | ), 62 | ), 63 | ], 64 | ); 65 | }, 66 | ), 67 | ); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/src/autostart/autostart_service.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:helpers/helpers.dart'; 5 | import 'package:launch_at_startup/launch_at_startup.dart'; 6 | import 'package:package_info_plus/package_info_plus.dart'; 7 | import 'package:xdg_desktop_portal/xdg_desktop_portal.dart'; 8 | 9 | import '../logs/logging_manager.dart'; 10 | 11 | /// Service to enable/disable autostart on desktop platforms. 12 | class AutostartService { 13 | /// Disables autostart on login. 14 | Future disable() async { 15 | assert(defaultTargetPlatform.isDesktop); 16 | 17 | if (runningInFlatpak()) { 18 | await _setForFlatpak(false); 19 | } else { 20 | await _disableForDesktop(); 21 | } 22 | } 23 | 24 | /// Enables autostart on login. 25 | Future enable() async { 26 | assert(defaultTargetPlatform.isDesktop); 27 | 28 | if (runningInFlatpak()) { 29 | await _setForFlatpak(true); 30 | } else { 31 | await _enableForDesktop(); 32 | } 33 | } 34 | 35 | Future _disableForDesktop() async { 36 | await _setupLaunchAtStartup(); 37 | await launchAtStartup.disable(); 38 | } 39 | 40 | Future _enableForDesktop() async { 41 | await _setupLaunchAtStartup(); 42 | await launchAtStartup.enable(); 43 | } 44 | 45 | Future _setForFlatpak(bool enable) async { 46 | log.i('Setting up autostart for Flatpak app.'); 47 | final client = XdgDesktopPortalClient(); 48 | 49 | final result = await client.background.requestBackground( 50 | reason: 'Autostarting Adventure List', 51 | autostart: enable, 52 | commandLine: ['adventure_list'], 53 | ).first; 54 | 55 | log.i('Result: $result'); 56 | await client.close(); 57 | } 58 | 59 | Future _setupLaunchAtStartup() async { 60 | final packageInfo = await PackageInfo.fromPlatform(); 61 | 62 | launchAtStartup.setup( 63 | appName: packageInfo.appName, 64 | appPath: Platform.resolvedExecutable, 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /lib/src/background_tasks/background_tasks.dart: -------------------------------------------------------------------------------- 1 | export 'background_tasks_service.dart'; 2 | -------------------------------------------------------------------------------- /lib/src/core/constants.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:helpers/helpers.dart'; 3 | 4 | const String kDonateUrl = 'https://merritt.codes/support'; 5 | const String kPackageId = 'codes.merritt.adventurelist'; 6 | const String kRepoUrl = 'https://github.com/Merrit/adventure_list'; 7 | const String kWebsiteUrl = 'https://github.com/Merrit/adventure_list'; 8 | 9 | /// The paths to the app's icons. 10 | abstract class AppIcons { 11 | /// Asset directory containing the app's icons. 12 | static const String path = 'assets/icons'; 13 | 14 | /// Normal icon as an SVG. 15 | static const String linux = '$path/$kPackageId.svg'; 16 | 17 | /// Normal icon as an ICO. 18 | static const String windows = '$path/$kPackageId.ico'; 19 | 20 | /// Normal icon with a red dot indicating a notification, as an SVG. 21 | static const String linuxWithNotificationBadge = 22 | '$path/$kPackageId-with-notification-badge.svg'; 23 | 24 | /// Normal icon with a red dot indicating a notification, as an ICO. 25 | static const String windowsWithNotificationBadge = 26 | '$path/$kPackageId-with-notification-badge.ico'; 27 | 28 | /// Symbolic icon as an SVG. 29 | static const String linuxSymbolic = '$path/$kPackageId-symbolic.svg'; 30 | 31 | /// Symbolic icon as an ICO. 32 | static const String windowsSymbolic = '$path/$kPackageId-symbolic.ico'; 33 | 34 | /// Symbolic icon with a red dot indicating a notification, as an SVG. 35 | static const String linuxSymbolicWithNotificationBadge = 36 | '$path/$kPackageId-symbolic-with-notification-badge.svg'; 37 | 38 | /// Symbolic icon with a red dot indicating a notification, as an ICO. 39 | static const String windowsSymbolicWithNotificationBadge = 40 | '$path/$kPackageId-symbolic-with-notification-badge.ico'; 41 | 42 | /// Returns the appropriate icon path (or icon name) based on the current platform. 43 | static String platformSpecific({ 44 | required bool symbolic, 45 | bool withNotificationBadge = false, 46 | }) { 47 | if (runningInFlatpak() || runningInSnap()) { 48 | // When running in a sandboxed environment the icon must be specified by 49 | // the icon's name, not the path. 50 | return kPackageId; 51 | } 52 | 53 | return defaultTargetPlatform.isWindows 54 | ? getWindowsIcon(symbolic, withNotificationBadge) 55 | : getLinuxIcon(symbolic, withNotificationBadge); 56 | } 57 | 58 | static String getWindowsIcon(bool symbolic, bool withNotificationBadge) { 59 | if (symbolic) { 60 | return withNotificationBadge 61 | ? windowsSymbolicWithNotificationBadge 62 | : windowsSymbolic; 63 | } else { 64 | return withNotificationBadge ? windowsWithNotificationBadge : windows; 65 | } 66 | } 67 | 68 | static String getLinuxIcon(bool symbolic, bool withNotificationBadge) { 69 | if (symbolic) { 70 | return withNotificationBadge ? linuxSymbolicWithNotificationBadge : linuxSymbolic; 71 | } else { 72 | return withNotificationBadge ? linuxWithNotificationBadge : linux; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/src/core/core.dart: -------------------------------------------------------------------------------- 1 | export 'constants.dart'; 2 | export 'package:helpers/helpers.dart'; 3 | export 'widgets/widgets.dart'; 4 | -------------------------------------------------------------------------------- /lib/src/core/helpers/date_time.dart: -------------------------------------------------------------------------------- 1 | import 'package:intl/intl.dart'; 2 | 3 | extension DateTimeHelper on DateTime { 4 | /// Returns true if this date is the same as [other] (ignoring time). 5 | bool isSameDay(DateTime other) { 6 | return year == other.year && month == other.month && day == other.day; 7 | } 8 | 9 | /// Returns a string representation of this date that is appropriate for 10 | /// displaying in the UI. 11 | /// 12 | /// If the task is due today, the label will be "Today,