diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..7989368 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Tap on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Device and Operating System (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/integration-test.md b/.github/ISSUE_TEMPLATE/integration-test.md new file mode 100644 index 0000000..f2bd7f5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/integration-test.md @@ -0,0 +1,14 @@ +--- +name: Integration test +about: Create a new integration test to verify a specific scenario +title: "[Integration Test]" +labels: enhancement, help wanted, test +assignees: '' + +--- + +**Test:** + +**Scenario outline:** + +**Verify that:** diff --git a/.github/ISSUE_TEMPLATE/new-feature-improvement.md b/.github/ISSUE_TEMPLATE/new-feature-improvement.md new file mode 100644 index 0000000..81030c2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/new-feature-improvement.md @@ -0,0 +1,14 @@ +--- +name: New feature/improvement +about: New feature or improvement to be implemented +title: "[Feature/Improvement]" +labels: enhancement +assignees: '' + +--- + +**Purpose:** As a user, I... + +**More details:** Some more details on the feature/improvement + +**Acceptance Criteria:** diff --git a/.github/ISSUE_TEMPLATE/refactor-refine.md b/.github/ISSUE_TEMPLATE/refactor-refine.md new file mode 100644 index 0000000..b7b409c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/refactor-refine.md @@ -0,0 +1,10 @@ +--- +name: Refactor/refine +about: Refactor or refinement to improve code quality. +title: "[Refactor]" +labels: refine +assignees: '' + +--- + + diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml new file mode 100644 index 0000000..f734ab8 --- /dev/null +++ b/.github/workflows/test.yaml @@ -0,0 +1,50 @@ +name: Test + +run-name: ${{ github.actor }} is testing the app + +on: + workflow_dispatch: + pull_request: + types: + - opened + - reopened + - synchronize + - ready_for_review + branches: + - 'main' + paths-ignore: + - '**.md' + - '**.yaml' + - 'doc/**' + - '.git/' + - '.vscode/' + +jobs: + Build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - name: Check Version + run: flutter --version + - name: Flutter Doctor + run: flutter doctor -v + - name: Enable KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + - name: run tests + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 34 + arch: x86_64 + script: flutter drive -d emulator-5554 + working-directory: example diff --git a/CHANGELOG.md b/CHANGELOG.md index 2318d8a..cbfa0cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,31 +5,39 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.0.0] - 2024-11-10 + +### Changed +- Refactored to use a SQFlite database. +- Implemented IntervalType class - requires a list of intervals to be passed. +- Added skip next and skip previous functions. +- Simplified implementation. + ## [1.0.0-dev.6] - 2024-08-10 -## Changed +### Changed - Removed dependency on `flutter_fgbg`. ## [1.0.0-dev.5] - 2024-08-10 -## Changed +### Changed - Simplified timer restart. -## Fixed +### Fixed - Fixed sound attempting to play when set to `none`. ## [1.0.0-dev.4] - 2024-07-31 -## Added +### Added - Volume control for timer audio. ## [1.0.0-dev.3] - 2024-06-23 -## Changed +### Changed - Upgraded dependencies. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..383e879 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,35 @@ +# Background HIIT Timer Code of Conduct + +## Purpose + +The community is built on the dedication of volunteers who contribute their time and expertise. We are committed to fostering a respectful and inclusive environment for all contributors. This Code of Conduct outlines our expectations to ensure a positive and collaborative experience. + +## Our Pledge + +In the spirit of acknowledging the voluntary contributions made by individuals, we pledge to respect and value the time and effort our contributors invest in the project. Additionally, we are resolute in our stance against rude behavior, ensuring a welcoming community for all. + +## Code of Conduct + +### 1. Acknowledge Voluntary Contributions + +Recognize that all contributors to this project are volunteers who generously offer their time. Appreciate and respect the dedication they bring to the community. + +### 2. Zero Tolerance for Rudeness + +Rude or disrespectful behavior will not be tolerated. This includes but is not limited to personal attacks, offensive language, and any behavior that disrupts the positive and collaborative atmosphere of the project. + +### 3. Be Inclusive + +We welcome and encourage participation from individuals of all backgrounds. Be respectful and considerate of differing viewpoints, experiences, and opinions. Treat everyone with kindness and empathy. + +### 4. Be Collaborative + +Open source thrives on collaboration. Work together to resolve issues, improve code, and enhance the project. Give and receive constructive feedback with a focus on improvement. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be addressed by the project maintainer, who has the right to remove, edit, or reject comments, commits, code, issues, and other contributions that are not aligned with this Code of Conduct. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org), version 2.0. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..512f110 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,37 @@ +# Contributing to background_hiit_timer + +Thank you for considering contributing! Contributions of all kinds are welcome, including bug reports, feature suggestions, code contributions, documentation improvements, and more. By participating in this project, you agree to abide by the [Code of Conduct](./CODE_OF_CONDUCT.md). + +## How to Contribute + +### Reporting Bugs + +1. Check the existing [issues](https://github.com/a-mabe/background_hiit_timer/issues) to make sure the bug hasn't been reported yet. +2. Open a new issue and include: + A clear title and description. + Steps to reproduce the issue. + Expected behavior vs. what actually happens. + Screenshots or logs, if applicable. + +### Suggesting Features +1. Open an issue labeled as a "feature request". +1. Clearly describe the proposed feature and its benefits. +1. If applicable, include examples or references. + +### Code Contributions + +1. Fork the repository on GitHub. + +2. Create a new branch with a descriptive name for your feature or bug fix. + +3. Make your changes and ensure they follow the project's coding style and guidelines. + +4. Commit your changes and push them to your forked repository. + +5. Test your changes following the [testing documentation](./doc/testing.md). + +6. Submit a pull request, detailing the changes you made and explaining their purpose. Please be sure to list what you have tested, as well as the device and OS details you performed tests on. + +## License + +By contributing, you agree that your contributions will be licensed under the MIT License. \ No newline at end of file diff --git a/README.md b/README.md index b7d00a6..3fa20fd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,22 @@ # background_hiit_timer -A Flutter package for creating a background HIIT (High-Intensity Interval Training) timer. This package is based on the [`timer_count_down`](https://pub.dev/packages/timer_count_down) package and extends its functionality to support background execution for interval timers. +A Flutter package for creating a high-intensity interval training (HIIT) timer with background service capabilities. Originally created for [OpenHIIT](https://github.com/a-mabe/OpenHIIT). + +![Screenshot 2024-11-10 at 4 22 43 PM](https://github.com/user-attachments/assets/0c990563-d8b0-4240-b11c-c865845c8ce9) + +--- +## Table of Contents + +1. [Installation](#installation) +1. [Basic Usage](#basic-usage) + 1. [Example Usage](#example-usage) +1. [Advanced Configuration](#Advanced-Configuration) +1. [Contributing](#Contributing) + 1. [Code of Conduct](#Code-of-Conduct) +1. [Credits](#credits) +1. [License](#license) + +--- ## Installation @@ -13,7 +29,7 @@ dependencies: background_hiit_timer: ^1.0.0 ``` -## Usage +## Basic Usage Import `background_hiit_timer` in your Dart file: @@ -21,33 +37,81 @@ Import `background_hiit_timer` in your Dart file: import 'package:background_hiit_timer/background_hiit_timer.dart'; ``` -Create a `Countdown` widget and configure it with your interval settings: +1. Ensure that your app is properly configured to handle background execution on both Android and iOS platforms. Refer to [flutter_background_service](https://pub.dev/packages/flutter_background_service) for details. + +2. Define a set of intervals: + +``` +final List intervals = [ + IntervalType( + id: "0", + workoutId: "1", + time: 10, // in seconds + name: "Get ready", + color: 0, + intervalIndex: 0, + startSound: "", + halfwaySound: "", + countdownSound: "countdown-beep", + endSound: ""), + + ... + + IntervalType( + id: "4", + workoutId: "1", + time: 10, // in seconds + name: "Cooldown", + color: 0, + intervalIndex: 4, + startSound: "long-rest-beep", + countdownSound: "countdown-beep", + endSound: "horn", + halfwaySound: ""), + ]; +``` + +3. Define a controller: + +``` +final CountdownController _controller = CountdownController(autoStart: true); +``` + +4. Create a `Countdown` widget and configure it with your interval settings: ```dart Countdown( - controller: _controller, - workSeconds: 20, - restSeconds: 10, - getreadySeconds: 10, - breakSeconds: 30, - warmupSeconds: 60, - cooldownSeconds: 60, - numberOfWorkIntervals: 8, - iterations: 2, - onFinished: () {}, - build: Text(timerData.currentMicroSeconds.toString()) + controller: _controller, + intervals: intervals, + onFinished: () {}, + build: (_, TimerState timerState) { + Text(timerState.currentMicroSeconds.toString()) + } +) ``` -Ensure that your app is properly configured to handle background execution on both Android and iOS platforms. +### Example Usage + +Check out the [example](example) directory in this repository for a complete example of how to use `background_hiit_timer` in a Flutter app. + +## Advanced Configuration -## Example +For more advanced information, view [the advanced configuration documentation](./doc/advanced_configuration.md). -Check out the `example` directory in this repository for a complete example of how to use `background_hiit_timer` in a Flutter app. +## Contributing + +View the [contributing documentation](./CONTRIBUTING.md). If contributing code changes, please checkout the [testing documentation](./doc/testing.md). + +### Code of Conduct + +When contributing, please keep the [Code of Conduct](./CODE_OF_CONDUCT.md) in mind. ## Credits -This package is based on the [`timer_count_down`](https://pub.dev/packages/timer_count_down) package by [Dizoft Team](https://github.com/DizoftTeam). +This package is inspired by the [`timer_count_down`](https://pub.dev/packages/timer_count_down) package by [Dizoft Team](https://github.com/DizoftTeam). + +Shoutout to [`flutter_background_service`](https://pub.dev/packages/flutter_background_service) for making the background timer possible. ## License -MIT License. See [LICENSE](LICENSE) for details. \ No newline at end of file +MIT License. See [LICENSE](LICENSE) for details. diff --git a/assets/audio/countdown-beep.mp3 b/assets/audio/countdown-beep.mp3 deleted file mode 100644 index 3f4e814..0000000 Binary files a/assets/audio/countdown-beep.mp3 and /dev/null differ diff --git a/assets/audio/ding-sequence.mp3 b/assets/audio/ding-sequence.mp3 deleted file mode 100644 index f0bac94..0000000 Binary files a/assets/audio/ding-sequence.mp3 and /dev/null differ diff --git a/assets/audio/ding.mp3 b/assets/audio/ding.mp3 deleted file mode 100644 index 0898dbd..0000000 Binary files a/assets/audio/ding.mp3 and /dev/null differ diff --git a/assets/audio/halfway-beep2.mp3 b/assets/audio/halfway-beep2.mp3 deleted file mode 100644 index 2c65dcf..0000000 Binary files a/assets/audio/halfway-beep2.mp3 and /dev/null differ diff --git a/assets/audio/harsh-beep-sequence.mp3 b/assets/audio/harsh-beep-sequence.mp3 deleted file mode 100644 index 61081e6..0000000 Binary files a/assets/audio/harsh-beep-sequence.mp3 and /dev/null differ diff --git a/assets/audio/harsh-beep.mp3 b/assets/audio/harsh-beep.mp3 deleted file mode 100644 index 8a6b1bf..0000000 Binary files a/assets/audio/harsh-beep.mp3 and /dev/null differ diff --git a/assets/audio/horn.mp3 b/assets/audio/horn.mp3 deleted file mode 100644 index e37673b..0000000 Binary files a/assets/audio/horn.mp3 and /dev/null differ diff --git a/assets/audio/long-bell.mp3 b/assets/audio/long-bell.mp3 deleted file mode 100644 index dbd15c2..0000000 Binary files a/assets/audio/long-bell.mp3 and /dev/null differ diff --git a/assets/audio/long-halfway-beep.mp3 b/assets/audio/long-halfway-beep.mp3 deleted file mode 100644 index 4c5c3de..0000000 Binary files a/assets/audio/long-halfway-beep.mp3 and /dev/null differ diff --git a/assets/audio/long-rest-beep.mp3 b/assets/audio/long-rest-beep.mp3 deleted file mode 100644 index e03d3e8..0000000 Binary files a/assets/audio/long-rest-beep.mp3 and /dev/null differ diff --git a/assets/audio/long-whistle.mp3 b/assets/audio/long-whistle.mp3 deleted file mode 100644 index f1100e7..0000000 Binary files a/assets/audio/long-whistle.mp3 and /dev/null differ diff --git a/assets/audio/short-halfway-beep.mp3 b/assets/audio/short-halfway-beep.mp3 deleted file mode 100644 index 21408a4..0000000 Binary files a/assets/audio/short-halfway-beep.mp3 and /dev/null differ diff --git a/assets/audio/short-rest-beep.mp3 b/assets/audio/short-rest-beep.mp3 deleted file mode 100644 index 4f68577..0000000 Binary files a/assets/audio/short-rest-beep.mp3 and /dev/null differ diff --git a/assets/audio/short-whistle.mp3 b/assets/audio/short-whistle.mp3 deleted file mode 100644 index 075adf7..0000000 Binary files a/assets/audio/short-whistle.mp3 and /dev/null differ diff --git a/assets/audio/thunk.mp3 b/assets/audio/thunk.mp3 deleted file mode 100644 index 3435235..0000000 Binary files a/assets/audio/thunk.mp3 and /dev/null differ diff --git a/assets/preview.gif b/assets/preview.gif deleted file mode 100644 index feb2e9d..0000000 Binary files a/assets/preview.gif and /dev/null differ diff --git a/doc/advanced_configuration.md b/doc/advanced_configuration.md new file mode 100644 index 0000000..e62926b --- /dev/null +++ b/doc/advanced_configuration.md @@ -0,0 +1,173 @@ +# Advanced Configuration - background_hiit_timer + +--- + +## Table of Contents +1. [Background Service](#background-service) + 1. [Android Foreground Service Configuration](#Android-Foreground-Service-Configuration) + 1. [iOS Background Service Configuration](#iOS-Background-Service-Configuration) +1. [SQLite](#sqflite) +1. [Audio](#audio) + 1. [Audio Assets](#audio-assets) + 1. [Audio Cues](#audio-cues) + 1. [Audio Session](#audio-session) +1. [Configuring the UI](#configuring-the-ui) +1. [iOS and Android Differences](#ios-and-android-differences) + +--- + +## Background Service + +This package relies on [flutter_background_service](https://pub.dev/packages/flutter_background_service). This documentation will walk through how the [example](example) app was configured. Refer to the `flutter_background_service` package for the most accurate information. + +### Android Foreground Service Configuration + +Outlined below is the configuration used in the [example](example) app to enable the timer to execute in the background and play audio on Android. + +1. Add the following `` and `` to [android/app/src/main/AndroidManifest.xml](android/app/src/main/AndroidManifest.xml) + +``` + + + + + + + + + + + + ... + + +``` + +1. On a production release, you may need to request the `SCHEDULE_EXACT_ALARM` permission from the user. This was left out of the [example](example) as when and how to request permissions is left up to the developer. You can use the [permission_handler](https://pub.dev/packages/permission_handler) package: + +``` +if (Platform.isAndroid) { + await Permission.scheduleExactAlarm.isDenied.then((value) { + if (value) { + Permission.scheduleExactAlarm.request(); + } + }); +} +``` + +### iOS Background Service Configuration + +1. Add the following to `ios/Runner/Info.plist`: + +``` +BGTaskSchedulerPermittedIdentifiers + + dev.flutter.background.refresh + +``` + +2. The following background modes will also need to be added to `ios/Runner/Info.plist` (or check the boxes in XCode): + +``` +UIBackgroundModes + + audio + fetch + processing + +``` + +## SQLite + +The timer uses a [sqflite](https://pub.dev/packages/sqflite) database to send data to the background service. Note that the frontend and the service cannot directly communicate - data must be passed through shared preferences, a database, etc. Stored in the database is the list of timer intervals for the service to iterate through. The database is cleared before storing the next set of data on service startup. + +## Audio + +### Audio Assets + +This package ships with some copyright free audio assets located at [`lib/assets/audio`](../lib/assets/audio/) + +Set what audio to use for an interval by specifying the asset file name (no need to include `.mp3`, leave blank for no audio): + +``` +IntervalType( + ... + startSound: "long-bell", + halfwaySound: "short-halfway-beep", + countdownSound: "countdown-beep", + endSound: ""), +``` + +### Audio Cues + +There are four configurable audio cues for intervals outlined below: + +| Parameter Name | Description | +|---------------|-------------------------------------------------| +| `startSound` | Plays at the start of an interval | +| `halfwaySound` | Plays at the halfway point of an interval. | +| `countdownSound` | Plays at the 3, 2, and 1 seconds marks to signify the interval is ending. | +| `endSound` | Plays at the end of an interval. If intervals are back to back, the start sound of the next interval takes precedence over the end sound of the previous interval. | + +### Audio Session + +This package currently uses the [soundpool](https://pub.dev/packages/soundpool) package to play audio. The package does not configure an audio session, leaving it up to the developer to set the audio session as they see fit. The [example](example) uses the [audio_session](https://pub.dev/packages/audio_session) package to configure the audio session so that audio from the service will play on both iOS and Android when the app is in the background. The session is also configured to not stop or duck audio from other apps. + +1. You can create a function to initialize an audio session with your desired settings. The following function sets the session configuration for the behavior in the example app described above: + +``` +Future initializeAudioSession() async { + final session = await AudioSession.instance; + + await session.configure(const AudioSessionConfiguration( + avAudioSessionCategory: AVAudioSessionCategory.playback, + avAudioSessionCategoryOptions: + AVAudioSessionCategoryOptions.mixWithOthers, + avAudioSessionMode: AVAudioSessionMode.defaultMode, + avAudioSessionRouteSharingPolicy: + AVAudioSessionRouteSharingPolicy.defaultPolicy, + avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none, + androidAudioAttributes: AndroidAudioAttributes( + contentType: AndroidAudioContentType.sonification, + flags: AndroidAudioFlags.audibilityEnforced, + usage: AndroidAudioUsage.notification, + ), + androidAudioFocusGainType: AndroidAudioFocusGainType.gain, + androidWillPauseWhenDucked: true, + )); +} +``` + +Without managing the audio session, the timer background service may not play audio when the app is in the background. + +## Configuring the UI + +The timer service will send back some data to the frontend: + +| Variable Name | Type | Description | +|---------------|----------|-------------------------------------------------| +| `paused` | Boolean | Whether the timer is currently paused. Passed to the service via shared preferences. | +| `status` | String | Name of the current interval. | +| `currentInterval` | Integer | Index of the current active interval, zero being the first and so on. | +| `currentMicroSeconds` | Integer | Time in microseconds that has currently passed for the active interval. | +| `currentMicroSeconds` | Integer | Full amount of time in microseconds for the active interval. | +| `volume` | double | The volume at which to play timer sound effects. Passed to the service via shared preferences. | +| `changeVolume` | boolean | Used to update the UI to show volume controls. Passed to the service via shared preferences. | + +This data can then be used to determine what to show in the UI, for example showing the currentMicroSeconds: + +``` +build: (_, TimerState timerState) { + Text(timerState.currentMicroSeconds.toString()) +} +``` + +## iOS and Android Differences + +### Background Service Life + +On iOS, the background service will automatically be killed if sound has not played recently. To remediate this, the timer will play a blank audio file every second a sound effect does not play. On Android, this is not necessary to keep the service alive. However, the sound effects can have some delay on Android when the timer first starts. Playing blank audio eliminates this delay. Thus, the blank audio is fired on both iOS and Android for different reasons. \ No newline at end of file diff --git a/doc/testing.md b/doc/testing.md new file mode 100644 index 0000000..b0dad74 --- /dev/null +++ b/doc/testing.md @@ -0,0 +1,71 @@ +# Testing Documentation + +This document outlines the testing strategy for this app, covering both automated integration tests and manual QA tests. The goal is to ensure that core functionality works as expected across platforms, and that audio features perform reliably on both Android and iOS. + +--- + +## Table of Contents +- [Integration Tests](#integration-tests) +- [Manual QA Tests](#manual-qa-tests) +- [Running Tests](#running-tests) + +--- + +## Integration Tests + +Integration tests focus on verifying the main functionality of the app, ensuring that critical features work as expected. These tests are automated and run during the CI/CD pipeline to catch issues early. + +### 1. Timer Functionality +- **Description**: Validates the timer states (e.g., "Get ready," "Warmup," "Work"). +- **Tests**: + - The timer transitions through all states without error. + - Timer pauses, resumes, and resets accurately. + - Skip interval controls function as expected. +- View the [Test workflow here](https://github.com/a-mabe/background_hiit_timer/actions/workflows/test.yaml). + +--- + +## Manual QA Tests + +Some audio functionalities require manual testing, particularly those involving interactions with other applications and background playback. These tests ensure a seamless experience across platforms. + +Ideally, each of these tests are performed on both iOS and Android. + +### Audio Playback QA Tests + +#### Test 1: Audio Playback without Interrupting Other Apps +- **Objective**: Verify that app audio does not stop or interfere with audio from other applications. +- **Steps**: + 1. Start playing music or a podcast on an external app (e.g., Spotify, Apple Music). + 2. Open the app under test and initiate audio playback. + 3. Verify that external audio continues playing and that app audio can be heard simultaneously. +- **Pass Criteria**: + - App audio and external audio both play uninterrupted. + +#### Test 2: Background Audio Playback +- **Objective**: Verify that app audio continues to play in the background for an extended period. +- **Steps**: + 1. Open the app and start a long interval timer. + 2. Minimize the app and leave it in the background for 5 minutes. + 3. After 5 minutes, confirm that the audio is still playing. +- **Pass Criteria**: + - Audio continues playing for at least 5 minutes without returning to the app. + +--- + +## Running Tests + +### Automated Integration Tests + +Integration tests will run automatically when a PR is opened. To run manually: + +1. Ensure you have all necessary dependencies installed. +2. Run integration tests with the following command: + ```bash + flutter drive + ``` +3. Confirm that all tests pass with no errors. + +### Manual QA Tests + +When contributing, please list what manual QA tests you were able to perform and please list device model, OS version, and any issues observed. It is not a requirement to perform all QA tests on each OS. Whatever tests you can perform are appreciated. diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index 663b340..8913c63 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -1,5 +1,5 @@ - + CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/example/ios/Podfile b/example/ios/Podfile index ed72b31..08c5231 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 7653a84..67f37dd 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,78 +1,60 @@ PODS: - audio_session (0.0.1): - Flutter - - audioplayers_darwin (0.0.1): - - Flutter - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) - flutter_background_service_ios (0.0.3): - Flutter - - flutter_fgbg (0.0.1): - - Flutter - flutter_local_notifications (0.0.1): - Flutter - - just_audio (0.0.1): - - Flutter - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS - soundpool (0.0.1): - Flutter + - sqflite_darwin (0.0.4): + - Flutter + - FlutterMacOS DEPENDENCIES: - audio_session (from `.symlinks/plugins/audio_session/ios`) - - audioplayers_darwin (from `.symlinks/plugins/audioplayers_darwin/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_background_service_ios (from `.symlinks/plugins/flutter_background_service_ios/ios`) - - flutter_fgbg (from `.symlinks/plugins/flutter_fgbg/ios`) - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`) - - just_audio (from `.symlinks/plugins/just_audio/ios`) - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - soundpool (from `.symlinks/plugins/soundpool/ios`) + - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`) EXTERNAL SOURCES: audio_session: :path: ".symlinks/plugins/audio_session/ios" - audioplayers_darwin: - :path: ".symlinks/plugins/audioplayers_darwin/ios" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter flutter_background_service_ios: :path: ".symlinks/plugins/flutter_background_service_ios/ios" - flutter_fgbg: - :path: ".symlinks/plugins/flutter_fgbg/ios" flutter_local_notifications: :path: ".symlinks/plugins/flutter_local_notifications/ios" - just_audio: - :path: ".symlinks/plugins/just_audio/ios" - path_provider_foundation: - :path: ".symlinks/plugins/path_provider_foundation/darwin" shared_preferences_foundation: :path: ".symlinks/plugins/shared_preferences_foundation/darwin" soundpool: :path: ".symlinks/plugins/soundpool/ios" + sqflite_darwin: + :path: ".symlinks/plugins/sqflite_darwin/darwin" SPEC CHECKSUMS: - audio_session: 4f3e461722055d21515cf3261b64c973c062f345 - audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 - device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + audio_session: 088d2483ebd1dc43f51d253d4a1c517d9a2e7207 + device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_background_service_ios: e30e0d3ee69e4cee66272d0c78eacd48c2e94aac - flutter_fgbg: 31c0d1140a131daea2d342121808f6aa0dcd879d - flutter_local_notifications: 0c0b1ae97e741e1521e4c1629a459d04b9aec743 - just_audio: baa7252489dbcf47a4c7cc9ca663e9661c99aafa - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + flutter_local_notifications: df98d66e515e1ca797af436137b4459b160ad8c9 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 soundpool: c7f4422ca206e77f8900ed3c4ee6a6ff5a0e38a9 + sqflite_darwin: 5a7236e3b501866c1c9befc6771dfd73ffb8702d -PODFILE CHECKSUM: e46e794ba0ddc397045dc2d44ed592903b330637 +PODFILE CHECKSUM: 30f8ec3cb135c39a41dbdc3f40009442f2639040 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index f06a724..c4c129d 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -215,7 +215,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -452,7 +452,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -580,7 +580,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -629,7 +629,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a0..8e3ca5d 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ audio fetch + processing UILaunchStoryboardName LaunchScreen diff --git a/example/lib/controls/control_bar.dart b/example/lib/controls/control_bar.dart new file mode 100644 index 0000000..1f4bd6c --- /dev/null +++ b/example/lib/controls/control_bar.dart @@ -0,0 +1,107 @@ +import 'package:example/controls/volume_bar.dart'; +import 'package:flutter/material.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class ControlBar extends StatefulWidget { + final VoidCallback onRestart; + final VoidCallback onTogglePlayPause; + final VoidCallback onAdjustVolume; + final VoidCallback onSkipNext; + final VoidCallback onSkipPrevious; + final bool paused; + final bool changeVolume; + final double volume; + + const ControlBar({ + super.key, + required this.onRestart, + required this.onTogglePlayPause, + required this.onAdjustVolume, + required this.onSkipNext, + required this.onSkipPrevious, + required this.paused, + required this.changeVolume, + required this.volume, + }); + + @override + ControlBarState createState() => ControlBarState(); +} + +class ControlBarState extends State { + double _currentSliderValue = .8; + + @override + void initState() { + super.initState(); + _loadVolume(); + } + + Future _loadVolume() async { + final prefs = await SharedPreferences.getInstance(); + setState(() { + _currentSliderValue = (prefs.getDouble('volume') ?? 80) / 100; + }); + } + + Future _saveVolume(double value) async { + final prefs = await SharedPreferences.getInstance(); + await prefs.setDouble('volume', value * 100); + } + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 80, + child: Stack( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: Icon( + widget.changeVolume ? Icons.close : Icons.volume_up, + color: Colors.white, + ), + onPressed: widget.onAdjustVolume, + ), + IconButton( + tooltip: 'Skip Previous', + icon: const Icon(Icons.skip_previous, color: Colors.white), + onPressed: widget.onSkipPrevious, + ), + IconButton( + tooltip: 'Pause', + icon: Icon( + widget.paused ? Icons.play_arrow : Icons.pause, + color: Colors.white, + ), + onPressed: widget.onTogglePlayPause, + ), + IconButton( + tooltip: 'Skip Next', + icon: const Icon(Icons.skip_next, color: Colors.white), + onPressed: widget.onSkipNext, + ), + IconButton( + tooltip: 'Restart', + icon: const Icon(Icons.restart_alt, color: Colors.white), + onPressed: widget.onRestart, + ), + ], + ), + if (widget.changeVolume) + VolumeBar( + volume: _currentSliderValue, + onVolumeChanged: (double value) { + setState(() { + _currentSliderValue = value; + }); + _saveVolume(value); + }, + ), + ], + ), + ); + } +} diff --git a/example/lib/controls/volume_bar.dart b/example/lib/controls/volume_bar.dart new file mode 100644 index 0000000..b21df90 --- /dev/null +++ b/example/lib/controls/volume_bar.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class VolumeBar extends StatelessWidget { + final double volume; + final Function(double) onVolumeChanged; + + const VolumeBar( + {super.key, required this.volume, required this.onVolumeChanged}); + + @override + Widget build(BuildContext context) { + return Positioned( + top: 0.0, + right: 20.0, + child: Material( + elevation: 4.0, + borderRadius: BorderRadius.circular(8.0), + child: Container( + padding: const EdgeInsets.all(8.0), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(8.0), + ), + child: Slider( + value: volume, + min: 0.0, + max: 1.0, + onChanged: onVolumeChanged, + ), + ), + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index d268384..3de1ec9 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,15 +1,18 @@ import 'package:audio_session/audio_session.dart'; +import 'package:background_hiit_timer/models/interval_type.dart'; +import 'package:background_hiit_timer/models/timer_state.dart'; +import 'package:example/controls/control_bar.dart'; import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:background_hiit_timer/background_timer.dart'; import 'package:background_hiit_timer/background_timer_controller.dart'; -import 'package:background_hiit_timer/background_timer_data.dart'; +import "package:flutter_driver/driver_extension.dart"; -void main() => runApp(const MyApp()); +Future main() async { + enableFlutterDriverExtension(); + runApp(const MyApp()); +} -/// -/// Test app -/// class MyApp extends StatelessWidget { const MyApp({super.key}); @@ -20,50 +23,108 @@ class MyApp extends StatelessWidget { theme: ThemeData( primarySwatch: Colors.blue, ), - home: const MyHomePage( - title: 'Flutter Demo Countdown', - ), + home: const MyHomePage(title: 'Flutter Demo Countdown'), ); } } -/// -/// Home page -/// class MyHomePage extends StatefulWidget { - /// - /// AppBar title - /// final String title; - - /// Home page - const MyHomePage({ - Key? key, - required this.title, - }) : super(key: key); + const MyHomePage({super.key, required this.title}); @override MyHomePageState createState() => MyHomePageState(); } -/// -/// Page state -/// class MyHomePageState extends State { - // Controller final CountdownController _controller = CountdownController(autoStart: true); - double _currentSliderValue = 80; + List listItems = []; + List removedItems = []; + bool _paused = false; + bool _changeVolume = false; + double _volume = .8; + late SharedPreferences prefs; + + final List intervals = [ + IntervalType( + id: "0", + workoutId: "1", + time: 5, + name: "Get ready", + color: 0, + intervalIndex: 0, + startSound: "", + halfwaySound: "", + countdownSound: "countdown-beep", + endSound: ""), + IntervalType( + id: "1", + workoutId: "1", + time: 5, + name: "Warmup", + color: 0, + intervalIndex: 1, + startSound: "long-bell", + halfwaySound: "", + countdownSound: "countdown-beep", + endSound: ""), + IntervalType( + id: "2", + workoutId: "1", + time: 5, + name: "Work", + color: 0, + intervalIndex: 2, + startSound: "long-bell", + halfwaySound: "short-halfway-beep", + countdownSound: "countdown-beep", + endSound: ""), + IntervalType( + id: "3", + workoutId: "1", + time: 5, + name: "Rest", + color: 0, + intervalIndex: 3, + startSound: "long-rest-beep", + halfwaySound: "", + countdownSound: "countdown-beep", + endSound: ""), + IntervalType( + id: "4", + workoutId: "1", + time: 5, + name: "Cooldown", + color: 0, + intervalIndex: 4, + startSound: "long-rest-beep", + countdownSound: "countdown-beep", + endSound: "horn", + halfwaySound: ""), + ]; + + static const Map intervalColors = { + "Work": Colors.green, + "Rest": Colors.red, + "Get ready": Colors.black, + "Warmup": Colors.orange, + "Cooldown": Colors.blue, + "End": Colors.purple, + }; @override - initState() { + void initState() { super.initState(); - init(); + initializeAudioSession(); + loadPreferences(); + listItems = intervals.map((interval) => interval.intervalIndex).toList(); } - void init() async { + Future initializeAudioSession() async { final session = await AudioSession.instance; + await session.configure(const AudioSessionConfiguration( - avAudioSessionCategory: AVAudioSessionCategory.ambient, + avAudioSessionCategory: AVAudioSessionCategory.playback, avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.mixWithOthers, avAudioSessionMode: AVAudioSessionMode.defaultMode, @@ -71,122 +132,105 @@ class MyHomePageState extends State { AVAudioSessionRouteSharingPolicy.defaultPolicy, avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none, androidAudioAttributes: AndroidAudioAttributes( - contentType: AndroidAudioContentType.speech, - flags: AndroidAudioFlags.none, - usage: AndroidAudioUsage.voiceCommunication, + contentType: AndroidAudioContentType.sonification, + flags: AndroidAudioFlags.audibilityEnforced, + usage: AndroidAudioUsage.notification, ), androidAudioFocusGainType: AndroidAudioFocusGainType.gain, androidWillPauseWhenDucked: true, )); } - Future setVolume(double volume) async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.setDouble('volume', volume); + Future loadPreferences() async { + prefs = await SharedPreferences.getInstance(); + setState(() { + _volume = prefs.getDouble('volume') ?? .8; + _changeVolume = prefs.getBool('changeVolume') ?? false; + }); + } + + Future toggleVolumeSlider() async { + setState(() { + _changeVolume = !_changeVolume; + }); + await prefs.setBool('changeVolume', _changeVolume); } - Color backgroundColor(String status) { - switch (status) { - case "work": - return Colors.green; - case "rest": - return Colors.red; - case "start": - return Colors.black; - case "break": - return Colors.teal; - case "warmup": - return Colors.orange; - case "cooldown": - return Colors.blue; - default: - return const Color.fromARGB(255, 0, 225, 255); - } + Future togglePause() async { + setState(() { + _paused = !_paused; + }); + _paused ? _controller.pause() : _controller.resume(); } + Color getBackgroundColor(String status) => + intervalColors[status] ?? const Color.fromARGB(255, 0, 225, 255); + @override Widget build(BuildContext context) { return Scaffold( body: Countdown( - controller: _controller, - workSeconds: 4, - restSeconds: 2, - getreadySeconds: 7, - breakSeconds: 5, - warmupSeconds: 10, - cooldownSeconds: 10, - numberOfWorkIntervals: 1, - iterations: 0, - onFinished: () {}, - build: (_, BackgroundTimerData timerData) { - return Container( - color: backgroundColor(timerData.status), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - timerData.status, - style: const TextStyle(fontSize: 50, color: Colors.white), - ), - Text( - timerData.currentMicroSeconds.toString(), - style: const TextStyle(fontSize: 100, color: Colors.white), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: 16, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - // Start - ElevatedButton( - child: const Text('Start'), - onPressed: () { - _controller.start(); - }, - ), - // Pause - ElevatedButton( - child: const Text('Pause'), - onPressed: () { - _controller.pause(); - }, - ), - // Resume - ElevatedButton( - child: const Text('Resume'), - onPressed: () { - _controller.resume(); - }, - ), - // Stop - ElevatedButton( - child: const Text('Restart'), - onPressed: () { - _controller.restart(); - }, - ), - ], - ), - ), - Slider( - value: _currentSliderValue, - max: 100, - divisions: 10, - label: _currentSliderValue.round().toString(), - onChanged: (double value) async { - setState(() { - _currentSliderValue = value; - }); - await setVolume(value); + controller: _controller, + intervals: intervals, + onFinished: () {}, + build: (_, TimerState timerState) { + while (listItems.length + timerState.currentInterval > + intervals.length) { + removedItems.add(listItems[0]); + listItems.removeAt(0); + } + + while (listItems.length + timerState.currentInterval < + intervals.length) { + listItems.insert(0, removedItems[removedItems.length - 1]); + removedItems.removeAt(removedItems.length - 1); + } + + return Container( + color: getBackgroundColor(timerState.status), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text(timerState.status, + style: const TextStyle(fontSize: 50, color: Colors.white)), + Text( + (timerState.currentMicroSeconds / + const Duration(seconds: 1).inMicroseconds) + .round() + .toString(), + style: const TextStyle(fontSize: 100, color: Colors.white)), + ControlBar( + onRestart: () => _controller.restart(), + paused: _paused, + changeVolume: _changeVolume, + volume: _volume, + onTogglePlayPause: togglePause, + onAdjustVolume: toggleVolumeSlider, + onSkipNext: _controller.skipNext, + onSkipPrevious: _controller.skipPrevious, + ), + SizedBox( + height: 220, + child: ListView.builder( + itemCount: listItems.length, + itemBuilder: (context, index) { + return Container( + color: const Color.fromARGB(64, 0, 0, 0), + child: ListTile( + title: Text( + intervals[listItems[index]].name, + style: const TextStyle(color: Colors.white), + ), + )); }, ), - ], - ), - ); - }), + ) + ], + ), + ); + }, + ), ); } } diff --git a/example/linux/.gitignore b/example/linux/.gitignore deleted file mode 100644 index d3896c9..0000000 --- a/example/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt deleted file mode 100644 index d67bd4e..0000000 --- a/example/linux/CMakeLists.txt +++ /dev/null @@ -1,139 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "example") -# The unique GTK application identifier for this application. See: -# https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.example") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(SET CMP0063 NEW) - -# Load bundled libraries from the lib/ directory relative to the binary. -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Root filesystem for cross-building. -if(FLUTTER_TARGET_PLATFORM_SYSROOT) - set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) - set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) - set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) - set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -endif() - -# Define build configuration options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Define the application target. To change its name, change BINARY_NAME above, -# not the value here, or `flutter run` will no longer work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add dependency libraries. Add any application-specific dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) - -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) - install(FILES "${bundled_library}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endforeach(bundled_library) - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/example/linux/flutter/CMakeLists.txt b/example/linux/flutter/CMakeLists.txt deleted file mode 100644 index d5bd016..0000000 --- a/example/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,88 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/example/linux/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 1830e5c..0000000 --- a/example/linux/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void fl_register_plugins(FlPluginRegistry* registry) { - g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); - audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); -} diff --git a/example/linux/flutter/generated_plugin_registrant.h b/example/linux/flutter/generated_plugin_registrant.h deleted file mode 100644 index e0f0a47..0000000 --- a/example/linux/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void fl_register_plugins(FlPluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/linux/flutter/generated_plugins.cmake b/example/linux/flutter/generated_plugins.cmake deleted file mode 100644 index e9abb91..0000000 --- a/example/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - audioplayers_linux -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/example/linux/main.cc b/example/linux/main.cc deleted file mode 100644 index e7c5c54..0000000 --- a/example/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc deleted file mode 100644 index 0ba8f43..0000000 --- a/example/linux/my_application.cc +++ /dev/null @@ -1,104 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "example"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "example"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject* object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - "flags", G_APPLICATION_NON_UNIQUE, - nullptr)); -} diff --git a/example/linux/my_application.h b/example/linux/my_application.h deleted file mode 100644 index 72271d5..0000000 --- a/example/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/example/macos/.gitignore b/example/macos/.gitignore deleted file mode 100644 index 746adbb..0000000 --- a/example/macos/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/dgph -**/xcuserdata/ diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig deleted file mode 100644 index 4b81f9b..0000000 --- a/example/macos/Flutter/Flutter-Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig deleted file mode 100644 index 5caa9d1..0000000 --- a/example/macos/Flutter/Flutter-Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/example/macos/Flutter/GeneratedPluginRegistrant.swift deleted file mode 100644 index 06c72b1..0000000 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ /dev/null @@ -1,26 +0,0 @@ -// -// Generated file. Do not edit. -// - -import FlutterMacOS -import Foundation - -import audio_session -import audioplayers_darwin -import device_info_plus -import flutter_local_notifications -import just_audio -import path_provider_foundation -import shared_preferences_foundation -import soundpool_macos - -func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { - AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) - AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin")) - DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) - FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) - JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) - PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) - SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) - SwiftSoundpoolPlugin.register(with: registry.registrar(forPlugin: "SwiftSoundpoolPlugin")) -} diff --git a/example/macos/Podfile b/example/macos/Podfile deleted file mode 100644 index c795730..0000000 --- a/example/macos/Podfile +++ /dev/null @@ -1,43 +0,0 @@ -platform :osx, '10.14' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) - target 'RunnerTests' do - inherit! :search_paths - end -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/example/macos/Podfile.lock b/example/macos/Podfile.lock deleted file mode 100644 index 9b6c027..0000000 --- a/example/macos/Podfile.lock +++ /dev/null @@ -1,66 +0,0 @@ -PODS: - - audio_session (0.0.1): - - FlutterMacOS - - audioplayers_darwin (0.0.1): - - FlutterMacOS - - device_info_plus (0.0.1): - - FlutterMacOS - - flutter_local_notifications (0.0.1): - - FlutterMacOS - - FlutterMacOS (1.0.0) - - just_audio (0.0.1): - - FlutterMacOS - - path_provider_foundation (0.0.1): - - Flutter - - FlutterMacOS - - shared_preferences_foundation (0.0.1): - - Flutter - - FlutterMacOS - - soundpool_macos (1.0.0): - - FlutterMacOS - -DEPENDENCIES: - - audio_session (from `Flutter/ephemeral/.symlinks/plugins/audio_session/macos`) - - audioplayers_darwin (from `Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos`) - - device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`) - - flutter_local_notifications (from `Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos`) - - FlutterMacOS (from `Flutter/ephemeral`) - - just_audio (from `Flutter/ephemeral/.symlinks/plugins/just_audio/macos`) - - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - - soundpool_macos (from `Flutter/ephemeral/.symlinks/plugins/soundpool_macos/macos`) - -EXTERNAL SOURCES: - audio_session: - :path: Flutter/ephemeral/.symlinks/plugins/audio_session/macos - audioplayers_darwin: - :path: Flutter/ephemeral/.symlinks/plugins/audioplayers_darwin/macos - device_info_plus: - :path: Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos - flutter_local_notifications: - :path: Flutter/ephemeral/.symlinks/plugins/flutter_local_notifications/macos - FlutterMacOS: - :path: Flutter/ephemeral - just_audio: - :path: Flutter/ephemeral/.symlinks/plugins/just_audio/macos - path_provider_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin - shared_preferences_foundation: - :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin - soundpool_macos: - :path: Flutter/ephemeral/.symlinks/plugins/soundpool_macos/macos - -SPEC CHECKSUMS: - audio_session: dea1f41890dbf1718f04a56f1d6150fd50039b72 - audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c - device_info_plus: 5401765fde0b8d062a2f8eb65510fb17e77cf07f - flutter_local_notifications: 3805ca215b2fb7f397d78b66db91f6a747af52e4 - FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - just_audio: 9b67ca7b97c61cfc9784ea23cd8cc55eb226d489 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c - shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 - soundpool_macos: 3bc0b8b238407516ad9b8afc2647670ff58124f9 - -PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 - -COCOAPODS: 1.12.1 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 6aa0f66..0000000 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,791 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 54; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 4CBC4E32A6758C1921A22ABD /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 734C362E01E31133B5815453 /* Pods_Runner.framework */; }; - 7E68347F0577D553A6434950 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0B5F4A2415B25672C97AF361 /* Pods_RunnerTests.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC10EC2044A3C60003C045; - remoteInfo = Runner; - }; - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 0B5F4A2415B25672C97AF361 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 17DA1C30D1B8CB6EA75F6D9D /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 43D50BF7EA9DD913E9F3537F /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 4E51F7DE7BD7C0317D656A47 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - 734C362E01E31133B5815453 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - 9A2250D13B0A8CB543A079DA /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; - A2732DC39A111E278C5C2921 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - F8B0E3C5BAFB267A2D0A0FB4 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 331C80D2294CF70F00263BE5 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7E68347F0577D553A6434950 /* Pods_RunnerTests.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 4CBC4E32A6758C1921A22ABD /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 331C80D6294CF71000263BE5 /* RunnerTests */ = { - isa = PBXGroup; - children = ( - 331C80D7294CF71000263BE5 /* RunnerTests.swift */, - ); - path = RunnerTests; - sourceTree = ""; - }; - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 331C80D6294CF71000263BE5 /* RunnerTests */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 6582887D17B9814E072009AE /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* example.app */, - 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 6582887D17B9814E072009AE /* Pods */ = { - isa = PBXGroup; - children = ( - 17DA1C30D1B8CB6EA75F6D9D /* Pods-Runner.debug.xcconfig */, - A2732DC39A111E278C5C2921 /* Pods-Runner.release.xcconfig */, - 43D50BF7EA9DD913E9F3537F /* Pods-Runner.profile.xcconfig */, - 9A2250D13B0A8CB543A079DA /* Pods-RunnerTests.debug.xcconfig */, - F8B0E3C5BAFB267A2D0A0FB4 /* Pods-RunnerTests.release.xcconfig */, - 4E51F7DE7BD7C0317D656A47 /* Pods-RunnerTests.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 734C362E01E31133B5815453 /* Pods_Runner.framework */, - 0B5F4A2415B25672C97AF361 /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 331C80D4294CF70F00263BE5 /* RunnerTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; - buildPhases = ( - 818A115978032575FF8679D8 /* [CP] Check Pods Manifest.lock */, - 331C80D1294CF70F00263BE5 /* Sources */, - 331C80D2294CF70F00263BE5 /* Frameworks */, - 331C80D3294CF70F00263BE5 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 331C80DA294CF71000263BE5 /* PBXTargetDependency */, - ); - name = RunnerTests; - productName = RunnerTests; - productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - D35514A0343F7E13C238FEA8 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - EC0CE66D7D5CE3F846E5714D /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* example.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 331C80D4294CF70F00263BE5 = { - CreatedOnToolsVersion = 14.0; - TestTargetID = 33CC10EC2044A3C60003C045; - }; - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 331C80D4294CF70F00263BE5 /* RunnerTests */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 331C80D3294CF70F00263BE5 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; - 818A115978032575FF8679D8 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - D35514A0343F7E13C238FEA8 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - EC0CE66D7D5CE3F846E5714D /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 331C80D1294CF70F00263BE5 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC10EC2044A3C60003C045 /* Runner */; - targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; - }; - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 331C80DB294CF71000263BE5 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9A2250D13B0A8CB543A079DA /* Pods-RunnerTests.debug.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; - }; - name = Debug; - }; - 331C80DC294CF71000263BE5 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = F8B0E3C5BAFB267A2D0A0FB4 /* Pods-RunnerTests.release.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; - }; - name = Release; - }; - 331C80DD294CF71000263BE5 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 4E51F7DE7BD7C0317D656A47 /* Pods-RunnerTests.profile.xcconfig */; - buildSettings = { - BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; - }; - name = Profile; - }; - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 331C80DB294CF71000263BE5 /* Debug */, - 331C80DC294CF71000263BE5 /* Release */, - 331C80DD294CF71000263BE5 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index 397f3d3..0000000 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/example/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc1..0000000 --- a/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d9810..0000000 --- a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef64..0000000 --- a/example/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f..0000000 --- a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 82b6f9d..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index 13b35eb..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png deleted file mode 100644 index 0a3f5fa..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bdb5722..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png deleted file mode 100644 index f083318..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index 326c0e7..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png deleted file mode 100644 index 2f1632c..0000000 Binary files a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and /dev/null differ diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/example/macos/Runner/Base.lproj/MainMenu.xib deleted file mode 100644 index 80e867a..0000000 --- a/example/macos/Runner/Base.lproj/MainMenu.xib +++ /dev/null @@ -1,343 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index dda192b..0000000 --- a/example/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = example - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.example - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/example/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd9..0000000 --- a/example/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/example/macos/Runner/Configs/Release.xcconfig b/example/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f49..0000000 --- a/example/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/example/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf4..0000000 --- a/example/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/example/macos/Runner/DebugProfile.entitlements b/example/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a3..0000000 --- a/example/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/example/macos/Runner/Info.plist b/example/macos/Runner/Info.plist deleted file mode 100644 index 4789daa..0000000 --- a/example/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/example/macos/Runner/MainFlutterWindow.swift b/example/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 3cc05eb..0000000 --- a/example/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/example/macos/Runner/Release.entitlements b/example/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a..0000000 --- a/example/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/example/macos/RunnerTests/RunnerTests.swift b/example/macos/RunnerTests/RunnerTests.swift deleted file mode 100644 index 5418c9f..0000000 --- a/example/macos/RunnerTests/RunnerTests.swift +++ /dev/null @@ -1,12 +0,0 @@ -import FlutterMacOS -import Cocoa -import XCTest - -class RunnerTests: XCTestCase { - - func testExample() { - // If you add code to the Runner application, consider adding tests here. - // See https://developer.apple.com/documentation/xctest for more information about using XCTest. - } - -} diff --git a/example/pubspec.lock b/example/pubspec.lock index 7804eda..dc15187 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -1,101 +1,66 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - audio_session: - dependency: "direct main" - description: - name: audio_session - sha256: a49af9981eec5d7cd73b37bacb6ee73f8143a6a9f9bd5b6021e6c346b9b6cf4e - url: "https://pub.dev" - source: hosted - version: "0.1.19" - audioplayers: - dependency: transitive - description: - name: audioplayers - sha256: "752039d6aa752597c98ec212e9759519061759e402e7da59a511f39d43aa07d2" - url: "https://pub.dev" - source: hosted - version: "6.0.0" - audioplayers_android: + _fe_analyzer_shared: dependency: transitive description: - name: audioplayers_android - sha256: de576b890befe27175c2f511ba8b742bec83765fa97c3ce4282bba46212f58e4 + name: _fe_analyzer_shared + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "5.0.0" - audioplayers_darwin: + version: "72.0.0" + _macros: dependency: transitive - description: - name: audioplayers_darwin - sha256: e507887f3ff18d8e5a10a668d7bedc28206b12e10b98347797257c6ae1019c3b - url: "https://pub.dev" - source: hosted - version: "6.0.0" - audioplayers_linux: + description: dart + source: sdk + version: "0.3.2" + analyzer: dependency: transitive description: - name: audioplayers_linux - sha256: "3d3d244c90436115417f170426ce768856d8fe4dfc5ed66a049d2890acfa82f9" + name: analyzer + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "4.0.0" - audioplayers_platform_interface: + version: "6.7.0" + args: dependency: transitive description: - name: audioplayers_platform_interface - sha256: "6834dd48dfb7bc6c2404998ebdd161f79cd3774a7e6779e1348d54a3bfdcfaa5" + name: args + sha256: bf9f5caeea8d8fe6721a9c358dd8a5c1947b27f1cfaa18b39c301273594919e6 url: "https://pub.dev" source: hosted - version: "7.0.0" - audioplayers_web: + version: "2.6.0" + async: dependency: transitive description: - name: audioplayers_web - sha256: db8fc420dadf80da18e2286c18e746fb4c3b2c5adbf0c963299dde046828886d + name: async + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "5.0.0" - audioplayers_windows: - dependency: transitive + version: "2.12.0" + audio_session: + dependency: "direct main" description: - name: audioplayers_windows - sha256: "8605762dddba992138d476f6a0c3afd9df30ac5b96039929063eceed416795c2" + name: audio_session + sha256: "343e83bc7809fbda2591a49e525d6b63213ade10c76f15813be9aed6657b3261" url: "https://pub.dev" source: hosted - version: "4.0.0" + version: "0.1.21" background_hiit_timer: dependency: "direct main" description: path: ".." relative: true source: path - version: "1.0.0-dev.3" + version: "1.0.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: @@ -108,10 +73,10 @@ packages: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: @@ -120,14 +85,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" + coverage: + dependency: transitive + description: + name: coverage + sha256: "4b03e11f6d5b8f6e5bb5e9f7889a56fe6c5cbe942da5378ea4d4d7f73ef9dfe5" + url: "https://pub.dev" + source: hosted + version: "1.11.0" crypto: dependency: transitive description: name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.6" dbus: dependency: transitive description: @@ -140,95 +121,116 @@ packages: dependency: transitive description: name: device_info_plus - sha256: eead12d1a1ed83d8283ab4c2f3fca23ac4082f29f25f29dff0f758f57d06ec91 + sha256: f545ffbadee826f26f2e1a0f0cbd667ae9a6011cc0f77c0f8f00a969655e6e95 url: "https://pub.dev" source: hosted - version: "10.1.0" + version: "11.1.1" device_info_plus_platform_interface: dependency: transitive description: name: device_info_plus_platform_interface - sha256: d3b01d5868b50ae571cd1dc6e502fc94d956b665756180f7b16ead09e836fd64 + sha256: "282d3cf731045a2feb66abfe61bbc40870ae50a3ed10a4d3d217556c35c8c2ba" url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "7.0.1" fake_async: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" ffi: dependency: transitive description: name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" file: dependency: transitive description: name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "7.0.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" - flutter_fgbg: + flutter_background_service: + dependency: transitive + description: + name: flutter_background_service + sha256: d32f078ec57647c9cfd6e1a8da9297f7d8f021d4dcc204a35aaad2cdbfe255f0 + url: "https://pub.dev" + source: hosted + version: "5.0.10" + flutter_background_service_android: + dependency: transitive + description: + name: flutter_background_service_android + sha256: "39da42dddf877beeef82bc2583130d8bedb4d0765e99ca9e7b4a32e8c6abd239" + url: "https://pub.dev" + source: hosted + version: "6.2.7" + flutter_background_service_ios: dependency: transitive description: - name: flutter_fgbg - sha256: "08c4d2fd229e3df26083d5aecc3dea9ff4f2d188f8cd57aaf2b3f047bd08a047" + name: flutter_background_service_ios + sha256: "6037ffd45c4d019dab0975c7feb1d31012dd697e25edc05505a4a9b0c7dc9fba" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "5.0.3" + flutter_background_service_platform_interface: + dependency: transitive + description: + name: flutter_background_service_platform_interface + sha256: ca74aa95789a8304f4d3f57f07ba404faa86bed6e415f83e8edea6ad8b904a41 + url: "https://pub.dev" + source: hosted + version: "5.1.2" + flutter_driver: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints - sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" url: "https://pub.dev" source: hosted - version: "2.0.3" + version: "5.0.0" flutter_local_notifications: dependency: transitive description: name: flutter_local_notifications - sha256: "40e6fbd2da7dcc7ed78432c5cdab1559674b4af035fddbfb2f9a8f9c2112fcef" + sha256: "725145682706fb0e5a30f93e5cb64f3df7ed7743de749bd555b22bf75ee718c0" url: "https://pub.dev" source: hosted - version: "17.1.2" + version: "18.0.0" flutter_local_notifications_linux: dependency: transitive description: name: flutter_local_notifications_linux - sha256: "33f741ef47b5f63cc7f78fe75eeeac7e19f171ff3c3df054d84c1e38bedb6a03" + sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01" url: "https://pub.dev" source: hosted - version: "4.0.0+1" + version: "5.0.0" flutter_local_notifications_platform_interface: dependency: transitive description: name: flutter_local_notifications_platform_interface - sha256: "340abf67df238f7f0ef58f4a26d2a83e1ab74c77ab03cd2b2d5018ac64db30b7" + sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52" url: "https://pub.dev" source: hosted - version: "7.1.0" + version: "8.0.0" flutter_test: dependency: "direct dev" description: flutter @@ -239,62 +241,83 @@ packages: description: flutter source: sdk version: "0.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" http: dependency: transitive description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.1" - http_parser: + version: "1.2.2" + http_multi_server: dependency: transitive description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" url: "https://pub.dev" source: hosted - version: "4.0.2" - just_audio: + version: "3.2.1" + http_parser: dependency: transitive description: - name: just_audio - sha256: "5abfab1d199e01ab5beffa61b3e782350df5dad036cb8c83b79fa45fc656614e" + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted - version: "0.9.38" - just_audio_platform_interface: + version: "4.0.2" + io: dependency: transitive description: - name: just_audio_platform_interface - sha256: "0243828cce503c8366cc2090cefb2b3c871aa8ed2f520670d76fd47aa1ab2790" + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" url: "https://pub.dev" source: hosted - version: "4.3.0" - just_audio_web: + version: "1.0.4" + js: dependency: transitive description: - name: just_audio_web - sha256: "0edb481ad4aa1ff38f8c40f1a3576013c3420bf6669b686fe661627d49bc606c" + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf url: "https://pub.dev" source: hosted - version: "0.4.11" + version: "0.7.1" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.5" leak_tracker_testing: dependency: transitive description: @@ -307,98 +330,90 @@ packages: dependency: transitive description: name: lints - sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: "3315600f3fb3b135be672bf4a178c55f274bebe368325ae18462c89ac1e3b413" url: "https://pub.dev" source: hosted - version: "0.12.16+1" - material_color_utilities: + version: "5.0.0" + logger: dependency: transitive description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + name: logger + sha256: "697d067c60c20999686a0add96cf6aba723b3aa1f83ecf806a8097231529ec32" url: "https://pub.dev" source: hosted - version: "0.8.0" - meta: + version: "2.4.0" + logging: dependency: transitive description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 url: "https://pub.dev" source: hosted - version: "1.12.0" - openhiit_background_service: + version: "1.3.0" + macros: dependency: transitive description: - name: openhiit_background_service - sha256: "25f4f98ec12c735fcba2aaf814422d7ee151dde4de5887fdbbec9013fc90c087" + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" url: "https://pub.dev" source: hosted - version: "1.0.0-dev.3" - openhiit_background_service_android: + version: "0.1.2-main.4" + matcher: dependency: transitive description: - name: openhiit_background_service_android - sha256: "97a6fe921708bd624c9a430440260822bce2d39f986ec823a8702abb12ba6c06" + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "1.0.0-dev.3" - openhiit_background_service_ios: + version: "0.12.16+1" + material_color_utilities: dependency: transitive description: - name: openhiit_background_service_ios - sha256: ed0449509310db836a7bb210f7f48850ef1425bd8e73fe9e4e265f67c2a662ec + name: material_color_utilities + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "1.0.0-dev.3" - openhiit_background_service_platform_interface: + version: "0.11.1" + meta: dependency: transitive description: - name: openhiit_background_service_platform_interface - sha256: "98316593f32dd334ea25c3d4a1552e0c8b9b6ac8fbc5c47676c816d420318976" + name: meta + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.0.0-dev.3" - path: + version: "1.15.0" + mime: dependency: transitive description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" url: "https://pub.dev" source: hosted - version: "1.9.0" - path_provider: + version: "2.0.0" + node_preamble: dependency: transitive description: - name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 + name: node_preamble + sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" url: "https://pub.dev" source: hosted - version: "2.1.3" - path_provider_android: + version: "2.0.2" + package_config: dependency: transitive description: - name: path_provider_android - sha256: "9c96da072b421e98183f9ea7464898428e764bc0ce5567f27ec8693442e72514" + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" url: "https://pub.dev" source: hosted - version: "2.2.5" - path_provider_foundation: + version: "2.1.0" + path: dependency: transitive description: - name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + name: path + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "1.9.1" path_provider_linux: dependency: transitive description: @@ -419,10 +434,10 @@ packages: dependency: transitive description: name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted - version: "2.2.1" + version: "2.3.0" petitparser: dependency: transitive description: @@ -435,10 +450,10 @@ packages: dependency: transitive description: name: platform - sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" + sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" url: "https://pub.dev" source: hosted - version: "3.1.5" + version: "3.1.6" plugin_platform_interface: dependency: transitive description: @@ -447,70 +462,126 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d" + url: "https://pub.dev" + source: hosted + version: "5.0.3" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" rxdart: dependency: transitive description: name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" url: "https://pub.dev" source: hosted - version: "0.27.7" + version: "0.28.0" shared_preferences: - dependency: transitive + dependency: "direct main" description: name: shared_preferences - sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 + sha256: "95f9997ca1fb9799d494d0cb2a780fd7be075818d59f00c43832ed112b158a82" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.3" shared_preferences_android: dependency: transitive description: name: shared_preferences_android - sha256: "93d0ec9dd902d85f326068e6a899487d1f65ffcd5798721a95330b26c8131577" + sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab" url: "https://pub.dev" source: hosted - version: "2.2.3" + version: "2.3.3" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation - sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" + sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.5.3" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux - sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface - sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web - sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e url: "https://pub.dev" source: hosted - version: "2.3.0" + version: "2.4.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows - sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_packages_handler: + dependency: transitive + description: + name: shelf_packages_handler + sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" url: "https://pub.dev" source: hosted - version: "2.3.2" + version: "3.0.2" + shelf_static: + dependency: transitive + description: + name: shelf_static + sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3 + url: "https://pub.dev" + source: hosted + version: "1.1.3" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + url: "https://pub.dev" + source: hosted + version: "2.0.0" sky_engine: dependency: transitive description: flutter @@ -548,6 +619,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + source_map_stack_trace: + dependency: transitive + description: + name: source_map_stack_trace + sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b + url: "https://pub.dev" + source: hosted + version: "2.1.2" + source_maps: + dependency: transitive + description: + name: source_maps + sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + url: "https://pub.dev" + source: hosted + version: "0.10.12" source_span: dependency: transitive description: @@ -556,22 +643,70 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" - sprintf: + sqflite: + dependency: transitive + description: + name: sqflite + sha256: "2d7299468485dca85efeeadf5d38986909c5eb0cd71fd3db2c2f000e6c9454bb" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_android: + dependency: transitive + description: + name: sqflite_android + sha256: "78f489aab276260cdd26676d2169446c7ecd3484bbd5fead4ca14f3ed4dd9ee3" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqflite_common: + dependency: transitive + description: + name: sqflite_common + sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" + url: "https://pub.dev" + source: hosted + version: "2.5.4+5" + sqflite_common_ffi: dependency: transitive description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + name: sqflite_common_ffi + sha256: d316908f1537725427ff2827a5c5f3b2c1bc311caed985fe3c9b10939c9e11ca url: "https://pub.dev" source: hosted - version: "7.0.0" + version: "2.3.4" + sqflite_darwin: + dependency: transitive + description: + name: sqflite_darwin + sha256: "96a698e2bc82bd770a4d6aab00b42396a7c63d9e33513a56945cbccb594c2474" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + sqflite_platform_interface: + dependency: transitive + description: + name: sqflite_platform_interface + sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920" + url: "https://pub.dev" + source: hosted + version: "2.4.0" + sqlite3: + dependency: transitive + description: + name: sqlite3 + sha256: bb174b3ec2527f9c5f680f73a89af8149dd99782fbb56ea88ad0807c5638f2ed + url: "https://pub.dev" + source: hosted + version: "2.4.7" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "9f47fd3630d76be3ab26f0ee06d213679aa425996925ff3feffdec504931c377" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.0" stream_channel: dependency: transitive description: @@ -584,18 +719,26 @@ packages: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "0bd04f5bb74fcd6ff0606a888a30e917af9bd52820b178eaa464beb11dca84b6" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "0.3.1" synchronized: dependency: transitive description: name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" + sha256: "69fe30f3a8b04a0be0c15ae6490fc859a78ef4c43ae2dd5e8a623d45bfcf9225" url: "https://pub.dev" source: hosted - version: "3.1.0+1" + version: "3.3.0+3" term_glyph: dependency: transitive description: @@ -604,38 +747,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" + test: + dependency: "direct main" + description: + name: test + sha256: "713a8789d62f3233c46b4a90b174737b2c04cb6ae4500f2aa8b1be8f03f5e67f" + url: "https://pub.dev" + source: hosted + version: "1.25.8" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "0.7.0" - timezone: + version: "0.7.3" + test_core: dependency: transitive description: - name: timezone - sha256: a6ccda4a69a442098b602c44e61a1e2b4bf6f5516e875bbf0f427d5df14745d5 + name: test_core + sha256: "12391302411737c176b0b5d6491f466b0dd56d4763e347b6714efbaa74d7953d" url: "https://pub.dev" source: hosted - version: "0.9.3" - typed_data: + version: "0.6.5" + timezone: dependency: transitive description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + name: timezone + sha256: "2236ec079a174ce07434e89fcd3fcda430025eb7692244139a9cf54fdcf1fc7d" url: "https://pub.dev" source: hosted - version: "1.3.2" - uuid: + version: "0.9.4" + typed_data: dependency: transitive description: - name: uuid - sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 url: "https://pub.dev" source: hosted - version: "4.4.0" + version: "1.4.0" vector_math: dependency: transitive description: @@ -648,42 +799,82 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.3.1" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" web: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "3c12d96c0c9a4eec095246debcea7b86c0324f22df69893d538fcc6f1b8cce83" + url: "https://pub.dev" + source: hosted + version: "0.1.6" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "9f187088ed104edd8662ca07af4b124465893caf063ba29758f97af57e61da8f" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "3.0.1" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8" + url: "https://pub.dev" + source: hosted + version: "3.0.4" + webkit_inspection_protocol: + dependency: transitive + description: + name: webkit_inspection_protocol + sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" + url: "https://pub.dev" + source: hosted + version: "1.2.1" win32: dependency: transitive description: name: win32 - sha256: a79dbe579cb51ecd6d30b17e0cae4e0ea15e2c0e66f69ad4198f22a6789e94f4 + sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" url: "https://pub.dev" source: hosted - version: "5.5.1" + version: "5.8.0" win32_registry: dependency: transitive description: name: win32_registry - sha256: "10589e0d7f4e053f2c61023a31c9ce01146656a70b7b7f0828c0b46d7da2a9bb" + sha256: "21ec76dfc731550fd3e2ce7a33a9ea90b828fdf19a5c3bcf556fa992cfa99852" url: "https://pub.dev" source: hosted - version: "1.1.3" + version: "1.1.5" xdg_directories: dependency: transitive description: name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.1.0" xml: dependency: transitive description: @@ -692,6 +883,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" diff --git a/example/pubspec.yaml b/example/pubspec.yaml index 03f2730..d50aeee 100644 --- a/example/pubspec.yaml +++ b/example/pubspec.yaml @@ -12,13 +12,15 @@ dependencies: background_hiit_timer: path: "../" audio_session: ^0.1.16 + shared_preferences: ^2.3.2 + flutter_driver: + sdk: flutter + test: ^1.25.7 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^2.0.0 + flutter_lints: ^5.0.0 flutter: uses-material-design: true - assets: - - assets/audio/ diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart deleted file mode 100644 index a19dabb..0000000 --- a/example/test/widget_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility that Flutter provides. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:example/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} diff --git a/example/test_driver/main_test.dart b/example/test_driver/main_test.dart new file mode 100644 index 0000000..06b4178 --- /dev/null +++ b/example/test_driver/main_test.dart @@ -0,0 +1,10 @@ +// test_driver/main_test.dart +import 'tests/complete_test.dart' as complete_test; +import 'tests/pause_test.dart' as pause_test; +import 'tests/skip_test.dart' as skip_test; + +void main() { + complete_test.main(); + skip_test.main(); + pause_test.main(); +} diff --git a/example/test_driver/tests/complete_test.dart b/example/test_driver/tests/complete_test.dart new file mode 100644 index 0000000..f4ccf13 --- /dev/null +++ b/example/test_driver/tests/complete_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +void main() { + FlutterDriver? driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) { + await driver!.close(); + } + }); + + test('Integration Test - Timer Completes', () async { + // Wait for "Get ready" text + await driver! + .waitFor(find.text('Get ready'), timeout: const Duration(seconds: 5)); + + // Proceed to next state + await Future.delayed(const Duration(seconds: 5)); + await driver! + .waitFor(find.text('Warmup'), timeout: const Duration(seconds: 5)); + + // Check for next state + await Future.delayed(const Duration(seconds: 5)); + await driver! + .waitFor(find.text('Work'), timeout: const Duration(seconds: 5)); + + // Check for next state + await Future.delayed(const Duration(seconds: 5)); + await driver! + .waitFor(find.text('Rest'), timeout: const Duration(seconds: 5)); + + // Check for next state + await Future.delayed(const Duration(seconds: 5)); + await driver! + .waitFor(find.text('Cooldown'), timeout: const Duration(seconds: 5)); + + // Check for next state + await Future.delayed(const Duration(seconds: 5)); + await driver! + .waitFor(find.text('End'), timeout: const Duration(seconds: 5)); + + // Find and tap the restart icon. + final restartIcon = find.byTooltip('Restart'); + await driver!.tap(restartIcon); + }); +} diff --git a/example/test_driver/tests/pause_test.dart b/example/test_driver/tests/pause_test.dart new file mode 100644 index 0000000..8360dda --- /dev/null +++ b/example/test_driver/tests/pause_test.dart @@ -0,0 +1,34 @@ +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +void main() { + FlutterDriver? driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) { + await driver!.close(); + } + }); + + test('Integration Test - Timer Restart', () async { + // Wait for "Get ready" text + await driver! + .waitFor(find.text('Get ready'), timeout: const Duration(seconds: 10)); + + // Find and tap the pause icon. + final pauseIcon = find.byTooltip('Pause'); + await driver!.tap(pauseIcon); + + // Ensure paused + await Future.delayed(const Duration(seconds: 15)); + await driver! + .waitFor(find.text('Get ready'), timeout: const Duration(seconds: 5)); + + // Find and tap the pause icon. + await driver!.tap(pauseIcon); + }, timeout: const Timeout(Duration(minutes: 1))); +} diff --git a/example/test_driver/tests/skip_test.dart b/example/test_driver/tests/skip_test.dart new file mode 100644 index 0000000..742699a --- /dev/null +++ b/example/test_driver/tests/skip_test.dart @@ -0,0 +1,44 @@ +import 'package:flutter_driver/flutter_driver.dart'; +import 'package:test/test.dart'; + +void main() { + FlutterDriver? driver; + + setUpAll(() async { + driver = await FlutterDriver.connect(); + }); + + tearDownAll(() async { + if (driver != null) { + await driver!.close(); + } + }); + + test('Integration Test - Timer Restart', () async { + // Wait for "Get ready" text + await driver! + .waitFor(find.text('Get ready'), timeout: const Duration(seconds: 10)); + + // Find and tap the skip next icon. + final nextIcon = find.byTooltip('Skip Next'); + await driver!.tap(nextIcon); + + // Tap the skip next icon again. + await driver!.tap(nextIcon); + + // Get ready and Warmup skipped, so wait for "Work" text + await driver! + .waitFor(find.text('Work'), timeout: const Duration(seconds: 5)); + + // Find and tap the skip previous icon. + final previousIcon = find.byTooltip('Skip Previous'); + await driver!.tap(previousIcon); + + // Tap the skip previous icon again. + await driver!.tap(previousIcon); + + // Back on Get ready + await driver! + .waitFor(find.text('Get ready'), timeout: const Duration(seconds: 5)); + }); +} diff --git a/example/web/favicon.png b/example/web/favicon.png deleted file mode 100644 index 8aaa46a..0000000 Binary files a/example/web/favicon.png and /dev/null differ diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png deleted file mode 100644 index b749bfe..0000000 Binary files a/example/web/icons/Icon-192.png and /dev/null differ diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48..0000000 Binary files a/example/web/icons/Icon-512.png and /dev/null differ diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d7..0000000 Binary files a/example/web/icons/Icon-maskable-192.png and /dev/null differ diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c566..0000000 Binary files a/example/web/icons/Icon-maskable-512.png and /dev/null differ diff --git a/example/web/index.html b/example/web/index.html deleted file mode 100644 index 45cf2ca..0000000 --- a/example/web/index.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - - - - - - - - - - - - - - - example - - - - - - - - - - diff --git a/example/web/manifest.json b/example/web/manifest.json deleted file mode 100644 index 096edf8..0000000 --- a/example/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "example", - "short_name": "example", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/example/windows/.gitignore b/example/windows/.gitignore deleted file mode 100644 index d492d0d..0000000 --- a/example/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/example/windows/CMakeLists.txt b/example/windows/CMakeLists.txt deleted file mode 100644 index c09389c..0000000 --- a/example/windows/CMakeLists.txt +++ /dev/null @@ -1,102 +0,0 @@ -# Project-level configuration. -cmake_minimum_required(VERSION 3.14) -project(example LANGUAGES CXX) - -# The name of the executable created for the application. Change this to change -# the on-disk name of your application. -set(BINARY_NAME "example") - -# Explicitly opt in to modern CMake behaviors to avoid warnings with recent -# versions of CMake. -cmake_policy(VERSION 3.14...3.25) - -# Define build configuration option. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() -# Define settings for the Profile build mode. -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -# -# Be cautious about adding new options here, as plugins use this function by -# default. In most cases, you should add new options to specific targets instead -# of modifying this function. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -# Flutter library and tool build rules. -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build; see runner/CMakeLists.txt. -add_subdirectory("runner") - - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/example/windows/flutter/CMakeLists.txt b/example/windows/flutter/CMakeLists.txt deleted file mode 100644 index 930d207..0000000 --- a/example/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,104 +0,0 @@ -# This file controls Flutter-level build steps. It should not be edited. -cmake_minimum_required(VERSION 3.14) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" - "flutter_texture_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/example/windows/flutter/generated_plugin_registrant.cc deleted file mode 100644 index 09e8e2c..0000000 --- a/example/windows/flutter/generated_plugin_registrant.cc +++ /dev/null @@ -1,14 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#include "generated_plugin_registrant.h" - -#include - -void RegisterPlugins(flutter::PluginRegistry* registry) { - AudioplayersWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); -} diff --git a/example/windows/flutter/generated_plugin_registrant.h b/example/windows/flutter/generated_plugin_registrant.h deleted file mode 100644 index dc139d8..0000000 --- a/example/windows/flutter/generated_plugin_registrant.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// Generated file. Do not edit. -// - -// clang-format off - -#ifndef GENERATED_PLUGIN_REGISTRANT_ -#define GENERATED_PLUGIN_REGISTRANT_ - -#include - -// Registers Flutter plugins. -void RegisterPlugins(flutter::PluginRegistry* registry); - -#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/example/windows/flutter/generated_plugins.cmake b/example/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 375535c..0000000 --- a/example/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - audioplayers_windows -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/example/windows/runner/CMakeLists.txt b/example/windows/runner/CMakeLists.txt deleted file mode 100644 index 394917c..0000000 --- a/example/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.14) -project(runner LANGUAGES CXX) - -# Define the application target. To change its name, change BINARY_NAME in the -# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer -# work. -# -# Any new source files that you add to the application should be added here. -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) - -# Apply the standard set of build settings. This can be removed for applications -# that need different build settings. -apply_standard_settings(${BINARY_NAME}) - -# Add preprocessor definitions for the build version. -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") -target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") - -# Disable Windows macros that collide with C++ standard library functions. -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") - -# Add dependency libraries and include directories. Add any application-specific -# dependencies here. -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") - -# Run the Flutter tool portions of the build. This must not be removed. -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/example/windows/runner/Runner.rc b/example/windows/runner/Runner.rc deleted file mode 100644 index aecaa2b..0000000 --- a/example/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) -#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD -#else -#define VERSION_AS_NUMBER 1,0,0,0 -#endif - -#if defined(FLUTTER_VERSION) -#define VERSION_AS_STRING FLUTTER_VERSION -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "example" "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "example" "\0" - VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "example.exe" "\0" - VALUE "ProductName", "example" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/example/windows/runner/flutter_window.cpp b/example/windows/runner/flutter_window.cpp deleted file mode 100644 index 955ee30..0000000 --- a/example/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(const flutter::DartProject& project) - : project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - - flutter_controller_->engine()->SetNextFrameCallback([&]() { - this->Show(); - }); - - // Flutter can complete the first frame before the "show window" callback is - // registered. The following call ensures a frame is pending to ensure the - // window is shown. It is a no-op if the first frame hasn't completed yet. - flutter_controller_->ForceRedraw(); - - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opportunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/example/windows/runner/flutter_window.h b/example/windows/runner/flutter_window.h deleted file mode 100644 index 6da0652..0000000 --- a/example/windows/runner/flutter_window.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow hosting a Flutter view running |project|. - explicit FlutterWindow(const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/example/windows/runner/main.cpp b/example/windows/runner/main.cpp deleted file mode 100644 index a61bf80..0000000 --- a/example/windows/runner/main.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.Create(L"example", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - ::MSG msg; - while (::GetMessage(&msg, nullptr, 0, 0)) { - ::TranslateMessage(&msg); - ::DispatchMessage(&msg); - } - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/example/windows/runner/resource.h b/example/windows/runner/resource.h deleted file mode 100644 index 66a65d1..0000000 --- a/example/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/example/windows/runner/resources/app_icon.ico b/example/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20c..0000000 Binary files a/example/windows/runner/resources/app_icon.ico and /dev/null differ diff --git a/example/windows/runner/runner.exe.manifest b/example/windows/runner/runner.exe.manifest deleted file mode 100644 index a42ea76..0000000 --- a/example/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/example/windows/runner/utils.cpp b/example/windows/runner/utils.cpp deleted file mode 100644 index b2b0873..0000000 --- a/example/windows/runner/utils.cpp +++ /dev/null @@ -1,65 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr) - -1; // remove the trailing null character - int input_length = (int)wcslen(utf16_string); - std::string utf8_string; - if (target_length <= 0 || target_length > utf8_string.max_size()) { - return utf8_string; - } - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - input_length, utf8_string.data(), target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/example/windows/runner/utils.h b/example/windows/runner/utils.h deleted file mode 100644 index 3879d54..0000000 --- a/example/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/example/windows/runner/win32_window.cpp b/example/windows/runner/win32_window.cpp deleted file mode 100644 index 60608d0..0000000 --- a/example/windows/runner/win32_window.cpp +++ /dev/null @@ -1,288 +0,0 @@ -#include "win32_window.h" - -#include -#include - -#include "resource.h" - -namespace { - -/// Window attribute that enables dark mode window decorations. -/// -/// Redefined in case the developer's machine has a Windows SDK older than -/// version 10.0.22000.0. -/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute -#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE -#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 -#endif - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -/// Registry key for app theme preference. -/// -/// A value of 0 indicates apps should use dark mode. A non-zero or missing -/// value indicates apps should use light mode. -constexpr const wchar_t kGetPreferredBrightnessRegKey[] = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; -constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - } - FreeLibrary(user32_module); -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registrar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::Create(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - UpdateTheme(window); - - return OnCreate(); -} - -bool Win32Window::Show() { - return ShowWindow(window_handle_, SW_SHOWNORMAL); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - - case WM_DWMCOLORIZATIONCOLORCHANGED: - UpdateTheme(hwnd); - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} - -void Win32Window::UpdateTheme(HWND const window) { - DWORD light_mode; - DWORD light_mode_size = sizeof(light_mode); - LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, - kGetPreferredBrightnessRegValue, - RRF_RT_REG_DWORD, nullptr, &light_mode, - &light_mode_size); - - if (result == ERROR_SUCCESS) { - BOOL enable_dark_mode = light_mode == 0; - DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, - &enable_dark_mode, sizeof(enable_dark_mode)); - } -} diff --git a/example/windows/runner/win32_window.h b/example/windows/runner/win32_window.h deleted file mode 100644 index e901dde..0000000 --- a/example/windows/runner/win32_window.h +++ /dev/null @@ -1,102 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates a win32 window with |title| that is positioned and sized using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size this function will scale the inputted width and height as - // as appropriate for the default monitor. The window is invisible until - // |Show| is called. Returns true if the window was created successfully. - bool Create(const std::wstring& title, const Point& origin, const Size& size); - - // Show the current window. Returns true if the window was successfully shown. - bool Show(); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - // Update the window frame's theme to match the system theme. - static void UpdateTheme(HWND const window); - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/lib/background_timer.dart b/lib/background_timer.dart index 123f4e5..2775ba9 100644 --- a/lib/background_timer.dart +++ b/lib/background_timer.dart @@ -1,341 +1,169 @@ import 'dart:async'; import 'dart:io'; import 'dart:ui'; -import 'package:background_hiit_timer/utils/timer_config.dart'; -import 'package:background_hiit_timer/utils/timer_state.dart'; +import 'package:background_hiit_timer/background_timer_controller.dart'; +import 'package:background_hiit_timer/models/interval_type.dart'; +import 'package:background_hiit_timer/utils/database.dart'; +import 'package:background_hiit_timer/utils/log.dart'; +import 'package:background_hiit_timer/models/timer_state.dart'; +import 'package:background_hiit_timer/utils/utils.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:openhiit_background_service/openhiit_background_service.dart'; -import 'package:openhiit_background_service_android/openhiit_background_service_android.dart'; +import 'package:flutter_background_service/flutter_background_service.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:shared_preferences/shared_preferences.dart'; -import 'package:background_hiit_timer/background_timer_controller.dart'; -import 'package:background_hiit_timer/background_timer_data.dart'; import 'package:soundpool/soundpool.dart'; import 'utils/constants.dart'; -import 'utils/utils.dart'; -/// -/// Background service countdown interval timer. -/// class Countdown extends StatefulWidget { - /// Number of seconds in the work interval. - /// - final int workSeconds; - - /// Number of seconds in the rest interval. - /// - final int restSeconds; - - /// Number of seconds in the break interval. - /// - final int breakSeconds; - - final int getreadySeconds; - - final int warmupSeconds; - - final int cooldownSeconds; - - /// Number of times to repeat the timer. - /// - final int iterations; - - /// Build method for the timer. - /// - final Widget Function(BuildContext, BackgroundTimerData) build; - - /// Called when the timer has finished all intervals. - /// + final List intervals; + final Widget Function(BuildContext, TimerState) build; final Function? onFinished; - - /// Controller for the countdown timer. - /// Allows external control and monitoring of the countdown timer. - /// final CountdownController? controller; - /// End sound - Sound to play at session completion. - /// E.g. all intervals finished and timer complete. - /// - final String completeSound; - - /// Work sound - Sound to play at the start of the - /// work interval. - /// - final String workSound; - - /// Rest sound - Sound to play at the start of the - /// rest interval. - /// - final String restSound; - - /// Halfway sound - Sound to play at the halfway point - /// of the work interval. - /// - final String halfwaySound; - - /// Countdown sound - Sound to play at the 3, 2, and 1 - /// second mark of each interval. Signifies the current - /// interval is nearing the end. - /// - final String countdownSound; - - /// Number of work intervals in the session. - /// - /// The number of rest intervals will be extrapolated from - /// this value. - /// - final int numberOfWorkIntervals; - - /// Current interval status. - /// - /// One of: start, work, rest, end. - /// - final String status; - - /// - /// Constructor - /// const Countdown({ - Key? key, - required this.workSeconds, - required this.restSeconds, - required this.numberOfWorkIntervals, + super.key, + required this.intervals, required this.build, - this.breakSeconds = 0, - this.getreadySeconds = 0, - this.warmupSeconds = 0, - this.cooldownSeconds = 0, - this.iterations = 0, - this.status = 'start', - this.completeSound = 'horn', - this.workSound = 'short-whistle', - this.restSound = 'short-rest-beep', - this.halfwaySound = 'short-halfway-beep', - this.countdownSound = 'countdown-beep', this.onFinished, this.controller, - }) : super(key: key); + }); @override CountdownState createState() => CountdownState(); } -/// -/// State of timer -/// class CountdownState extends State with WidgetsBindingObserver { - /// Whether the timer is currently active. - /// bool isActive = false; + late SharedPreferences _preferences; - /// - /// Initialize the timer. - /// @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + _initializeController(); + _initializePreferences(); + + if (widget.controller?.autoStart ?? true) { + _startTimer(); + } + } + + Future _initializePreferences() async { + _preferences = await SharedPreferences.getInstance(); + } + + void _initializeController() { widget.controller?.setOnStart(_startTimer); + widget.controller?.setOnStop(_stopTimer); widget.controller?.setOnPause(_onTimerPaused); widget.controller?.setOnResume(_onTimerResumed); widget.controller?.setOnRestart(_onTimerRestart); + widget.controller?.setOnSkipNext(_onTimerSkipNext); + widget.controller?.setOnSkipPrevious(_onTimerSkipPrevious); widget.controller?.isCompleted = false; - - // Start the timer if autostart is enabled. - if ((widget.controller == null) || (widget.controller!.autoStart == true)) { - _startTimer(); - } } - /// - /// On dispose, stop the timer if active. - /// @override void dispose() { - /// Stop timer if active. if (isActive) { - final service = OpenhiitBackgroundService(); - service.invoke("stopService"); + FlutterBackgroundService().invoke("stopService"); } - WidgetsBinding.instance.removeObserver(this); - super.dispose(); } - /// - /// On timer paused, updates the paused shared preference to true. - /// void _onTimerPaused() async { - /// Stop timer if currently active. Otherwise, a timer - /// is not currently running so we ignore the timer pause. + logger.d("Pausing timer"); if (isActive) { - SharedPreferences preferences = await SharedPreferences.getInstance(); - await preferences.setBool("pause", true); + await _preferences.setBool("pause", true); } } - /// - /// On timer resumed, updates the paused shared preference to false. - /// void _onTimerResumed() async { - /// Resume timer if currently active. Otherwise, a timer - /// is not currently running so we ignore the timer resume. + logger.d("Resuming timer"); if (isActive) { - SharedPreferences preferences = await SharedPreferences.getInstance(); - await preferences.setBool("pause", false); + await _preferences.setBool("pause", false); } } - /// - /// On timer restarted, stops the service and restarts the timer - /// by running _startTimer. - /// void _onTimerRestart() { - final service = OpenhiitBackgroundService(); - service.invoke("restartService"); + logger.d("Restarting timer"); + FlutterBackgroundService().invoke("restartService"); + } + + void _onTimerSkipNext() { + logger.d("Skipping to next interval"); + FlutterBackgroundService().invoke("skipNext"); + } + + void _onTimerSkipPrevious() { + logger.d("Skipping to previous interval"); + FlutterBackgroundService().invoke("skipPrevious"); } - /// - /// Start the timer. - /// void _startTimer() async { - // Set isActive to true to indicate the timer is active - isActive = true; - - TimerConfig timerConfig = TimerConfig( - widget.workSeconds, - widget.restSeconds, - widget.breakSeconds, - widget.getreadySeconds, - widget.warmupSeconds, - widget.cooldownSeconds, - widget.iterations, - widget.workSound, - widget.restSound, - widget.halfwaySound, - widget.completeSound, - widget.countdownSound); + logger.d("Starting timer"); + setState(() => isActive = true); - TimerState timerState = TimerState( - false, widget.numberOfWorkIntervals, 0, 0, "start", widget.iterations); + // Ensure the 'pause' shared preference is set to false when the timer starts + SharedPreferences preferences = await SharedPreferences.getInstance(); + await preferences.setBool("pause", false); - // Save timer settings to SharedPreferences - saveTimerPreferences(timerConfig, timerState); + await _initializeService(); + widget.controller?.isCompleted = false; + } - // Initialize background service - await initializeService().then((value) { - widget.controller?.isCompleted = false; - }); + void _stopTimer() { + logger.d("Stopping timer"); + setState(() => isActive = false); + FlutterBackgroundService().invoke("stopService"); } @override Widget build(BuildContext context) { return StreamBuilder?>( - stream: OpenhiitBackgroundService().on('update'), + stream: FlutterBackgroundService().on('update'), builder: (context, snapshot) { if (!snapshot.hasData) { - // Show loading indicator while waiting for data - return const Center( - child: CircularProgressIndicator(), - ); + return const Center(child: CircularProgressIndicator()); } - // Grab the data from the snapshot final data = snapshot.data!; + final TimerState timerState = TimerState.fromMap(data); - // Check if the timer has completed - if (data["microSeconds"] == 0 && - widget.controller?.isCompleted == false) { - // Invoke the onFinished callback if provided - if (widget.onFinished != null) { - widget.onFinished!(); - } - widget.controller?.isCompleted = true; - } - // If not completed, ensure the isCompleted bool is set as such - else if (data["microSeconds"] > 0) { - widget.controller?.isCompleted = false; - } - - /// Create object of data sent back from the timer - BackgroundTimerData backgroundTimerData = BackgroundTimerData( - data["microSeconds"], - data["status"], - data["numberOfWorkIntervals"], - data["numberOfIntervals"], - data["paused"], - data["iterations"], - data["changeVolume"], - data["volume"]); - - // Return data and context to the UI - return widget.build(context, backgroundTimerData); + return widget.build(context, timerState); }, ); } - /// - /// Initialize background service - /// - Future initializeService() async { - final service = OpenhiitBackgroundService(); - - /// --- FOR BACKGROUND SERVICE NOTIFICATION CHANNEL --- - - /// OPTIONAL, using custom notification channel id - const AndroidNotificationChannel channel = AndroidNotificationChannel( - 'timer_foreground', // id - 'TIMER', // title - description: - 'This channel is used for important notifications.', // description - importance: Importance.low, // importance must be at low or higher level - ); + Future _initializeService() async { + final service = FlutterBackgroundService(); - final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = - FlutterLocalNotificationsPlugin(); + // Database setup + DatabaseManager dbManager = DatabaseManager(); + await dbManager.clearDatabaseIfNotEmpty(); + await dbManager.openIntervalDatabase(); + await dbManager.insertIntervals(widget.intervals); - if (Platform.isIOS || Platform.isAndroid) { - await flutterLocalNotificationsPlugin.initialize( - const InitializationSettings( - iOS: DarwinInitializationSettings(), - android: AndroidInitializationSettings('ic_bg_service_small'), - ), - ); - } - - await flutterLocalNotificationsPlugin - .resolvePlatformSpecificImplementation< - AndroidFlutterLocalNotificationsPlugin>() - ?.createNotificationChannel(channel); - - /// --- END FOR BACKGROUND SERVICE NOTIFICATION CHANNEL --- + // Initialize notification channels for Android/iOS + await _setupNotifications(); await service.configure( androidConfiguration: AndroidConfiguration( - // This will be executed when app is in foreground - // or background in separated isolate - onStart: onStart, - - // auto start service - autoStart: true, - isForegroundMode: true, - - notificationChannelId: 'timer_foreground', - initialNotificationTitle: 'TIMER', - initialNotificationContent: 'Initializing', - foregroundServiceNotificationId: 888, - ), + onStart: onStart, + autoStart: true, + autoStartOnBoot: false, + isForegroundMode: true, + notificationChannelId: 'timer_foreground', + initialNotificationTitle: 'TIMER', + initialNotificationContent: 'Initializing', + foregroundServiceNotificationId: 888, + foregroundServiceTypes: [AndroidForegroundType.mediaPlayback]), iosConfiguration: IosConfiguration( - // auto start service autoStart: true, - // this will be executed when app is in foreground in separated isolate onForeground: onStart, - // you have to enable background fetch capability on xcode project onBackground: onIosBackground, ), ); @@ -343,45 +171,101 @@ class CountdownState extends State with WidgetsBindingObserver { service.startService(); } - /// - /// Run on iOS background - /// - @pragma('vm:entry-point') - Future onIosBackground(ServiceInstance service) async { - WidgetsFlutterBinding.ensureInitialized(); - DartPluginRegistrant.ensureInitialized(); + Future _setupNotifications() async { + const channel = AndroidNotificationChannel( + 'timer_foreground', + 'TIMER', + description: 'This channel is used for important notifications.', + importance: Importance.low, + ); - return true; + final notificationsPlugin = FlutterLocalNotificationsPlugin(); + + if (Platform.isIOS || Platform.isAndroid) { + await notificationsPlugin.initialize( + const InitializationSettings( + iOS: DarwinInitializationSettings(), + android: AndroidInitializationSettings('ic_bg_service_small'), + ), + ); + } + + await notificationsPlugin + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() + ?.createNotificationChannel(channel); } - /// - /// Run on service start - /// @pragma('vm:entry-point') static void onStart(ServiceInstance service) async { DartPluginRegistrant.ensureInitialized(); - - // get SharedPreferences instance SharedPreferences preferences = await SharedPreferences.getInstance(); + double volume = preferences.getDouble("volume") ?? 80.0; + bool changeVolume = preferences.getBool("changeVolume") ?? false; + + Soundpool pool = Soundpool.fromOptions(); + DatabaseManager dbManager = DatabaseManager(); + List intervals = await dbManager.getIntervals(); - // Define empty timer config - TimerConfig timerConfig = await loadTimerPreferences(preferences); + Map soundMap = await _loadIntervalSounds(intervals, pool); + IntervalType currentInterval = intervals[0]; TimerState timerState = TimerState( false, - preferences.getInt('numberOfWorkIntervals')!, + currentInterval.name, 0, - preferences.getInt('getreadySeconds')! * secondsFactor, - "start", - preferences.getInt('iterations')!); + currentInterval.time * secondsFactor, + currentInterval.time * secondsFactor, + volume, + changeVolume); - // Configure the audio session so that the timer does not - // duck or pause other audio - await configureAudioSession(); + _registerServiceEvents( + service, intervals, preferences, soundMap, timerState, pool); + } + + static Future> _loadIntervalSounds( + List intervals, Soundpool pool) async { + Map soundMap = {}; - // Configure the soundpool for the sound effects. - SoundpoolOptions soundpoolOptions = const SoundpoolOptions(); - Soundpool pool = Soundpool.fromOptions(options: soundpoolOptions); + for (int i = 0; i < intervals.length; i++) { + if (intervals[i].startSound.isNotEmpty) { + int soundID = await loadSound(intervals[i].startSound, pool); + soundMap['${i}_sound'] = soundID; + } else { + soundMap['${i}_sound'] = -1; + } + + if (intervals[i].halfwaySound.isNotEmpty) { + int halfwaySoundID = await loadSound(intervals[i].halfwaySound, pool); + soundMap['${i}_halfwaySound'] = halfwaySoundID; + } else { + soundMap['${i}_halfwaySound'] = -1; + } + + if (intervals[i].countdownSound.isNotEmpty) { + int countdownSoundID = + await loadSound(intervals[i].countdownSound, pool); + soundMap['${i}_countdownSound'] = countdownSoundID; + } else { + soundMap['${i}_countdownSound'] = -1; + } + + if (intervals[i].endSound.isNotEmpty) { + int endSoundID = await loadSound(intervals[i].endSound, pool); + soundMap['${i}_endSound'] = endSoundID; + } else { + soundMap['${i}_endSound'] = -1; + } + } + return soundMap; + } + static Future _registerServiceEvents( + ServiceInstance service, + List intervals, + SharedPreferences preferences, + Map soundMap, + TimerState timerState, + Soundpool pool) async { if (service is AndroidServiceInstance) { service.on('setAsForeground').listen((event) { service.setAsForegroundService(); @@ -392,111 +276,81 @@ class CountdownState extends State with WidgetsBindingObserver { }); } - service.on('restartService').listen((event) { - timerState = TimerState( - false, - preferences.getInt('numberOfWorkIntervals')!, - 0, - preferences.getInt('getreadySeconds')! * secondsFactor, - "start", - preferences.getInt('iterations')!); + service.on('restartService').listen((_) { + timerState.reset(intervals); }); - service.on('stopService').listen((event) { - service.stopSelf(); + service.on('skipNext').listen((_) { + timerState.advanceToNextInterval(intervals); }); - int blankSoundID = await rootBundle - .load("packages/background_hiit_timer/lib/assets/audio/blank.mp3") - .then((ByteData soundData) { - return pool.load(soundData); + service.on('skipPrevious').listen((_) { + if (timerState.currentInterval > 0) { + timerState.currentInterval--; + timerState.currentMicroSeconds = + intervals[timerState.currentInterval].time * secondsFactor; + timerState.intervalMicroSeconds = + intervals[timerState.currentInterval].time * secondsFactor; + timerState.status = intervals[timerState.currentInterval].name; + } + }); + + service.on('stopService').listen((_) { + service.stopSelf(); }); - int countdownSoundID = await loadSound(timerConfig.countdownSound, pool); - int halfwaySoundID = await loadSound(timerConfig.halfwaySound, pool); - int restSoundID = await loadSound(timerConfig.restSound, pool); - int workSoundID = await loadSound(timerConfig.workSound, pool); - int completeSoundID = await loadSound(timerConfig.completeSound, pool); + int blankSoundId = await loadSound('blank', pool); Timer.periodic(interval, (timer) async { - // Refresh shared preferences preferences.reload(); - - // Grab the current pause state of the timer (true or false) - timerState.paused = preferences.getBool('pause')!; - - // If the timer is not paused, keep counting down - if (!timerState.paused) { - /// If the timer has not been completed, then - /// deduct a tenth of a second from the timer - if (timerState.status != completeStatus) { - timerState.currentMicroSeconds = - (timerState.currentMicroSeconds - interval.inMicroseconds); - } - - /// If there is no more time on the timer to deduct, then - /// calculate the next action. - if (timerState.currentMicroSeconds < -500000) { - /// Determine timer status - - /// If the status was start or break - if (timerState.status == startStatus || - timerState.status == breakStatus) { - timerState = startIntervalEnd(timerState, timerConfig); - } - - /// If the status was work - else if (timerState.status == workStatus || - timerState.status == warmupStatus) { - timerState = await workIntervalEnd(timerState, timerConfig); + timerState.paused = preferences.getBool('pause') ?? false; + + if (timerState.currentMicroSeconds <= 0) { + timerState.status = "End"; + } else if (!timerState.paused && timerState.currentMicroSeconds > 0) { + timerState.currentMicroSeconds -= interval.inMicroseconds; + + int intervalIndex = timerState.currentInterval; + int nextIntervalIndex = intervalIndex + 1; + + if ([1500000, 2500000, 3500000] + .contains(timerState.currentMicroSeconds)) { + await playSound( + soundMap["${intervalIndex}_countdownSound"]!, pool, preferences); + } else if (timerState.currentMicroSeconds == + timerState.intervalMicroSeconds ~/ 2) { + await playSound( + soundMap["${intervalIndex}_halfwaySound"]!, pool, preferences); + } else if (timerState.currentMicroSeconds == 700000) { + if (intervalIndex < intervals.length - 1) { + int soundId = soundMap["${nextIntervalIndex}_sound"]!; + if (soundId != 0) { + await playSound(soundId, pool, preferences); + } else if (soundMap["${intervalIndex}_endSound"]! != 0) { + await playSound( + soundMap["${intervalIndex}_endSound"]!, pool, preferences); + } + } else { + await playSound( + soundMap["${intervalIndex}_endSound"]!, pool, preferences); } - - /// If the status was rest - else if (timerState.status == restStatus) { - timerState = restIntervalEnd(timerState, timerConfig); - } - timerState.currentOverallInterval++; - } - - /// There is still more time to deduct from the timer, so - /// calculate if a sound effect should play - else { - timerState = await playSoundEffectAndDetermineStatus( - timerConfig, - timerState, - secondsFactor, - timerState.currentMicroSeconds, - workSoundID, - restSoundID, - halfwaySoundID, - countdownSoundID, - completeSoundID, - blankSoundID, - pool, - preferences, - service); + } else if (timerState.currentMicroSeconds == 0 && + intervalIndex < intervals.length - 1) { + logger.d("Advancing to next interval"); + timerState.advanceToNextInterval(intervals); } + } else if (timerState.currentMicroSeconds % 1000000 == 0) { + await playSound(blankSoundId, pool, preferences); } - int time = 0; - if (timerState.currentMicroSeconds > 0) { - time = (timerState.currentMicroSeconds / secondsFactor).round(); - } - - // Send data back to the UI - service.invoke( - 'update', - { - "microSeconds": time, - "status": timerState.status, - "numberOfWorkIntervals": timerState.numberOfWorkIntervalsRemaining, - "numberOfIntervals": timerState.currentOverallInterval, - "paused": timerState.paused, - "iterations": timerState.iterations, - "changeVolume": preferences.getBool('changeVolume') ?? false, - "volume": preferences.getDouble('volume') ?? 80, - }, - ); + service.invoke('update', timerState.toMap()); }); } + + @pragma('vm:entry-point') + static Future onIosBackground(ServiceInstance service) async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); + return true; + } } diff --git a/lib/background_timer_controller.dart b/lib/background_timer_controller.dart index 79a0956..783fb0f 100644 --- a/lib/background_timer_controller.dart +++ b/lib/background_timer_controller.dart @@ -1,25 +1,30 @@ import 'package:flutter/widgets.dart'; /// -/// Controller for Count down +/// Controller for CountDownTimer /// class CountdownController { /// Called when pausing the timer. - /// VoidCallback? onPause; /// Called when resuming the timer. - /// VoidCallback? onResume; /// Called when restarting the timer. - /// VoidCallback? onRestart; /// Called when the timer starts. - /// VoidCallback? onStart; + /// Called when the timer stops. + VoidCallback? onStop; + + /// Called when skipping to the next interval. + VoidCallback? onSkipNext; + + /// Called when skipping to the previous interval. + VoidCallback? onSkipPrevious; + /// /// Checks if the timer is running and enables you to take actions /// according to that. if the timer is still active, @@ -35,75 +40,92 @@ class CountdownController { bool? isCompleted; /// Whether or not the timer should automatically begin. - /// final bool autoStart; - /// /// Constructor - /// CountdownController({this.autoStart = false}); - /// /// Run timer - /// start() { if (onStart != null) { onStart!(); } } - /// /// Set onStart callback. - /// setOnStart(VoidCallback onStart) { this.onStart = onStart; } - /// + /// Stop timer + stop() { + if (onStop != null) { + onStop!(); + } + } + + /// Set onStop callback. + setOnStop(VoidCallback onStop) { + this.onStop = onStop; + } + /// Set timer pause. - /// pause() async { if (onPause != null) { onPause!(); } } - /// /// Set onPause callback. - /// setOnPause(VoidCallback onPause) { this.onPause = onPause; } - /// /// Resume from pause. - /// resume() { if (onResume != null) { onResume!(); } } - /// /// Set onResume callback. - /// setOnResume(VoidCallback onResume) { this.onResume = onResume; } - /// /// Restart timer, starts things fresh. - /// restart() { if (onRestart != null) { onRestart!(); } } - /// /// set onRestart callback. - /// setOnRestart(VoidCallback onRestart) { this.onRestart = onRestart; } + + /// Skip to the next interval. + skipNext() { + if (onSkipNext != null) { + onSkipNext!(); + } + } + + /// Set onSkipNext callback. + setOnSkipNext(VoidCallback onSkipNext) { + this.onSkipNext = onSkipNext; + } + + /// Skip to the previous interval. + skipPrevious() { + if (onSkipPrevious != null) { + onSkipPrevious!(); + } + } + + /// Set onSkipPrevious callback. + setOnSkipPrevious(VoidCallback onSkipPrevious) { + this.onSkipPrevious = onSkipPrevious; + } } diff --git a/lib/background_timer_data.dart b/lib/background_timer_data.dart deleted file mode 100644 index f93660d..0000000 --- a/lib/background_timer_data.dart +++ /dev/null @@ -1,64 +0,0 @@ -/// Defines the object returned from the background timer process -/// that is sent to the foreground. -/// - -import 'dart:core'; - -class BackgroundTimerData { - /// Current microseconds, used to calculate how many - /// seconds to display on the UI. - /// - int currentMicroSeconds = 0; - - /// Current timer interval status. - /// - /// One of: start, work, rest, end. - /// - String status = ""; - - /// Remaining number of work intervals. - /// - int remainingWorkIntervals = 0; - - /// Current interval number, includes all possible - /// interval status types. E.g. start, work, or rest. - /// - int currentOverallInterval = 0; - - /// Whether the timer is currently paused. - /// - bool paused = false; - - int iterations = 0; - - bool changeVolume = false; - - int volume = 80; - - /// - /// Constructor - /// - BackgroundTimerData( - this.currentMicroSeconds, - this.status, - this.remainingWorkIntervals, - this.currentOverallInterval, - this.paused, - this.iterations, - this.changeVolume, - this.volume); - - /// - /// Empty constructor - /// - BackgroundTimerData.empty() { - currentMicroSeconds = 0; - status = ""; - remainingWorkIntervals = 0; - currentOverallInterval = 0; - paused = false; - iterations = 0; - changeVolume = false; - volume = 80; - } -} diff --git a/lib/models/interval_type.dart b/lib/models/interval_type.dart new file mode 100644 index 0000000..2664851 --- /dev/null +++ b/lib/models/interval_type.dart @@ -0,0 +1,87 @@ +class IntervalType { + String id; + String workoutId; + int time; + String name; + int color; + int intervalIndex; + String startSound; + String halfwaySound; + String countdownSound; + String endSound; + + IntervalType( + {required this.id, + required this.workoutId, + required this.time, + required this.name, + required this.color, + required this.intervalIndex, + required this.startSound, + required this.halfwaySound, + required this.countdownSound, + required this.endSound}); + + // Convert an Interval object to a Map + Map toMap() { + return { + 'id': id, + 'workoutId': workoutId, + 'time': time, + 'name': name, + 'color': color, + 'intervalIndex': intervalIndex, + 'startSound': startSound, + 'halfwaySound': halfwaySound, + 'countdownSound': countdownSound, + 'endSound': endSound, + }; + } + + // Copy an Interval object with optional new values + IntervalType copy( + {String? id, + String? workoutId, + int? time, + String? name, + int? color, + int? intervalIndex, + String? startSound, + String? halfwaySound, + String? countdownSound, + String? endSound}) { + return IntervalType( + id: id ?? this.id, + workoutId: workoutId ?? this.workoutId, + time: time ?? this.time, + name: name ?? this.name, + color: color ?? this.color, + intervalIndex: intervalIndex ?? this.intervalIndex, + startSound: startSound ?? this.startSound, + halfwaySound: halfwaySound ?? this.halfwaySound, + countdownSound: countdownSound ?? this.countdownSound, + endSound: endSound ?? this.endSound, + ); + } + + // Create an Interval object from a Map + factory IntervalType.fromMap(Map map) { + return IntervalType( + id: map['id'], + workoutId: map['workoutId'], + time: map['time'], + name: map['name'], + color: map['color'], + intervalIndex: map['intervalIndex'], + startSound: map['startSound'], + halfwaySound: map['halfwaySound'], + countdownSound: map['countdownSound'], + endSound: map['endSound'], + ); + } + + @override + String toString() { + return 'IntervalType{id: $id, workoutId: $workoutId, time: $time, name: $name, color: $color, intervalIndex: $intervalIndex, startSound: $startSound, halfwaySound: $halfwaySound, countdownSound: $countdownSound, endSound: $endSound}'; + } +} diff --git a/lib/models/timer_state.dart b/lib/models/timer_state.dart new file mode 100644 index 0000000..c8bffdb --- /dev/null +++ b/lib/models/timer_state.dart @@ -0,0 +1,79 @@ +import 'dart:core'; + +import 'package:background_hiit_timer/models/interval_type.dart'; +import 'package:background_hiit_timer/utils/constants.dart'; + +class TimerState { + bool paused = false; + String status = ""; + int currentInterval = 0; + int currentMicroSeconds = 0; + int intervalMicroSeconds = 0; + double volume = 80; + bool changeVolume = false; + + TimerState( + this.paused, + this.status, + this.currentInterval, + this.currentMicroSeconds, + this.intervalMicroSeconds, + this.volume, + this.changeVolume); + + TimerState.empty() { + paused = false; + status = "Get ready"; + currentInterval = 0; + currentMicroSeconds = 0; + intervalMicroSeconds = 0; + volume = 80; + changeVolume = false; + } + + Map toMap() { + return { + 'paused': paused, + 'status': status, + 'currentInterval': currentInterval, + 'currentMicroSeconds': currentMicroSeconds, + 'intervalMicroSeconds': intervalMicroSeconds, + 'volume': volume, + 'changeVolume': changeVolume, + }; + } + + factory TimerState.fromMap(Map map) { + double volume = map['volume'].toDouble() ?? 80.0; + + return TimerState( + map['paused'] ?? false, + map['status'] ?? "", + map['currentInterval'] ?? 0, + map['currentMicroSeconds'] ?? 0, + map['intervalMicroSeconds'] ?? 0, + volume, + map['changeVolume'] ?? false, + ); + } + + void reset(List intervals) { + paused = false; + status = intervals[0].name; + currentInterval = 0; + currentMicroSeconds = intervals[0].time * secondsFactor; + intervalMicroSeconds = intervals[0].time * secondsFactor; + changeVolume = false; + } + + void advanceToNextInterval(List intervals) { + if (currentInterval < intervals.length - 1) { + currentInterval++; + currentMicroSeconds = intervals[currentInterval].time * secondsFactor; + intervalMicroSeconds = intervals[currentInterval].time * secondsFactor; + status = intervals[currentInterval].name; + } else { + currentMicroSeconds = 0; + } + } +} diff --git a/lib/sounds.dart b/lib/sounds.dart deleted file mode 100644 index e69de29..0000000 diff --git a/lib/utils/constants.dart b/lib/utils/constants.dart index 61ccf91..c0c4518 100644 --- a/lib/utils/constants.dart +++ b/lib/utils/constants.dart @@ -1,10 +1,2 @@ -/// Possible interval states -const String startStatus = "start"; -const String workStatus = "work"; -const String restStatus = "rest"; -const String completeStatus = "complete"; -const String breakStatus = "break"; -const String warmupStatus = "warmup"; -const String cooldownStatus = "cooldown"; const int secondsFactor = 1000000; const Duration interval = Duration(microseconds: 100000); diff --git a/lib/utils/database.dart b/lib/utils/database.dart new file mode 100644 index 0000000..5447360 --- /dev/null +++ b/lib/utils/database.dart @@ -0,0 +1,175 @@ +import 'dart:async'; +import 'package:background_hiit_timer/models/interval_type.dart'; +import 'package:background_hiit_timer/utils/log.dart'; +import 'package:sqflite/sqflite.dart'; +import 'package:path/path.dart'; + +class DatabaseManager { + static const String _databaseName = "background_service_temp_timer.db"; + static const String _intervalTableName = "CurrentTimerIntervals"; + + // Singleton instance + static final DatabaseManager _instance = DatabaseManager._internal(); + + // Private constructor + DatabaseManager._internal(); + + // Factory constructor to return the singleton instance + factory DatabaseManager() { + return _instance; + } + + Database? _database; + + // Lazy initialization of the database, open it only once + Future _getDatabase() async { + if (_database != null) { + return _database!; + } + _database = await openIntervalDatabase(); + return _database!; + } + + // Open the workout database + Future openIntervalDatabase() async { + logger.d("Opening database"); + + const createIntervalTableQuery = ''' + CREATE TABLE IF NOT EXISTS $_intervalTableName( + id TEXT PRIMARY KEY, + workoutId TEXT, + time INTEGER, + name TEXT, + color INTEGER, + intervalIndex INTEGER, + startSound TEXT, + halfwaySound TEXT, + countdownSound TEXT, + endSound TEXT + ) + '''; + + String dbPath = join(await getDatabasesPath(), _databaseName); + int dbVersion = 1; + + return openDatabase( + dbPath, + version: dbVersion, + onCreate: (db, version) async { + logger.d("Creating interval table"); + await db.execute(createIntervalTableQuery); + }, + ); + } + + // Check if the database already has data and delete it if so + Future clearDatabaseIfNotEmpty() async { + logger.d("Checking if database has data"); + + final db = await _getDatabase(); + final count = Sqflite.firstIntValue( + await db.rawQuery('SELECT COUNT(*) FROM $_intervalTableName')); + + if (count != null && count > 0) { + logger.d("Database has data, clearing it"); + await db.delete(_intervalTableName); + } else { + logger.d("Database is already empty"); + } + } + + // Insert interval + Future insertInterval(IntervalType interval) async { + logger.d("Inserting interval: ${interval.name}"); + + final db = await _getDatabase(); + await db.insert( + _intervalTableName, + interval.toMap(), + conflictAlgorithm: ConflictAlgorithm.fail, + ); + } + + // Insert intervals + Future insertIntervals(List intervals) async { + logger.d("Inserting ${intervals.length} intervals"); + + final db = await _getDatabase(); + Batch batch = db.batch(); + + for (var interval in intervals) { + batch.insert( + _intervalTableName, + interval.toMap(), + conflictAlgorithm: ConflictAlgorithm.fail, + ); + } + + await batch.commit(noResult: true); + } + + // Update interval + Future updateInterval(IntervalType interval) async { + logger.d("Updating interval: ${interval.name}"); + + final db = await _getDatabase(); + await db.update( + _intervalTableName, + interval.toMap(), + where: 'id = ?', + whereArgs: [interval.id], + ); + } + + // Batch update intervals + Future updateIntervals(List intervals) async { + logger.d("Updating ${intervals.length} intervals"); + + final db = await _getDatabase(); + Batch batch = db.batch(); + + for (var interval in intervals) { + batch.update( + _intervalTableName, + interval.toMap(), + where: 'id = ?', + whereArgs: [interval.id], + ); + } + + await batch.commit(noResult: true); + } + + // Delete interval + Future deleteInterval(String id) async { + logger.d("Deleting interval with ID: $id"); + + final db = await _getDatabase(); + await db.delete( + _intervalTableName, + where: 'id = ?', + whereArgs: [id], + ); + } + + // Delete intervals + Future deleteIntervalsByWorkoutId(String workoutId) async { + logger.d("Deleting intervals for workout ID: $workoutId"); + + final db = await _getDatabase(); + await db.delete( + _intervalTableName, + where: 'workoutId = ?', + whereArgs: [workoutId], + ); + } + + // Get all intervals + Future> getIntervals() async { + logger.d("Getting all intervals"); + + final db = await _getDatabase(); + final List> maps = await db.query(_intervalTableName); + return maps.map((map) => IntervalType.fromMap(map)).toList(); + } +} diff --git a/lib/lifecycle_event_handler.dart b/lib/utils/lifecycle_event_handler.dart similarity index 86% rename from lib/lifecycle_event_handler.dart rename to lib/utils/lifecycle_event_handler.dart index a2dccff..42a3a61 100644 --- a/lib/lifecycle_event_handler.dart +++ b/lib/utils/lifecycle_event_handler.dart @@ -27,11 +27,6 @@ class LifecycleEventHandler extends WidgetsBindingObserver { case AppLifecycleState.hidden: // TODO: Handle this case. } - print(''' -============================================================= - $state -============================================================= -'''); } // @override diff --git a/lib/utils/log.dart b/lib/utils/log.dart new file mode 100644 index 0000000..0a4f01f --- /dev/null +++ b/lib/utils/log.dart @@ -0,0 +1,6 @@ +// Global logger instance for logging messages +import 'package:logger/logger.dart'; + +var logger = Logger( + printer: PrettyPrinter(methodCount: 0), +); diff --git a/lib/utils/timer_config.dart b/lib/utils/timer_config.dart deleted file mode 100644 index b3eb5a2..0000000 --- a/lib/utils/timer_config.dart +++ /dev/null @@ -1,89 +0,0 @@ -import 'dart:core'; - -class TimerConfig { - /// Amount of time for an exercise, in seconds. - /// - /// e.g., 30 - /// - late int exerciseTime; - - /// Amount of time between exercises, in seconds. - /// - /// e.g., 30 - /// - late int restTime; - - /// Amount of time between iterations, in seconds. - /// - /// e.g., 30 - /// - late int breakTime; - - late int getreadyTime; - - late int warmupTime; - - late int cooldownTime; - - late int firstIteration; - - /// Sound for the work/exercise period. - /// - /// e.g., "beep" - /// - late String workSound; - - /// Sound for the rest period. - /// - /// e.g., "ding" - /// - late String restSound; - - /// Sound for the halfway mark. - /// - /// e.g., "ding" - /// - late String halfwaySound; - - /// Sound for the end of the timer. - /// - /// e.g., "ding" - /// - late String completeSound; - - /// Sound to play at the 3, 2, and 1 - /// second mark of each interval. - /// - /// e.g., "beep" - /// - late String countdownSound; - - TimerConfig( - this.exerciseTime, - this.restTime, - this.breakTime, - this.getreadyTime, - this.warmupTime, - this.cooldownTime, - this.firstIteration, - this.workSound, - this.restSound, - this.halfwaySound, - this.completeSound, - this.countdownSound); - - TimerConfig.empty() { - exerciseTime = 0; - restTime = 0; - breakTime = 0; - getreadyTime = 0; - warmupTime = 0; - cooldownTime = 0; - firstIteration = 0; - workSound = "short-whistle"; - restSound = "short-rest-beep"; - halfwaySound = "short-halfway-beep"; - countdownSound = "countdown-beep"; - completeSound = "long-bell"; - } -} diff --git a/lib/utils/timer_state.dart b/lib/utils/timer_state.dart deleted file mode 100644 index 03c51fb..0000000 --- a/lib/utils/timer_state.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:core'; - -class TimerState { - /// Whether or not the timer is currently paused. - /// - /// e.g., false - /// - late bool paused; - - late String status; - - /// The number of exercises. - /// - /// e.g., "I need to do X but do it in Y way" - /// - late int numberOfWorkIntervalsRemaining; - - /// Current interval number, includes all possible - /// interval status types. E.g. start, work, or rest. - /// - late int currentOverallInterval; - - late int currentMicroSeconds; - - late int iterations; - - TimerState( - this.paused, - this.numberOfWorkIntervalsRemaining, - this.currentOverallInterval, - this.currentMicroSeconds, - this.status, - this.iterations); - - TimerState.empty() { - paused = false; - numberOfWorkIntervalsRemaining = 0; - currentOverallInterval = 0; - currentMicroSeconds = 0; - status = "start"; - iterations = 1; - } -} diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 82905e3..988f30c 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -1,39 +1,11 @@ -import 'dart:io'; - -import 'package:audio_session/audio_session.dart'; -import 'package:background_hiit_timer/utils/timer_config.dart'; -import 'package:background_hiit_timer/utils/timer_state.dart'; +import 'package:background_hiit_timer/utils/log.dart'; import 'package:flutter/services.dart'; -import 'package:openhiit_background_service/openhiit_background_service.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:soundpool/soundpool.dart'; -import 'constants.dart'; - -Future configureAudioSession() async { - final session = await AudioSession.instance; - - await session.configure(const AudioSessionConfiguration( - avAudioSessionCategory: AVAudioSessionCategory.playback, - avAudioSessionCategoryOptions: AVAudioSessionCategoryOptions.mixWithOthers, - avAudioSessionMode: AVAudioSessionMode.defaultMode, - avAudioSessionRouteSharingPolicy: - AVAudioSessionRouteSharingPolicy.defaultPolicy, - avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none, - androidAudioAttributes: AndroidAudioAttributes( - contentType: AndroidAudioContentType.sonification, - flags: AndroidAudioFlags.audibilityEnforced, - usage: AndroidAudioUsage.notification, - ), - androidAudioFocusGainType: AndroidAudioFocusGainType.gain, - androidWillPauseWhenDucked: true, - )); - - return session; -} - Future loadSound(String sound, Soundpool pool) async { - if (!sound.contains("none")) { + if (sound.isNotEmpty) { + logger.d('Loading sound $sound'); return await rootBundle .load("packages/background_hiit_timer/lib/assets/audio/$sound.mp3") .then((ByteData soundData) { @@ -43,220 +15,13 @@ Future loadSound(String sound, Soundpool pool) async { return -1; } -void saveTimerPreferences( - TimerConfig timerConfig, TimerState timerState) async { - SharedPreferences preferences = await SharedPreferences.getInstance(); - await preferences.setBool("pause", false); - await preferences.setInt("workSeconds", timerConfig.exerciseTime); - await preferences.setInt("restSeconds", timerConfig.restTime); - await preferences.setInt("breakSeconds", timerConfig.breakTime); - await preferences.setInt("getreadySeconds", timerConfig.getreadyTime); - await preferences.setInt("warmupSeconds", timerConfig.warmupTime); - await preferences.setInt("cooldownSeconds", timerConfig.cooldownTime); - await preferences.setString("halfwaySound", timerConfig.halfwaySound); - await preferences.setString("completeSound", timerConfig.completeSound); - await preferences.setString("countdownSound", timerConfig.countdownSound); - await preferences.setString("workSound", timerConfig.workSound); - await preferences.setString("restSound", timerConfig.restSound); - await preferences.setInt( - "numberOfWorkIntervals", timerState.numberOfWorkIntervalsRemaining); - await preferences.setInt("iterations", timerState.iterations); -} - -Future loadTimerPreferences(SharedPreferences preferences) async { - preferences.reload(); - - TimerConfig timerConfig = TimerConfig( - preferences.getInt("workSeconds")!, - preferences.getInt("restSeconds")!, - preferences.getInt("breakSeconds")!, - preferences.getInt("getreadySeconds")!, - preferences.getInt("warmupSeconds")!, - preferences.getInt("cooldownSeconds")!, - preferences.getInt("iterations")!, - preferences.getString('workSound')!, - preferences.getString('restSound')!, - preferences.getString('halfwaySound')!, - preferences.getString('completeSound')!, - preferences.getString('countdownSound')!); - - return timerConfig; -} - Future playSound( int soundID, Soundpool pool, SharedPreferences preferences) async { if (soundID != -1) { + logger.d('Playing sound $soundID'); await pool.setVolume( soundId: soundID, volume: ((preferences.getDouble('volume') ?? 80) / 100)); await pool.play(soundID); } } - -Future playSoundEffectAndDetermineStatus( - TimerConfig timerConfig, - TimerState timerState, - int secondsFactor, - int currentMicroSeconds, - int workSoundID, - int restSoundID, - int halfwaySoundID, - int countdownSoundID, - int completeSoundID, - int blankSoundID, - Soundpool pool, - SharedPreferences preferences, - ServiceInstance service) async { - /// Calculate half of the work time - int halfWorkSeconds = - ((timerConfig.exerciseTime * secondsFactor) / 2).round(); - - /// Check if the halfway sound should play - if (currentMicroSeconds == halfWorkSeconds && - halfwaySoundID != -1 && - timerState.status == workStatus) { - await playSound(halfwaySoundID, pool, preferences); - } - // Check if the 3, 2, 1 sound should play - else if ((currentMicroSeconds - 500000) == 3500000) { - await playSound(blankSoundID, pool, preferences); - } else if ((currentMicroSeconds - 500000) == 2500000 || - (currentMicroSeconds - 500000) == 1500000 || - (currentMicroSeconds - 500000) == 500000) { - await playSound(countdownSoundID, pool, preferences); - } - - /// Check which end sound should play - else if (currentMicroSeconds == 0) { - if (timerState.status == cooldownStatus) { - /// Play complete sound - await playSound(completeSoundID, pool, preferences); - - /// Switch to the complete state - timerState.status = completeStatus; - } - - /// The whole iteration is done, play the final sound - else if (timerState.numberOfWorkIntervalsRemaining == 0 && - timerState.status != completeStatus) { - SharedPreferences preferences = await SharedPreferences.getInstance(); - - if (timerState.status != cooldownStatus && - preferences.getInt('cooldownSeconds')! > 0 && - timerState.iterations == 0) { - // timerState.iterations = timerState.iterations - 1; - // Play the rest sound - await playSound(restSoundID, pool, preferences); - timerState = TimerState( - false, - preferences.getInt('numberOfWorkIntervals')!, - timerState.currentOverallInterval + 1, - preferences.getInt('cooldownSeconds')! * secondsFactor, - "cooldown", - timerState.iterations); - } else { - /// Play complete sound - await playSound(completeSoundID, pool, preferences); - - /// Switch to the complete state - timerState.status = completeStatus; - - /// Decrement the iterations if this was the last work interval - if (timerState.numberOfWorkIntervalsRemaining == 0 && - timerState.iterations > 0) { - timerState.iterations = timerState.iterations - 1; - - int breakSeconds = timerConfig.breakTime; - if (breakSeconds > 0) { - timerState = TimerState( - false, - preferences.getInt('numberOfWorkIntervals')!, - timerState.currentOverallInterval + 1, - breakSeconds * secondsFactor, - "break", - timerState.iterations); - } else { - timerState = TimerState( - false, - preferences.getInt('numberOfWorkIntervals')!, - timerState.currentOverallInterval + 1, - preferences.getInt('getreadySeconds')! * secondsFactor, - "start", - timerState.iterations); - } - } - } - } else if (timerState.status == workStatus || - timerState.status == warmupStatus) { - // Play the rest sound - await playSound(restSoundID, pool, preferences); - } else if (timerState.status == restStatus || - timerState.status == startStatus || - timerState.status == breakStatus) { - // Play the work sound - await playSound(workSoundID, pool, preferences); - } - } else if (currentMicroSeconds < -500000) { - await pool.release(); - service.stopSelf(); - } else { - if (Platform.isIOS && currentMicroSeconds % 1000000 == 0) { - await playSound(blankSoundID, pool, preferences); - } - } - - return timerState; -} - -Future workIntervalEnd( - TimerState timerState, TimerConfig timerConfig) async { - /// Switch to the rest state - timerState.status = restStatus; - - /// Update the current time to the rest time - timerState.currentMicroSeconds = timerConfig.restTime * secondsFactor; - - return timerState; -} - -TimerState restIntervalEnd(TimerState timerState, TimerConfig timerConfig) { - /// Switch to the work state - timerState.status = workStatus; - - /// Update the current time to the work time - timerState.currentMicroSeconds = timerConfig.exerciseTime * secondsFactor; - - /// Since we have changed intervals, decrement the - /// number of intervals at each work session - timerState.numberOfWorkIntervalsRemaining = - timerState.numberOfWorkIntervalsRemaining - 1; - - return timerState; -} - -TimerState startIntervalEnd(TimerState timerState, TimerConfig timerConfig) { - if (timerConfig.warmupTime > 0 && - timerState.status != warmupStatus && - timerState.iterations == timerConfig.firstIteration) { - timerState = TimerState( - false, - timerState.numberOfWorkIntervalsRemaining, - timerState.currentOverallInterval, - timerConfig.warmupTime * secondsFactor, - "warmup", - timerState.iterations); - } else { - /// Switch to the work state - timerState.status = workStatus; - - /// Update the current time to the work time - timerState.currentMicroSeconds = timerConfig.exerciseTime * secondsFactor; - - /// Since we have changed intervals, decrement the - /// number of intervals at each work session - timerState.numberOfWorkIntervalsRemaining = - timerState.numberOfWorkIntervalsRemaining - 1; - } - - return timerState; -} diff --git a/pubspec.yaml b/pubspec.yaml index e27ddc7..32a9efa 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: background_hiit_timer description: Flutter background timer package developed for HIIT timers. -version: 1.0.0-dev.6 +version: 1.0.0 repository: https://github.com/a-mabe/background_hiit_timer funding: @@ -11,25 +11,24 @@ environment: flutter: ">=1.17.0" dependencies: - audioplayers: ^6.0.0 - # device_info_plus: ^9.0.3 - device_info_plus: ^10.1.0 + device_info_plus: ^11.1.1 flutter: sdk: flutter - # flutter_background_service: ^5.0.1 - flutter_local_notifications: ^17.1.2 + flutter_background_service: ^5.0.10 + flutter_background_service_android: ^6.2.7 shared_preferences: ^2.2.0 - audio_session: ^0.1.16 - just_audio: ^0.9.35 soundpool: ^2.4.1 - openhiit_background_service: ^1.0.0-dev.3 - openhiit_background_service_android: ^1.0.0-dev.3 + logger: ^2.4.0 + sqflite_common_ffi: ^2.3.4 + sqflite: ^2.4.0 + path: ^1.9.0 + flutter_local_notifications: ^18.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec dev_dependencies: - flutter_lints: ^4.0.0 + flutter_lints: ^5.0.0 # The following section is specific to Flutter packages. flutter: